Showing preview only (3,558K chars total). Download the full file or copy to clipboard to get everything.
Repository: lukexor/tetanes
Branch: main
Commit: 900e840134f4
Files: 534
Total size: 16.1 MB
Directory structure:
gitextract_bip8yc8i/
├── .cargo/
│ └── config.toml
├── .config/
│ └── nextest.toml
├── .git-blame-ignore-revs
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── defect-report.md
│ │ └── feature_request.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── cd.yml
│ ├── ci.yml
│ ├── outdated.yml
│ ├── release-pr.yml
│ ├── security.yml
│ └── triage.yml
├── .gitignore
├── .gitmodules
├── .prettierignore
├── .rgignore
├── Cargo.toml
├── Cross.toml
├── LICENSE-APACHE
├── LICENSE-MIT
├── Makefile.toml
├── README.md
├── ROADMAP.md
├── assets/
│ ├── linux/
│ │ └── tetanes.desktop
│ └── macos/
│ ├── .DS_Store
│ ├── Icon.icns
│ └── Info.plist
├── cliff.toml
├── deny.toml
├── docs/
│ ├── apu/
│ │ ├── apu_ref.txt
│ │ ├── audio_psuedo_code.txt
│ │ ├── blargg_tests_readme.txt
│ │ ├── mixer_readme.txt
│ │ ├── test_readme.txt
│ │ └── volume_readme.txt
│ ├── cartridge_board_list.txt
│ ├── cpu/
│ │ ├── branch_timing_readme.txt
│ │ ├── dummy_writes_readme.txt
│ │ ├── exec_space_readme.txt
│ │ ├── instr_misc_readme.txt
│ │ ├── instr_test_readme.txt
│ │ ├── instr_timing_readme.txt
│ │ ├── interrupts_readme.txt
│ │ ├── opcode_list.txt
│ │ └── reset_readme.txt
│ ├── genie_codes
│ ├── mapper/
│ │ ├── 000.txt
│ │ ├── 001.txt
│ │ ├── 002.txt
│ │ ├── 003.txt
│ │ ├── 004.txt
│ │ ├── 005.txt
│ │ ├── 007.txt
│ │ ├── 009.txt
│ │ ├── 010.txt
│ │ ├── 011.txt
│ │ ├── 013.txt
│ │ ├── 015.txt
│ │ ├── 016.txt
│ │ ├── 018.txt
│ │ ├── 019.txt
│ │ ├── 021.txt
│ │ ├── 022.txt
│ │ ├── 023.txt
│ │ ├── 024.txt
│ │ ├── 025.txt
│ │ ├── 026.txt
│ │ ├── 032.txt
│ │ ├── 033.txt
│ │ ├── 034.txt
│ │ ├── 044.txt
│ │ ├── 045.txt
│ │ ├── 046.txt
│ │ ├── 047.txt
│ │ ├── 048.txt
│ │ ├── 049.txt
│ │ ├── 050.txt
│ │ ├── 052.txt
│ │ ├── 057.txt
│ │ ├── 058.txt
│ │ ├── 060.txt
│ │ ├── 061.txt
│ │ ├── 062.txt
│ │ ├── 064.txt
│ │ ├── 065.txt
│ │ ├── 066.txt
│ │ ├── 067.txt
│ │ ├── 068.txt
│ │ ├── 069.txt
│ │ ├── 070.txt
│ │ ├── 071.txt
│ │ ├── 072.txt
│ │ ├── 073.txt
│ │ ├── 074.txt
│ │ ├── 075.txt
│ │ ├── 076.txt
│ │ ├── 077.txt
│ │ ├── 078.txt
│ │ ├── 079.txt
│ │ ├── 080.txt
│ │ ├── 082.txt
│ │ ├── 085.txt
│ │ ├── 086.txt
│ │ ├── 087.txt
│ │ ├── 088.txt
│ │ ├── 089.txt
│ │ ├── 090.txt
│ │ ├── 091.txt
│ │ ├── 092.txt
│ │ ├── 093.txt
│ │ ├── 094.txt
│ │ ├── 095.txt
│ │ ├── 096.txt
│ │ ├── 097.txt
│ │ ├── 105.txt
│ │ ├── 107.txt
│ │ ├── 112.txt
│ │ ├── 113.txt
│ │ ├── 115.txt
│ │ ├── 118.txt
│ │ ├── 119.txt
│ │ ├── 140.txt
│ │ ├── 152.txt
│ │ ├── 154.txt
│ │ ├── 159.txt
│ │ ├── 164.txt
│ │ ├── 165.txt
│ │ ├── 180.txt
│ │ ├── 182.txt
│ │ ├── 184.txt
│ │ ├── 185.txt
│ │ ├── 189.txt
│ │ ├── 191.txt
│ │ ├── 192.txt
│ │ ├── 193.txt
│ │ ├── 194.txt
│ │ ├── 200.txt
│ │ ├── 201.txt
│ │ ├── 203.txt
│ │ ├── 205.txt
│ │ ├── 207.txt
│ │ ├── 209.txt
│ │ ├── 210.txt
│ │ ├── 225.txt
│ │ ├── 226.txt
│ │ ├── 227.txt
│ │ ├── 228.txt
│ │ ├── 230.txt
│ │ ├── 231.txt
│ │ ├── 232.txt
│ │ ├── 233.txt
│ │ ├── 234.txt
│ │ ├── 240.txt
│ │ ├── 242.txt
│ │ ├── 243.txt
│ │ ├── 245.txt
│ │ ├── 246.txt
│ │ ├── __ READ THIS FIRST __.txt
│ │ ├── changes.txt
│ │ ├── mmc3_irq_tests_readme.txt
│ │ └── mmc3_test_readme.txt
│ ├── memory_mapping.txt
│ ├── nes_arch.txt
│ ├── nes_graphics.txt
│ ├── nes_tech.txt
│ └── ppu/
│ ├── blargg_tests_readme.txt
│ ├── nmi_sync_ntsc_readme.txt
│ ├── oam_read_readme.txt
│ ├── oam_stress_readme.txt
│ ├── open_bus_readme.txt
│ ├── ppu_2c02_ref.txt
│ ├── ppu_scrolling.txt
│ ├── read_buffer_test_readme.txt
│ ├── sprite_hit_readme.txt
│ ├── sprite_overflow_readme.txt
│ ├── tv_readme.txt
│ ├── vbl_nmi_readme.txt
│ └── vbl_nmi_timing_readme.txt
├── release-plz.toml
├── rust-toolchain.toml
├── static/
│ └── tetanes.xcf
├── tetanes/
│ ├── CHANGELOG.md
│ ├── Cargo.toml
│ ├── assets/
│ │ ├── main.css
│ │ ├── pixeloid-license.txt
│ │ └── roms/
│ │ ├── alter_ego.nes
│ │ ├── alter_ego.txt
│ │ ├── ao_demo.nes
│ │ ├── ao_demo.txt
│ │ ├── assimilate.nes
│ │ ├── assimilate.txt
│ │ ├── blade_buster.nes
│ │ ├── blade_buster.txt
│ │ ├── cheril_the_goddess.nes
│ │ ├── cheril_the_goddess.txt
│ │ ├── data_man_demo.nes
│ │ ├── dushlan.nes
│ │ ├── dushlan.txt
│ │ ├── from_below.nes
│ │ ├── from_below.txt
│ │ ├── lan_master.nes
│ │ ├── lan_master.txt
│ │ ├── lawn_mower.nes
│ │ ├── lawn_mower.txt
│ │ ├── mad_wizard.nes
│ │ ├── mad_wizard.txt
│ │ ├── micro_knight.nes
│ │ ├── micro_knight.txt
│ │ ├── nebs_n_debs.txt
│ │ ├── nebs_n_debs_demo.nes
│ │ ├── owlia.nes
│ │ ├── owlia.txt
│ │ ├── streemerz.nes
│ │ ├── streemerz.txt
│ │ ├── super_painter.nes
│ │ ├── super_painter.txt
│ │ ├── tiger_jenny.nes
│ │ ├── tiger_jenny.txt
│ │ ├── yun.nes
│ │ └── yun.txt
│ ├── build.rs
│ ├── index.html
│ ├── initializer.js
│ ├── shaders/
│ │ ├── crt-easymode.wgsl
│ │ └── gui.wgsl
│ ├── src/
│ │ ├── bin/
│ │ │ └── build_artifacts.rs
│ │ ├── error.rs
│ │ ├── lib.rs
│ │ ├── logging.rs
│ │ ├── main.rs
│ │ ├── nes/
│ │ │ ├── action.rs
│ │ │ ├── audio.rs
│ │ │ ├── config.rs
│ │ │ ├── emulation/
│ │ │ │ ├── replay.rs
│ │ │ │ └── rewind.rs
│ │ │ ├── emulation.rs
│ │ │ ├── event.rs
│ │ │ ├── input.rs
│ │ │ ├── renderer/
│ │ │ │ ├── clipboard.rs
│ │ │ │ ├── event.rs
│ │ │ │ ├── gui/
│ │ │ │ │ ├── keybinds.rs
│ │ │ │ │ ├── lib.rs
│ │ │ │ │ ├── ppu_viewer.rs
│ │ │ │ │ └── preferences.rs
│ │ │ │ ├── gui.rs
│ │ │ │ ├── painter.rs
│ │ │ │ ├── shader.rs
│ │ │ │ └── texture.rs
│ │ │ ├── renderer.rs
│ │ │ ├── rom.rs
│ │ │ └── version.rs
│ │ ├── nes.rs
│ │ ├── opts.rs
│ │ ├── platform.rs
│ │ ├── sys/
│ │ │ ├── info/
│ │ │ │ ├── os.rs
│ │ │ │ └── wasm.rs
│ │ │ ├── info.rs
│ │ │ ├── logging/
│ │ │ │ ├── os.rs
│ │ │ │ └── wasm.rs
│ │ │ ├── logging.rs
│ │ │ ├── platform/
│ │ │ │ ├── os.rs
│ │ │ │ └── wasm.rs
│ │ │ ├── platform.rs
│ │ │ ├── thread/
│ │ │ │ ├── os.rs
│ │ │ │ └── wasm.rs
│ │ │ └── thread.rs
│ │ ├── sys.rs
│ │ └── thread.rs
│ └── wix/
│ └── main.wxs
├── tetanes-core/
│ ├── CHANGELOG.md
│ ├── Cargo.toml
│ ├── README.md
│ ├── benches/
│ │ └── clock_frame.rs
│ ├── game_database.txt
│ ├── ntscpalette.pal
│ ├── src/
│ │ ├── action.rs
│ │ ├── apu/
│ │ │ ├── dmc.rs
│ │ │ ├── envelope.rs
│ │ │ ├── filter.rs
│ │ │ ├── frame_counter.rs
│ │ │ ├── length_counter.rs
│ │ │ ├── noise.rs
│ │ │ ├── pulse.rs
│ │ │ ├── timer.rs
│ │ │ └── triangle.rs
│ │ ├── apu.rs
│ │ ├── bus.rs
│ │ ├── cart.rs
│ │ ├── common.rs
│ │ ├── control_deck.rs
│ │ ├── cpu/
│ │ │ └── instr.rs
│ │ ├── cpu.rs
│ │ ├── debug.rs
│ │ ├── error.rs
│ │ ├── fs.rs
│ │ ├── genie.rs
│ │ ├── input.rs
│ │ ├── lib.rs
│ │ ├── mapper/
│ │ │ ├── bandai_fcg.rs
│ │ │ ├── m000_nrom.rs
│ │ │ ├── m001_sxrom.rs
│ │ │ ├── m002_uxrom.rs
│ │ │ ├── m003_cnrom.rs
│ │ │ ├── m004_txrom.rs
│ │ │ ├── m005_exrom.rs
│ │ │ ├── m007_axrom.rs
│ │ │ ├── m009_pxrom.rs
│ │ │ ├── m010_fxrom.rs
│ │ │ ├── m011_color_dreams.rs
│ │ │ ├── m018_jalecoss88006.rs
│ │ │ ├── m019_namco163.rs
│ │ │ ├── m024_m026_vrc6.rs
│ │ │ ├── m034_bnrom.rs
│ │ │ ├── m034_nina001.rs
│ │ │ ├── m066_gxrom.rs
│ │ │ ├── m069_sunsoft_fme7.rs
│ │ │ ├── m071_bf909x.rs
│ │ │ ├── m079_nina003_006.rs
│ │ │ └── vrc_irq.rs
│ │ ├── mapper.rs
│ │ ├── mem.rs
│ │ ├── ppu/
│ │ │ ├── ctrl.rs
│ │ │ ├── frame.rs
│ │ │ ├── mask.rs
│ │ │ ├── scroll.rs
│ │ │ ├── sprite.rs
│ │ │ └── status.rs
│ │ ├── ppu.rs
│ │ ├── sys/
│ │ │ ├── fs/
│ │ │ │ ├── os.rs
│ │ │ │ └── wasm.rs
│ │ │ ├── fs.rs
│ │ │ └── time.rs
│ │ ├── sys.rs
│ │ ├── time.rs
│ │ └── video.rs
│ └── test_roms/
│ ├── apu/
│ │ ├── apu_env.nes
│ │ ├── blargg_readme.txt
│ │ ├── clock_jitter.nes
│ │ ├── dmc.nes
│ │ ├── dmc_basics.nes
│ │ ├── dmc_buffer_retained.nes
│ │ ├── dmc_dma_2007_read.nes
│ │ ├── dmc_dma_2007_write.nes
│ │ ├── dmc_dma_4016_read.nes
│ │ ├── dmc_dma_double_2007_read.nes
│ │ ├── dmc_dma_read_write_2007.nes
│ │ ├── dmc_latency.nes
│ │ ├── dmc_pitch.nes
│ │ ├── dmc_rates.nes
│ │ ├── dmc_status.nes
│ │ ├── dmc_status_irq.nes
│ │ ├── dpcmletterbox.nes
│ │ ├── dpcmletterbox.txt
│ │ ├── irq_flag.nes
│ │ ├── irq_flag_timing.nes
│ │ ├── irq_timing.nes
│ │ ├── len_ctr.nes
│ │ ├── len_halt_timing.nes
│ │ ├── len_reload_timing.nes
│ │ ├── len_table.nes
│ │ ├── len_timing.nes
│ │ ├── len_timing_mode0.nes
│ │ ├── len_timing_mode1.nes
│ │ ├── lin_ctr.nes
│ │ ├── mixer.txt
│ │ ├── noise.nes
│ │ ├── noise_pitch.nes
│ │ ├── pal_clock_jitter.nes
│ │ ├── pal_irq_flag.nes
│ │ ├── pal_irq_flag_timing.nes
│ │ ├── pal_irq_timing.nes
│ │ ├── pal_len_ctr.nes
│ │ ├── pal_len_halt_timing.nes
│ │ ├── pal_len_reload_timing.nes
│ │ ├── pal_len_table.nes
│ │ ├── pal_len_timing_mode0.nes
│ │ ├── pal_len_timing_mode1.nes
│ │ ├── pal_readme.txt
│ │ ├── phase_reset.nes
│ │ ├── readme.txt
│ │ ├── reset.txt
│ │ ├── reset_4015_cleared.nes
│ │ ├── reset_4017_timing.nes
│ │ ├── reset_4017_written.nes
│ │ ├── reset_irq_flag_cleared.nes
│ │ ├── reset_len_ctrs_enabled.nes
│ │ ├── reset_timing.nes
│ │ ├── reset_works_immediately.nes
│ │ ├── square.nes
│ │ ├── square_pitch.nes
│ │ ├── sweep_cutoff.nes
│ │ ├── sweep_sub.nes
│ │ ├── test_1.nes
│ │ ├── test_10.nes
│ │ ├── test_2.nes
│ │ ├── test_3.nes
│ │ ├── test_4.nes
│ │ ├── test_5.nes
│ │ ├── test_6.nes
│ │ ├── test_7.nes
│ │ ├── test_8.nes
│ │ ├── test_9.nes
│ │ ├── tests.json
│ │ ├── triangle.nes
│ │ ├── triangle_pitch.nes
│ │ ├── volumes.nes
│ │ └── volumes.txt
│ ├── cpu/
│ │ ├── branch.txt
│ │ ├── branch_backward.nes
│ │ ├── branch_basics.nes
│ │ ├── branch_forward.nes
│ │ ├── dummy_reads.nes
│ │ ├── dummy_writes.txt
│ │ ├── dummy_writes_oam.nes
│ │ ├── dummy_writes_ppumem.nes
│ │ ├── exec_space.txt
│ │ ├── exec_space_apu.nes
│ │ ├── exec_space_ppuio.nes
│ │ ├── flag_concurrency.nes
│ │ ├── instr.txt
│ │ ├── instr_abs.nes
│ │ ├── instr_abs_xy.nes
│ │ ├── instr_basics.nes
│ │ ├── instr_branches.nes
│ │ ├── instr_brk.nes
│ │ ├── instr_imm.nes
│ │ ├── instr_imp.nes
│ │ ├── instr_ind_x.nes
│ │ ├── instr_ind_y.nes
│ │ ├── instr_jmp_jsr.nes
│ │ ├── instr_misc.nes
│ │ ├── instr_misc.txt
│ │ ├── instr_rti.nes
│ │ ├── instr_rts.nes
│ │ ├── instr_special.nes
│ │ ├── instr_stack.nes
│ │ ├── instr_timing.nes
│ │ ├── instr_timing.txt
│ │ ├── instr_zp.nes
│ │ ├── instr_zp_xy.nes
│ │ ├── int_branch_delays_irq.nes
│ │ ├── int_cli_latency.nes
│ │ ├── int_irq_and_dma.nes
│ │ ├── int_nmi_and_brk.nes
│ │ ├── int_nmi_and_irq.nes
│ │ ├── interrupts.txt
│ │ ├── nestest.nes
│ │ ├── nestest.txt
│ │ ├── overclock.nes
│ │ ├── ram_after_reset.nes
│ │ ├── regs_after_reset.nes
│ │ ├── reset.txt
│ │ ├── sprdma_and_dmc_dma.nes
│ │ ├── sprdma_and_dmc_dma_512.nes
│ │ ├── tests.json
│ │ ├── timing.txt
│ │ └── timing_test.nes
│ ├── input/
│ │ ├── tests.json
│ │ ├── zapper_flip.nes
│ │ ├── zapper_light.nes
│ │ ├── zapper_stream.nes
│ │ └── zapper_trigger.nes
│ ├── mapper/
│ │ ├── m004_txrom/
│ │ │ ├── a12_clocking.nes
│ │ │ ├── big_chr_ram.nes
│ │ │ ├── clocking.nes
│ │ │ ├── details.nes
│ │ │ ├── irq.txt
│ │ │ ├── rev_a.nes
│ │ │ ├── rev_b.nes
│ │ │ ├── scanline_timing.nes
│ │ │ └── tests.json
│ │ └── m005_exrom/
│ │ ├── basics.nes
│ │ ├── exram.nes
│ │ └── tests.json
│ ├── ppu/
│ │ ├── _240pee.nes
│ │ ├── blargg_readme.txt
│ │ ├── color.nes
│ │ ├── ntsc_torture.nes
│ │ ├── oam_read.nes
│ │ ├── oam_read.txt
│ │ ├── oam_stress.nes
│ │ ├── oam_stress.txt
│ │ ├── open_bus.nes
│ │ ├── open_bus.txt
│ │ ├── palette.nes
│ │ ├── palette_ram.nes
│ │ ├── read_buffer.nes
│ │ ├── read_buffer.txt
│ │ ├── scanline.nes
│ │ ├── spr_hit.txt
│ │ ├── spr_hit_alignment.nes
│ │ ├── spr_hit_basics.nes
│ │ ├── spr_hit_corners.nes
│ │ ├── spr_hit_double_height.nes
│ │ ├── spr_hit_edge_timing.nes
│ │ ├── spr_hit_flip.nes
│ │ ├── spr_hit_left_clip.nes
│ │ ├── spr_hit_right_edge.nes
│ │ ├── spr_hit_screen_bottom.nes
│ │ ├── spr_hit_timing_basics.nes
│ │ ├── spr_hit_timing_order.nes
│ │ ├── spr_overflow.txt
│ │ ├── spr_overflow_basics.nes
│ │ ├── spr_overflow_details.nes
│ │ ├── spr_overflow_emulator.nes
│ │ ├── spr_overflow_obscure.nes
│ │ ├── spr_overflow_timing.nes
│ │ ├── sprite_ram.nes
│ │ ├── tests.json
│ │ ├── tv.nes
│ │ ├── tv.txt
│ │ ├── vbl_nmi.txt
│ │ ├── vbl_nmi_basics.nes
│ │ ├── vbl_nmi_clear_timing.nes
│ │ ├── vbl_nmi_control.nes
│ │ ├── vbl_nmi_disable.nes
│ │ ├── vbl_nmi_even_odd_frames.nes
│ │ ├── vbl_nmi_even_odd_timing.nes
│ │ ├── vbl_nmi_frame_basics.nes
│ │ ├── vbl_nmi_off_timing.nes
│ │ ├── vbl_nmi_on_timing.nes
│ │ ├── vbl_nmi_set_time.nes
│ │ ├── vbl_nmi_suppression.nes
│ │ ├── vbl_nmi_timing.nes
│ │ ├── vbl_nmi_timing.txt
│ │ ├── vbl_timing.nes
│ │ └── vram_access.nes
│ └── spritecans.nes
├── tetanes-utils/
│ ├── Cargo.toml
│ └── src/
│ └── bin/
│ ├── generate_db.rs
│ └── list_boards.rs
└── vendored/
├── linuxdeploy-aarch64.AppImage
└── linuxdeploy-x86_64.AppImage
================================================
FILE CONTENTS
================================================
================================================
FILE: .cargo/config.toml
================================================
[build]
rustflags = ["-Z", "threads=8"]
[target.'cfg(target_arch = "wasm32")']
rustflags = [
"-Zthreads=8",
"-Zwasm-c-abi=spec",
"--cfg=web_sys_unstable_apis",
"--cfg=getrandom_backend=\"wasm_js\"",
]
================================================
FILE: .config/nextest.toml
================================================
[profile.ci]
fail-fast = false
slow-timeout = { period = "30s", terminate-after = 4 }
test-threads = 1
================================================
FILE: .git-blame-ignore-revs
================================================
================================================
FILE: .gitattributes
================================================
*.rs linguist-detectable=true
*.js linguist-detectable=false
*.html linguist-detectable=false
================================================
FILE: .github/ISSUE_TEMPLATE/defect-report.md
================================================
---
name: Defect Report
about: Report issues to improve TetaNES
title: ''
labels: needs-triage bug
assignees: lukexor
---
## Describe the issue
A clear and concise description of what the defect is.
Be sure to try the suggestions in the
[Troubleshooting](https://github.com/lukexor/tetanes#troubleshooting) section of
the README first.
## To Reproduce
Steps to reproduce the behavior:
1. Load '...'
1. Press '....'
1. See error
## Expected behavior
A clear and concise description of what you expected to happen.
## Screenshots, Logs or Artifacts
If applicable, attach screenshots, logs, configuration files, or save states to
help explain or reproduce the issue. See the
[Directories](https://github.com/lukexor/tetanes#directories)
section for file locations.
## Environment
- ROM title
- Please don't attach any download links or ROMs due to copyright laws.
- Operating System and Version
- e.g. Windows 7, macOS Mojave 10.14.6, Ubuntu 22.04
- Browser Vendor and Version (if using TetaNES Web)
- e.g. Chrome 77.0.3865
- TetaNES Version
- Can be found in the `About` menu, e.g. 0.12.1
## Additional context
Add any other context about the issue here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature Request
about: Suggest an idea
title: ''
labels: needs-triage enhancement
assignees: lukexor
---
## Is your feature request related to a problem?
A clear and concise description of what the problem is. e.g. I'm always
frustrated when [...].
## Describe the solution you'd like
A clear and concise description of what you want to happen.
## Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've
considered.
## Additional context
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/dependabot.yml
================================================
---
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
assignees:
- "lukexor"
open-pull-requests-limit: 1
groups:
ci-dependencies:
patterns:
- "*"
================================================
FILE: .github/workflows/cd.yml
================================================
---
name: CD
# yamllint disable-line rule:truthy
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: "Release tag"
required: true
type: string
os:
description: "Target platform"
required: true
type: choice
options:
- all
- linux
- macos
- windows
- web
permissions:
contents: write
env:
# Unnecessary for CI and just pollutes cache
CARGO_INCREMENTAL: 0
# Remove debug symbols, substantially reduces cache size
CARGO_PROFILE_DEV_DEBUG: 0
CARGO_PROFILE_TEST_DEBUG: 0
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
build-linux:
name: Build Linux Artifacts (${{ matrix.target }})
if: >
((startsWith(github.event.release.name, 'tetanes')
&& !startsWith(github.event.release.name, 'tetanes-core'))
|| (inputs.tag && (inputs.os == 'all' || inputs.os == 'linux')))
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
# TODO: aarch64 linux having trouble with docker in CI
# - target: aarch64-unknown-linux-gnu
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- uses: Swatinem/rust-cache@v2
- uses: baptiste0928/cargo-install@v3
with:
crate: cross
git: https://github.com/cross-rs/cross
commit: 19bc73f
- uses: taiki-e/install-action@v2
with:
tool: cargo-make,cargo-deb,cargo-pgo
- run: |
sudo apt update
sudo apt install -y libudev-dev libasound2-dev libssl-dev libfuse2
- if: startsWith(matrix.target, 'x86_64')
run: |
time cargo make build-artifacts -- --target ${{ matrix.target }}
# aarch64 requires cross building
- if: startsWith(matrix.target, 'aarch64')
run: |
export CROSS_CONTAINER_IN_CONTAINER=true
time cargo make build-artifacts -- --target ${{ matrix.target }} --cross
- if: success()
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.target }}-artifacts
path: tetanes/dist/
build-macos:
name: Build macOS Artifacts (${{ matrix.target }})
if: >
((startsWith(github.event.release.name, 'tetanes')
&& !startsWith(github.event.release.name, 'tetanes-core'))
|| (inputs.tag && (inputs.os == 'all' || inputs.os == 'macos')))
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-apple-darwin
- target: aarch64-apple-darwin
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/setup-cross-toolchain-action@v1
with:
target: ${{ matrix.target }}
- uses: taiki-e/install-action@v2
with:
tool: cargo-make,cargo-pgo
- run: |
time cargo make build-artifacts -- --target ${{ matrix.target }}
- if: success()
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.target }}-artifacts
path: tetanes/dist/
build-windows:
name: Build Windows Artifacts (${{ matrix.target }})
if: >
((startsWith(github.event.release.name, 'tetanes')
&& !startsWith(github.event.release.name, 'tetanes-core'))
|| (inputs.tag && (inputs.os == 'all' || inputs.os == 'windows')))
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-pc-windows-msvc
# TODO: windows aarch64
# - target: aarch64-pc-windows-msvc
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/setup-cross-toolchain-action@v1
with:
target: ${{ matrix.target }}
- uses: taiki-e/install-action@v2
with:
tool: cargo-make,cargo-wix,cargo-pgo
- if: startsWith(matrix.target, 'x86_64')
run: |
time cargo make build-artifacts -- --target ${{ matrix.target }}
- if: success()
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.target }}-artifacts
path: tetanes/dist/
build-web:
name: Build Web Artifacts (wasm32-unknown-unknown)
if: >
((startsWith(github.event.release.name, 'tetanes')
&& !startsWith(github.event.release.name, 'tetanes-core'))
|| (inputs.tag && (inputs.os == 'all' || inputs.os == 'web')))
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: cargo-make,trunk
- run: |
sudo apt update
sudo apt install -y libudev-dev libasound2-dev libssl-dev libfuse2
- run: |
time cargo make build-artifacts -- --target wasm32-unknown-unknown
- if: success()
uses: actions/upload-artifact@v7
with:
name: wasm32-unknown-unknown-artifacts
path: tetanes/dist/
upload-artifacts:
name: Attach Release Artifacts
runs-on: ubuntu-latest
needs: [build-linux, build-macos, build-windows, build-web]
if: |
always() && contains(needs.*.result, 'success')
steps:
- uses: actions/download-artifact@v8
with:
path: artifacts
merge-multiple: true
- env:
GH_TOKEN: ${{ github.token }}
run: |
gh release upload ${{ github.event.release.tag_name || inputs.tag }} \
artifacts/* --clobber --repo "${{ github.repository }}"
update-tetanes-web:
name: Update TetaNES Web
needs: build-web
runs-on: ubuntu-latest
env:
RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
steps:
- uses: actions/checkout@v6
with:
repository: "lukexor/lukeworks"
token: ${{ secrets.REPOS }}
- uses: actions/download-artifact@v8
with:
path: artifacts
pattern: "wasm32-unknown-unknown-artifacts"
- id: commit
run: |
rm -f web/public/tetanes-web/*
tar -xzf artifacts/*.tar.gz -C web/public/tetanes-web/
VERSION=${RELEASE_TAG#"tetanes-v"}
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- uses: stefanzweifel/git-auto-commit-action@v7
with:
file_pattern: "web/public/tetanes-web/*"
commit_message: Updated TetaNES Web v${{ steps.commit.outputs.version }}
update-homebrew-formula:
name: Update Homebrew Formula
needs: build-macos
runs-on: ubuntu-latest
env:
RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
steps:
- uses: actions/checkout@v6
with:
repository: "lukexor/homebrew-formulae"
token: ${{ secrets.REPOS }}
- uses: actions/download-artifact@v8
with:
path: artifacts
pattern: "*-apple-darwin-artifacts"
merge-multiple: true
- id: commit
run: |
x86_64_SHA=$(cat artifacts/*-x86_64-apple-darwin.tar.gz-sha256.txt | awk '{ print $1 }')
aarch64_SHA=$(cat artifacts/*-aarch64-apple-darwin.tar.gz-sha256.txt | awk '{ print $1 }')
VERSION=${RELEASE_TAG#"tetanes-v"}
cat tetanes.rb.tmpl | \
sed "s/%VERSION%/${VERSION}/g" | \
sed "s/%x86_64_SHA%/${x86_64_SHA}/g" | \
sed "s/%aarch64_SHA%/${aarch64_SHA}/g" \
> Casks/tetanes.rb
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- uses: stefanzweifel/git-auto-commit-action@v7
with:
file_pattern: "*.rb"
commit_message: Version Bump v${{ steps.commit.outputs.version }}
================================================
FILE: .github/workflows/ci.yml
================================================
---
name: CI
# yamllint disable-line rule:truthy
on:
push:
branches: [main]
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
env:
# Unnecessary for CI and just pollutes cache
CARGO_INCREMENTAL: 0
# Remove debug symbols, substantially reduces cache size
CARGO_PROFILE_DEV_DEBUG: 0
CARGO_PROFILE_TEST_DEBUG: 0
CARGO_TERM_COLOR: always
RUST_LOG: debug
RUST_BACKTRACE: 1
jobs:
format:
name: Check format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
targets: wasm32-unknown-unknown
components: clippy
- uses: Swatinem/rust-cache@v2
- run: |
time cargo fmt --all --check
lint-web:
name: Lint Web
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
targets: wasm32-unknown-unknown
components: clippy
- uses: Swatinem/rust-cache@v2
- run: |
time cargo clippy --locked --lib --bin tetanes --target wasm32-unknown-unknown --all-features --keep-going -- -D warnings
lint-tetanes:
name: Lint TetaNES (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
components: clippy
- uses: Swatinem/rust-cache@v2
- if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt update
sudo apt install -y libudev-dev libasound2-dev
- run: |
cargo clippy --locked -p tetanes --all-features --keep-going -- -D warnings
lint-tetanes-core:
name: Lint TetaNES Core (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
toolchain: [nightly, stable, 1.85]
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
components: clippy
- uses: Swatinem/rust-cache@v2
- env:
# Unset nightly RUSTFLAGS so we can lint with non-nightly toolchains
CARGO_ENCODED_RUSTFLAGS: ""
run: |
cargo +${{ matrix.toolchain }} clippy --locked -p tetanes-core --all-features --keep-going -- -D warnings
# No tests currently
# test-tetanes:
# name: Test TetaNES
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v6
# with:
# fetch-depth: 0
# - uses: dtolnay/rust-toolchain@master
# with:
# toolchain: nightly
# - uses: taiki-e/install-action@v2
# with:
# tool: cargo-nextest
# - uses: Swatinem/rust-cache@v2
# - run: |
# sudo apt update
# sudo apt install -y libudev-dev libasound2-dev
# - run: |
# cargo nextest run --locked -p tetanes --all-features --profile ci
test-tetanes-core:
name: Test TetaNES Core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
- uses: Swatinem/rust-cache@v2
- run: |
cargo nextest run --locked -p tetanes-core --all-features --profile ci
docs-web:
name: Docs Web
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- env:
RUSTDOCFLAGS: -D warnings
run: |
time cargo doc --locked --no-deps --document-private-items --lib --target wasm32-unknown-unknown --all-features --keep-going
docs-tetanes:
name: Docs TetaNES
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- uses: Swatinem/rust-cache@v2
- if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt update
sudo apt install -y libudev-dev libasound2-dev
- env:
RUSTDOCFLAGS: -D warnings
run: |
cargo doc --locked --no-deps --document-private-items --all-features --workspace --keep-going
================================================
FILE: .github/workflows/outdated.yml
================================================
---
name: Check Outdated
# yamllint disable-line rule:truthy
on:
schedule:
# At 06:00 on day-of-month 2 and 16
- cron: "0 6 2,16 * *"
permissions:
contents: read
env:
# Unnecessary for CI and just pollutes cache
CARGO_INCREMENTAL: 0
# Remove debug symbols, substantially reduces cache size
CARGO_PROFILE_DEV_DEBUG: 0
CARGO_PROFILE_TEST_DEBUG: 0
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
outdated:
name: Check Outdated
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: dtolnay/install@cargo-outdated
- run: |
# gilrs and sysinfo currently conflict over objc2-core-foundation
# accesskit is blocked by egui upgrading it
# criterion is blocked by pprof upgrading it
cargo outdated -e -d 1 --exit-code 1 \
--ignore gilrs \
--ignore sysinfo \
--ignore accesskit \
--ignore accesskit_winit \
--ignore criterion
================================================
FILE: .github/workflows/release-pr.yml
================================================
---
name: Release PR
# yamllint disable-line rule:truthy
on:
push:
branches: [main]
permissions:
pull-requests: write
contents: write
id-token: write
env:
# Unnecessary for CI and just pollutes cache
CARGO_INCREMENTAL: 0
# Remove debug symbols, substantially reduces cache size
CARGO_PROFILE_DEV_DEBUG: 0
CARGO_PROFILE_TEST_DEBUG: 0
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
release-pr:
name: Release PR
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'lukexor' }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
# Required to trigger post-release workflows
token: ${{ secrets.RELEASE_PLZ_TOKEN }}
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- uses: Swatinem/rust-cache@v2
- run: |
sudo apt update
sudo apt install -y libudev-dev libasound2-dev
- uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
- uses: Swatinem/rust-cache@v2
- run: |
cargo nextest run --locked -p tetanes-core --all-features --profile ci
- name: Run release-plz
uses: MarcoIeni/release-plz-action@v0.5
env:
# Required to trigger post-release workflows
GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }}
================================================
FILE: .github/workflows/security.yml
================================================
---
name: Security Audit
# yamllint disable-line rule:truthy
on:
schedule:
# At 06:00 once a week on Sunday
- cron: "0 6 * * 0"
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
audit:
name: Security Audit
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v6
- uses: EmbarkStudios/cargo-deny-action@v2
================================================
FILE: .github/workflows/triage.yml
================================================
---
name: Triage Issues
# yamllint disable-line rule:truthy
on:
issues:
types: [opened, reopened]
permissions:
issues: write
jobs:
triage:
name: Triage Issue
runs-on: ubuntu-latest
steps:
- name: add needs-triage label
uses: andymckay/labeler@master
with:
add-labels: "needs-triage"
ignore-if-labeled: true
================================================
FILE: .gitignore
================================================
tmp/
test_results*
target/
.DS_Store
!assets/macos/.DS_Store
logs/
dist/
.direnv
perf.*
================================================
FILE: .gitmodules
================================================
================================================
FILE: .prettierignore
================================================
*.wxs
================================================
FILE: .rgignore
================================================
roms
test_roms
docs
================================================
FILE: Cargo.toml
================================================
# Disabled for now since it was ICEing
# cargo-features = ["codegen-backend"]
[workspace]
resolver = "2"
members = ["tetanes", "tetanes-core", "tetanes-utils"]
[workspace.package]
version = "0.14.1"
edition = "2024"
license = "MIT OR Apache-2.0"
authors = ["Luke Petherbridge <me@lukeworks.tech>"]
readme = "README.md"
repository = "https://github.com/lukexor/tetanes.git"
homepage = "https://lukeworks.tech/tetanes"
[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
missing_const_for_fn = "warn"
print_literal = "warn"
[workspace.lints.rust]
future_incompatible = "warn"
nonstandard_style = "warn"
rust_2018_compatibility = "warn"
rust_2018_idioms = "warn"
rust_2021_compatibility = "warn"
unused = "warn"
[workspace.dependencies]
anyhow = "1.0"
bincode = { version = "2.0", default-features = false, features = [
"std",
"serde",
] }
cfg-if = "1.0"
clap = { version = "4.5", default-features = false, features = [
"std",
"help",
"usage",
"suggestions",
"derive",
] }
dirs = "6.0"
image = { version = "0.25", default-features = false, features = ["png"] }
serde = { version = "1.0", features = ["derive"] }
tetanes-core = { version = "0.14", path = "tetanes-core" }
thiserror = "2.0"
tracing = { version = "0.1", default-features = false, features = [
"std",
"release_max_level_info",
] }
tracing-subscriber = { version = "0.3", default-features = false, features = [
"ansi",
"std",
"registry",
"parking_lot",
] }
serde_json = "1.0"
web-time = "1.0"
web-sys = "0.3.95"
[profile.dev]
# `debug = false` Saves both compile time and WASM download times
debug = false
# Playable framerates in development
opt-level = 1
# Higher opt-level for deps speeds up incremental compile times and can improve
# runtime performance
[profile.dev.package."*"]
opt-level = 3
[profile.dev.build-override]
opt-level = 3
# TODO: Would be nice to move lto to `dist` but Trunk doesn't support profiles yet
# See: https://github.com/trunk-rs/trunk/issues/605
# https://github.com/trunk-rs/trunk/issues/933
[profile.release]
codegen-units = 1
lto = true
# See: https://smallcultfollowing.com/babysteps/blog/2024/05/02/unwind-considered-harmful/
panic = "abort"
strip = true
# Profile to run performance profiling with debug symbols
[profile.perf]
inherits = "release"
lto = "off"
debug = true
strip = false
[workspace.metadata.wix]
upgrade-guid = "DB76CEB0-15B8-4727-9C3E-55819AB5E7B9"
path-guid = "5731AE63-80DE-4CD7-ADFA-9E79BEDCE08B"
license = false
eula = false
================================================
FILE: Cross.toml
================================================
[build]
pre-build = [
"dpkg --add-architecture $CROSS_DEB_ARCH",
"""apt update && apt install -y \
libudev-dev:$CROSS_DEB_ARCH \
libssl-dev:$CROSS_DEB_ARCH \
libasound2-dev:$CROSS_DEB_ARCH
""",
]
================================================
FILE: LICENSE-APACHE
================================================
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: LICENSE-MIT
================================================
MIT License
Copyright (c) 2021 Luke Petherbridge <me@lukeworks.tech>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile.toml
================================================
[env]
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
[config]
reduce_output = false
skip_core_tasks = true
default_to_workspace = false
[tasks.default]
alias = "run"
[tasks.version]
description = "Print the crate version"
category = "Tools"
script = ["echo Version: ${CARGO_MAKE_PROJECT_VERSION}"]
[tasks.clean]
description = "Clean up build artifacts"
category = "Development"
command = "cargo"
args = ["clean"]
[tasks.check-fmt]
description = "Check format"
category = "Development"
command = "cargo"
args = ["fmt", "--all", "--check"]
[tasks.lint-web]
description = "Lint TetaNES Web"
category = "Development"
command = "cargo"
args = ["clippy", "--locked", "--lib", "--bin", "tetanes", "--target", "wasm32-unknown-unknown", "--all-features", "--keep-going"]
dependencies = ["add-wasm-target"]
[tasks.lint]
description = "Lint TetaNES"
category = "Development"
command = "cargo"
args = ["clippy", "--locked", "--all-features", "--keep-going"]
dependencies = ["lint-web"]
[tasks.pgo-profile]
description = "Run benchmark to generate PGO profile"
category = "Build"
command = "cargo"
args = ["pgo", "bench", "--", "--bench", "clock_frame"]
[tasks.pgo-build]
description = "Optimize TetaNES with PGO"
category = "Build"
command = "cargo"
args = ["pgo", "optimize", "build", "--", "-p", "tetanes", "${@}"]
[tasks.build]
description = "Build TetaNES with PGO"
category = "Build"
dependencies = ["pgo-profile", "pgo-build"]
[tasks.build-artifacts]
description = "Build TetaNES Artifacts for a given target_arch"
category = "Build"
command = "cargo"
args = ["run", "--bin", "build_artifacts", "${@}"]
[tasks.build-cross]
description = "Cross-Build TetaNES for a given target_arch"
category = "Build"
command = "cross"
args = ["build", "-p", "tetanes", "${@}"]
[tasks.bench]
description = "Benchmark TetaNES"
category = "Development"
command = "perf"
args = [
"stat", "-e",
"cycles,instructions,cache-misses,cache-references,branch-misses,branches,L1-dcache-load-misses,L1-dcache-loads",
"taskset", "-c", "0", "cargo", "bench", "--profile", "perf", "--bench", "clock_frame", "${@}"
]
[tasks.test]
description = "Test TetaNES"
category = "Development"
command = "cargo"
args = ["nextest", "run", "--locked", "--all-features", "--no-fail-fast", "${@}"]
[tasks.run]
description = "Run TetaNES in release mode"
category = "Development"
command = "cargo"
args = ["run", "-p", "tetanes", "--release", "${@}"]
[tasks.profile]
description = "Run TetaNES in release mode w/profiling"
category = "Development"
command = "cargo"
args = ["run", "-p", "tetanes", "--release", "--features", "profiling", "${@}"]
[tasks.dev]
description = "Run TetaNES in development mode"
category = "Development"
command = "cargo"
args = ["run", "-p", "tetanes", "${@}"]
[tasks.add-wasm-target]
description = "Add wasm target"
category = "Development"
command = "rustup"
args = ["target", "add", "wasm32-unknown-unknown"]
[tasks.build-web]
description = "Build TetaNES Web"
category = "Build"
command = "trunk"
args = ["build", "--config", "tetanes/Cargo.toml", "--release", "--dist", "dist/web", "--public-url", "./"]
dependencies = ["add-wasm-target"]
[tasks.docs-web]
description = "Document TetaNES Web"
category = "Documentation"
command = "cargo"
args = ["doc", "--locked", "--no-deps", "--document-private-items", "--lib", "--target", "wasm32-unknown-unknown", "--all-features", "--keep-going"]
dependencies = ["add-wasm-target"]
[tasks.docs]
description = "Document TetaNES"
category = "Documentation"
command = "cargo"
args = ["doc", "--locked", "--no-deps", "--document-private-items", "--all-features", "--workspace", "--keep-going"]
dependencies = ["docs-web"]
[tasks.run-web]
description = "Run TetaNES Web in release mode"
category = "Development"
command = "trunk"
args = ["serve", "--release", "--config", "tetanes/Cargo.toml", "--address", "0.0.0.0"]
dependencies = ["add-wasm-target"]
[tasks.profile-web]
description = "Run TetaNES Web in release mode w/profiling"
category = "Development"
command = "trunk"
args = ["serve", "--release", "--features", "profiling", "--config", "tetanes/Cargo.toml", "--address", "0.0.0.0"]
dependencies = ["add-wasm-target"]
[tasks.dev-web]
description = "Run TetaNES Web in development mode"
category = "Development"
command = "trunk"
args = ["serve", "--config", "tetanes/Cargo.toml", "--address", "0.0.0.0"]
dependencies = ["add-wasm-target"]
================================================
FILE: README.md
================================================
<!-- markdownlint-disable no-inline-html no-duplicate-heading -->
# TetaNES
[![Build Status]][build] [![Doc Status]][docs] [![Latest Version]][crates.io]
[![Downloads]][crates.io] [![License]][gnu]
[build status]: https://img.shields.io/github/actions/workflow/status/lukexor/tetanes/ci.yml?branch=main
[build]: https://github.com/lukexor/tetanes/actions/workflows/ci.yml
[doc status]: https://img.shields.io/docsrs/tetanes?style=plastic
[docs]: https://docs.rs/tetanes/
[latest version]: https://img.shields.io/crates/v/tetanes?style=plastic
[crates.io]: https://crates.io/crates/tetanes
[downloads]: https://img.shields.io/crates/d/tetanes?style=plastic
[license]: https://img.shields.io/crates/l/tetanes?style=plastic
[gnu]: https://github.com/lukexor/tetanes/blob/main/LICENSE-MIT
<!-- markdownlint-disable line-length -->
📖 [Summary](#summary) - ✨ [Features](#features) - 🌆 [Screenshots](#screenshots) - 🚀 [Getting
Started](#getting-started) - 🛠️ [Roadmap](#roadmap) - ⚠️ [Known
Issues](#known-issues) - 💬 [Contact](#contact)
<!-- markdownlint-enable line-length -->
## Summary
<img width="100%" alt="TetaNES"
src="https://raw.githubusercontent.com/lukexor/tetanes/main/static/tetanes.png">
> photo credit for background: [Zsolt Palatinus](https://unsplash.com/@sunitalap)
> on [unsplash](https://unsplash.com/photos/pEK3AbP8wa4)
`TetaNES` is a cross-platform emulator for the Nintendo Entertainment System
(NES) released in Japan in 1983 and North America in 1986, written in
[Rust][] using [wgpu][]. It runs on Linux, macOS, Windows, and in a web browser
with [Web Assembly][].
It started as a personal curiosity that turned into a passion project. It is
still being actively developed with new features and improvements constantly
being added. It is a fairly accurate emulator that can play most NES titles.
`TetaNES` is also meant to showcase using Rust's performance, memory safety, and
fearless concurrency features in a large project. Features used in this project
include complex enums, traits, generics, matching, iterators, channels, and
threads.
Try it out in your [browser](https://lukeworks.tech/tetanes-web)!
## Features
- Runs on Linux, macOS, Windows, and Web.
- Standalone emulation core in `tetanes-core`.
- NTSC, PAL and Dendy emulation.
- Headless Mode when using `tetanes-core`.
- Pixellate and NTSC filters.
- CRT shader for that retro feel.
- Up to 4 players with gamepad support.
- Zapper (Light Gun) support using the mouse.
- iNES and NES 2.0 ROM header formats supported.
- Over 30 supported mappers covering >90% of licensed games.
- Game Genie Codes.
- PPU Debugger
- Runtime performance stats
- Preference snd keybonding menus using [egui](https://egui.rs).
- Increase/Decrease speed & Fast Forward
- Visual & Instant Rewind
- Save & Load States
- Battery-backed RAM saves
- Screenshots
- Gameplay recording and playback
- Audio recording
## Screenshots
<img width="48%" alt="Donkey Kong"
src="https://raw.githubusercontent.com/lukexor/tetanes/main/static/donkey_kong.png"> <img
width="48%" alt="Super Mario Bros."
src="https://raw.githubusercontent.com/lukexor/tetanes/main/static/super_mario_bros.png">
<img width="48%" alt="The Legend of Zelda"
src="https://raw.githubusercontent.com/lukexor/tetanes/main/static/legend_of_zelda.png"> <img
width="48%" alt="Metroid"
src="https://raw.githubusercontent.com/lukexor/tetanes/main/static/metroid.png">
## TetaNES Core
TetaNES is split into two crates. This is the primary crate, which provides the
cross-platform emulator UI
binary. [tetanes-core](https://crates.io/crates/tetanes_core) is the emulation
library that emulator developers can use to develop custom emulator applications
with. `tetanes-core` is aimed to have stronger stability guarantees, but it's
still not `1.0` yet and there are several large features on the roadmap that may
result in breaking changes.
## Stability
Preferences and save file formats are fairly stable at this point, but since
TetaNES is not yet `1.0` and there are several large features on the roadmap
that may result in breaking changes which may result in being unable to restore
your preferences or save files.
Once some of these larger features are completed, and `1.0` is released, more
effort will be dedicatged to versioning these files for backward compatibility
in the event of future breaking changes.
## Getting Started
`TetaNES` runs on all major operating systems (Linux, macOS, Windows, and the
web).
### Install
There are multiple options for installation, depending on your operating system,
preference and existing tooling.
#### Linux
##### Ubuntu/Debian
A `.deb` package is provided under `Assets` on the latest [Release][]. Once
downloaded, you can install it and the required dependencies. e.g.
```sh
sudo apt install ./tetanes-0.10.0-1-amd64.deb
```
##### Other Distros
An [AppImage](https://appimage.org/) is provided under `Assets` on the latest
[Release][]. Simply download it and put it wherever you want.
A `.tar.gz` package is also provided under `Assets` on the latest
[Release][]. You can place the `tetanes` binary anywhere in your `PATH`.
The following dependencies are required to be installed:
- ALSA Shared Library
- GTK3
e.g.
`apt install libasound2 libgtk-3-0`
`dnf install alsa-lib gtk3`
`pacman -Sy alsa-lib gtk3`
#### MacOS
##### App Bundle
The easiest is to download the correct app bundle for your processor. The `.dmg`
downloads can be found under the `Assets` section of the latest
[Release][].
##### Homebrew
`TetaNES` can also be installed through [Homebrew](https://brew.sh/).
```sh
brew install lukexor/formulae/tetanes
```
#### Windows
A windows installer is provided under `Assets` on the latest [Release][]. Note:
You will need the latest ["Microsoft Visual C++ 2015 - 2022
Redistributable"](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-microsoft-visual-c-redistributable-version)
installed, otherwise you'll get an error about `vcruntime140.dll` not being found.
#### Cargo Install
You can also build and install with `cargo` which comes with [rustup](https://www.rust-lang.org/tools/install).
```sh
cargo install tetanes
```
This will install the latest released version of the `TetaNES` binary to your
`cargo` bin directory located at either `$HOME/.cargo/bin/` on a Unix-like
platform or `%USERPROFILE%\.cargo\bin` on Windows.
Alternatively, if you have [`cargo binstall`](https://crates.io/crates/cargo-binstall/)
installed:
```sh
cargo binstall tetanes
```
This will try to find the target binary for your platform from the latest
[Release][] or install from source, similar to above.
#### Web
You can also play directly in your web browser without installing by visiting
<https://lukeworks.tech/tetanes-web>.
### Usage
```text
Usage: tetanes [OPTIONS] [PATH]
Arguments:
[PATH] The NES ROM to load or a directory containing `.nes` ROM files.
[default: current directory]
Options:
--rewind Enable rewinding
-s, --silent Silence audio
-f, --fullscreen Start fullscreen
-4, --four-player <FOUR_PLAYER> Set four player adapter. [default: 'disabled']
[possible values: disabled, four-score, satellite]
-z, --zapper Enable zapper gun
--no-threaded Disable multi-threaded
-m, --ram-state <RAM_STATE> Choose power-up RAM state. [default: "all-zeros"]
[possible values: all-zeros, all-ones, random]
-w, --emulate-ppu-warmup Whether to emulate PPU warmup where writes to
certain registers are ignored. Can result in
some games not working correctly
-r, --region <REGION> Choose default NES region. [default: "ntsc"]
[possible values: ntsc, pal, dendy]
-i, --save-slot <SAVE_SLOT> Save slot. [default: 1]
--no-load Don't load save state on start
--no-save Don't auto save state or save on exit
-x, --speed <SPEED> Emulation speed. [default: 1.0]
-g, --genie-code <GENIE_CODE> Add Game Genie Code(s). e.g. `AATOZE`
(Start Super Mario Bros. with 9 lives)
--config <CONFIG> Custom Config path
-c, --clean "Default Config" (skip user config and previous
save states)
-d, --debug Start with debugger open
-h, --help Print help
-V, --version Print version
```
[iNES][] and [NES 2.0][] formatted ROMS are supported, though some advanced `NES
2.0` features may not be implemented.
[ines]: https://wiki.nesdev.org/w/index.php/INES
[nes 2.0]: https://wiki.nesdev.org/w/index.php/NES_2.0
### Supported Mappers
Support for the following mappers is currently implemented or in development:
<!-- markdownlint-disable line-length -->
| # | Name | Example Games | # of Games<sup>1</sup> | % of Games<sup>1</sup> |
| --- | --------------------- | ------------------------------------------ | ---------------------- | ---------------------- |
| 000 | NROM | Bomberman, Donkey Kong, Super Mario Bros. | ~247 | ~10% |
| 001 | SxROM/MMC1B/C | Metroid, Legend of Zelda, Tetris | ~680 | ~28% |
| 002 | UxROM | Castlevania, Contra, Mega Man | ~270 | ~11% |
| 003 | CNROM | Arkanoid, Paperboy, Pipe Dream | ~155 | ~6% |
| 004 | TxROM/MMC3/MMC6 | Kirby's Adventure, Super Mario Bros. 2/3 | ~599 | ~24% |
| 005 | ExROM/MMC5 | Castlevania 3, Laser Invasion | ~24 | <0.01% |
| 007 | AxROM | Battletoads, Marble Madness | ~75 | ~3% |
| 009 | PxROM/MMC2 | Punch Out!! | 1 | <0.01% |
| 010 | FxROM/MMC4 | Fire Emblem Gaiden | 3 | <0.01% |
| 011 | Color Dreams | Crystal Mines, Metal Fighter | 15 | ~1% |
| 016 | Bandai FCG | Dragon Ball: Daimaou Fukkatsu | 14 | ~1% |
| 018 | Jaleco SS 88006 | Magic John | 15 | ~1% |
| 019 | Namco163 | Battle Fleet, Dragon Ninja | 20 | ~1% |
| 024 | VRC6a | Akumajou Densetsu | 1 | <0.01% |
| 026 | VRC6b | Madara, Esper Dream 2 | 2 | <0.01% |
| 034 | BNROM/NINA-001 | Deadly Towers, Impossible Mission II | 3 | <0.01% |
| 066 | GxROM/MxROM | Super Mario Bros. + Duck Hunt | ~17 | <0.01% |
| 069 | Sunsoft/FME-7 | Batman: Return of the Joker, Gimmick! | ~15 | <0.01% |
| 071 | Camerica/Codemasters | Firehawk, Bee 52, MiG 29 - Soviet Fighter | ~15 | <0.01% |
| 076 | DxROM/Namco 108 | Megami Tensei: Digital Devil Story | 1 | <0.01% |
| 079 | NINA-003/006 | Black Jack, Double Strike | 16 | <0.01% |
| 088 | DxROM/Namco 108 | Quinty, Dragon Spirit - Aratanaru Densetsu | 3 | <0.01% |
| 095 | DxROM/Namco 108 | Dragon Buster | 1 | <0.01% |
| 113 | NINA-003/006 | HES 6-in-1, Total Funpak | ~3 | <0.01% |
| 144 | Color Dreams | Death Race | 1 | <0.01% |
| 146 | NINA-003/006 | Galactic Crusader | 1 | <0.01% |
| 153 | Bandai FCG | Famicom Jump II: Saikyou no 7-nin | 1 | <0.01% |
| 154 | DxROM/Namco 108 | Devil Man | 1 | <0.01% |
| 157 | Bandai FCG/Datach | SD Gundam Wars | 7 | <0.01% |
| 155 | SxROM/MMC1A | Tatakae!! Ramen Man: Sakuretsu Choujin | 2 | <0.01% |
| 159 | Bandai FCG | Dragon Ball Z: Kyoushuu! Saiya-jin | 4 | <0.01% |
| 206 | DxROM/Namco 108 | Fantasy Zone, Gauntlet | 45 | ~2% |
| 210 | Namco175/340 | Dream Master, Family Circuit '91 | 4 | <0.01% |
| | | | ~2256 / 2447 | ~92.2% |
<!-- markdownlint-enable line-length -->
1. [Source](http://bootgod.dyndns.org:7777/stats.php?page=6) [Mirror](https://nescartdb.com/)
### Controls
Keybindings can be customized in the keybindings menu. Below are the defaults.
NES joypad:
| Button | Keyboard (Player 1) | Controller |
| --------- | ------------------- | ---------------- |
| A | Z | East |
| B | X | South |
| A (Turbo) | A | North |
| B (Turbo) | S | West |
| Select | Q | Select |
| Start | W | Start |
| D-Pad | Arrow Keys | D-Pad |
Controller Layout:
SDL-compatible mappings are used:
<https://github.com/mdqinc/SDL_GameControllerDB?tab=readme-ov-file> but can be
overriden by setting `SDL_GAMECONTROLLERCONFIG`.
```text
Left Triggers Right Triggers
_=====_ _=====_
/ _____ \ / _____ \
+.-'_____'-.---------------------------.-' '-.+
/ | | '. .' \
/ ___| /|\ |___ \ / (N) \
/ | | | ; _ _ ; ; Action Pad
D-Pad | | <--- ---> | | <:_| |_:> | (W) (E) | (South, East,
| |___ | ___| ; Select Start ; ; North, West)
|\ | \|/ | / _ _ \ (S) /|
| \ |_____| .','" "', ,'" "', '. .' |
| '-.______.-' / Left \------/ Right \ '-._____.-' |
| /\ Stick / \ Stick /\ |
| / '.___.' '.___.' \ |
| / \ |
\ / \ /
\________/ \_________/
```
Emulator shortcuts:
| Action | Keyboard | Controller |
| ----------------------------- | ------------ | -------------- |
| Pause | Escape | Guide Button |
| About TetaNES | F1 | |
| Preferences Menu | Ctrl-P or F2 | |
| Load/Open ROM | Ctrl-O or F3 | |
| Quit | Ctrl-Q | |
| Reset | Ctrl-R | |
| Power Cycle | Ctrl-H | |
| Increase Speed by 25% | = | Right Shoulder |
| Decrease Speed by 25% | - | Left Shoulder |
| Increase Emulation Scale | Shift-= | |
| Decrease Emulation Scale | Shift-- | |
| Increase UI Scale | Ctrl-= | |
| Decrease UI Scale | Ctrl-- | |
| Fast-Forward 2x | Space (Hold) | |
| Set Save State Slot (1-4) | Ctrl-(1-4) | |
| Save State | Ctrl-S | |
| Load State | Ctrl-L | |
| Instant Rewind | R (Tap) | |
| Visual Rewind | R (Hold) | |
| Take Screenshot | F10 | |
| Toggle Gameplay Recording | Shift-V | |
| Toggle Audio Recording | Shift-R | |
| Toggle Audio | Ctrl-M | |
| Toggle Pulse Channel 1 | Shift-1 | |
| Toggle Pulse Channel 2 | Shift-2 | |
| Toggle Triangle Channel | Shift-3 | |
| Toggle Noise Channel | Shift-4 | |
| Toggle DMC Channel | Shift-5 | |
| Toggle Mapper Channel | Shift-6 | |
| Toggle Fullscreen | Ctrl-Enter | |
| Toggle NTSC Filter | Ctrl-N | |
| Toggle CRT Shader | Ctrl-T | |
| Toggle CPU Debugger | Shift-D | |
| Toggle PPU Debugger | Shift-P | |
| Toggle APU Debugger | Shift-A | |
While the CPU Debugger is open:
| Action | Keyboard |
| ----------------------------- | -------- |
| Step a single CPU instruction | C |
| Step over a function | O |
| Step out of a function | Shift-O |
| Step a single scanline | Shift-L |
| Step an entire frame | Shift-F |
While the PPU Debugger is open:
| Action | Keyboard |
| ------------------------------ | --------------- |
| Move debug scanline up by 1 | Ctrl-Up |
| Move debug scanline up by 10 | Ctrl-Shift-Up |
| Move debug scanline down by 1 | Ctrl-Down |
| Move debug scanline down by 10 | Ctrl-Shift-Down |
Other mappings can be found and modified in the `Config -> Keybinds` menu.
### Directories
`TetaNES` saves files to disk to support a number of features and, depending on the
file type, varies based on operating system.
#### Preferences
- Linux: `$HOME/.config`
- macOS: `$HOME/Library/Application Support`
- Windows: `%LOCALAPPDATA%\tetanes`
- Web: localStorage (e.g. `config/config.json`)
#### Screenshots
- Linux, macOS, & Windows: `$HOME/Pictures`
- Web: Does not currently support saving screenshots.
#### Replay Recordings
- Linux, macOS, & Windows: `$HOME/Documents`
- Web: Does not currently support saving recordings.
#### Audio Recordings
- Linux, macOS, & Windows: `$HOME/Music`
- Web: Does not currently support saving recordings.
#### Battery-backed RAM, save states, and logs
- Linux: `$HOME/.local/share/tetanes`
- macOS: `$HOME/Library/Application Support/tetanes`
- Windows: `%LOCALAPPDATA%\tetanes`
- Web: localStorage (e.g. `data/save/AO Demo/slot-1.sav`)
### Powerup State
The original NES hardware had semi-random contents located in RAM upon power-up
and several games made use of this to seed their Random Number Generators
(RNGs). By default, `TetaNES` honors the original hardware and emulates
randomized powerup RAM state. This shows up in several games such as `Final
Fantasy`, `River City Ransom`, and `Impossible Mission II`, amongst others. Not
emulating this would make these games seem deterministic when they weren't
intended to be.
If you would like `TetaNES` to provide fully deterministic emulated power-up
state, you'll need to change the `RAM State` setting in the configuration menu
and trigger a power-cycle or use the `-m`/`--ram_state` flag from the command
line.
### Building/Running
To build/run `TetaNES`, you'll need a nightly version of the compiler and run
`cargo build` or `cargo build --release` (if you want better framerates).
To run the web version, you'll also need the `wasm32-unknown-unknown` target and
[trunk](https://trunkrs.dev/) installed:
```sh
rustup target add wasm32-unknown-unknown
trunk serve --release
```
Unit and integration tests can be run with `cargo test`. There are also several
test roms that can be run to test various capabilities of the emulator. They are
all located in the `tetanes-core/tests_roms/` directory.
Run them in a similar way you would run a game. e.g.
```sh
cargo run --release tetanes-core/test_roms/cpu/nestest.nes
```
#### Feature Flags
- **webgpu** - Enables the
[`BrowserWebGpu`](https://docs.rs/wgpu/latest/wgpu/enum.Backend.html) backend
for TetaNES Web. The default is to use `WebGl2` until WebGPU is stable across
all platforms and browsers. Currently pending Firefox and Chrome on Linux
(See: <https://caniuse.com/webgpu>).
### Troubleshooting
If you get an error running a ROM that's using the supported Mapper list above,
it could be a corrupted or incompatible ROM format. If you're unsure which games
use which mappers, see <http://bootgod.dyndns.org:7777/>. Trying other
versions of the same game from different sources sometimes resolves the issue.
If you get some other error when trying to start a game that previously
worked, try removing any configurations or save states from the
[Directories](#directories) listed above to ensure it's not an incompatible
savestate file causing the issue.
If you encounter any shortcuts not working, ensure your operating system does
not have a binding for it that is overriding it. macOS specifically has many
things bound to `Ctrl-*`.
If an an issue is not already created, please use the [github issue tracker][]
to create it.
## Roadmap
See [ROADMAP.md][].
## Known Issues
See the [github issue tracker][].
## Documentation
In addition to the wealth of information in the `docs/` directory, I also
referenced these websites extensively during development:
- [NES Documentation (PDF)](https://nesdev.org/NESDoc.pdf)
- [NES Dev Wiki](https://wiki.nesdev.org/w/index.php/Nesdev_Wiki)
- [6502 Datasheet](https://archive.6502.org/datasheets/rockwell_r650x_r651x.pdf)
## License
`TetaNES` is licensed under a MIT or Apache-2.0 license. See the `LICENSE-MIT`
or `LICENSE-APACHE` file in the root for a copy.
## Contribution
While this is primarily a personal project, I welcome any contributions or
suggestions. Feel free to submit a pull request if you want to help out!
### Contact
For issue reporting, please use the [github issue tracker][]. You can also
contact me directly at <https://lukeworks.tech/contact/>.
## Credits
Implementation was inspiried by several amazing NES projects, without which I
would not have been able to understand or digest all the information on the NES
wiki.
- [fogleman NES](https://github.com/fogleman/nes)
- [sprocketnes](https://github.com/pcwalton/sprocketnes)
- [nes-emulator](https://github.com/MichaelBurge/nes-emulator)
- [LaiNES](https://github.com/AndreaOrru/LaiNES)
- [ANESE](https://github.com/daniel5151/ANESE)
- [FCEUX](https://fceux.com/web/home.html)
I also couldn't have gotten this far without the amazing people over on the
[NES Dev Forums](https://forums.nesdev.org/):
- [blargg](https://forums.nesdev.org/memberlist.php?mode=viewprofile&u=17) for
all his amazing [test roms](https://wiki.nesdev.org/w/index.php/Emulator_tests)
- [bisqwit](https://bisqwit.iki.fi/) for his test roms & integer NTSC video
implementation
- [Disch](https://forums.nesdev.org/memberlist.php?mode=viewprofile&u=33)
- [Quietust](https://forums.nesdev.org/memberlist.php?mode=viewprofile&u=7)
- [rainwarrior](https://forums.nesdev.org/memberlist.php?mode=viewprofile&u=5165)
- And many others who helped me understand the stickier bits of emulation
Also, a huge shout out to
[OneLoneCoder](https://github.com/OneLoneCoder/) for his
[NES](https://github.com/OneLoneCoder/olcNES) and
[olcPixelGameEngine](https://github.com/OneLoneCoder/olcPixelGameEngine)
series as those helped a ton in some recent refactorings.
[rust]: https://www.rust-lang.org/
[wgpu]: https://wgpu.rs/
[web assembly]: https://webassembly.org/
[github issue tracker]: https://github.com/lukexor/tetanes/issues
[ROADMAP.md]: ROADMAP.md
[Release]: https://github.com/lukexor/tetanes/releases/latest
================================================
FILE: ROADMAP.md
================================================
# Roadmap
- NES Formats & Run Modes
- [x] NTSC
- [x] PAL
- [x] Dendy
- [x] Headless mode
- Central Processing Unit (CPU)
- [x] Official Instructions
- [x] Unofficial Instructions
- [x] Cycle Accurate
- Picture Processing Unit (PPU)
- [x] Pixellate Filter
- [x] NTSC Filter
- [x] CRT Filter
- Audio Processing Unit (APU)
- [x] Pulse Channels
- [x] Triangle Channel
- [x] Noise Channel
- [x] Delta Modulation Channel (DMC)
- Player Input
- [x] 1-2 Player w/ Keyboard or Controllers
- [x] 3-4 Player Support w/ Controllers
- [x] Zapper (Light Gun)
- Cartridge
- [x] iNES Format
- [x] NES 2.0 Format
- [ ] Complete NES 2.0 support
- Mappers
- [x] Mapper 000 - NROM
- [x] Mapper 001 - SxROM/MMC1B/C
- [x] Mapper 002 - UxROM
- [x] Mapper 003 - CNROM
- [x] Mapper 004 - TxROM/MMC3/MMC6
- [x] Mapper 005 - ExROM/MMC5
- [x] Mapper 007 - AxROM
- [x] Mapper 009 - PxROM/MMC2
- [x] Mapper 010 - FxROM/MMC4
- [x] Mapper 011 - Color Dreams
- [x] Mapper 019 - Namco 163
- [ ] Mapper 023 - VRC2b/VRC4e
- [ ] Mapper 025 - VRC4b/VRC4d
- [x] Mapper 024 - VRC6a
- [x] Mapper 026 - VRC6b
- [x] Mapper 034 - BNROM/NINA-001
- [ ] Mapper 064 - RAMBO-1
- [x] Mapper 066 - GxROM/MxROM
- [ ] Mapper 068 - After Burner
- [x] Mapper 069 - FME-7/Sunsoft 5B
- [x] Mapper 071 - Camerica/Codemasters/BF909x
- [x] Mapper 079 - NINA-03/NINA-06
- [x] Mapper 155 - SxROM/MMC1A
- [x] Mapper 206 - DxROM/Namco 118/MIMIC-1
- Releases
- [x] macOS Binaries
- [x] Linux Binaries
- [x] Windows Binaries
- [x] User Interface (UI)
- [x] WebAssembly (WASM) - Run TetaNES in the browser!
- [x] Configurable keybinds and default settings
- Menus
- [x] Configuration options
- [x] Customize Keybinds & Controllers
- [x] Load/Open ROM with file browser
- [x] Recent Game Selection
- [x] About Menu
- [ ] Config paths overrides
- [x] Increase/Decrease Speed
- [x] Fast-forward
- [x] Instant Rewind (2 seconds)
- [x] Visual Rewind (Holding R will time-travel backward)
- [x] Save/Load State
- [x] Auto-save
- [x] Take Screenshots
- [x] Gameplay Recording
- [x] Sound Recording (Save those memorable tunes!)
- [x] Toggle Fullscreen
- [x] Toggle Sound
- [x] Toggle individual sound channels
- [x] Toggle FPS
- [x] Toggle Messages
- [x] Change Video Filter
- Game Genie Support
- [x] Command-Line
- [ ] UI Menu
- [ ] [WideNES](https://prilik.com/ANESE/wideNES)
- [ ] Network Multi-player
- [ ] Self Updater
- [x] Drag and drop load ROMs
- Testing/Debugging/Documentation
- [x] Debugger (Displays CPU/PPU status, registers, and disassembly)
- [x] Step Into/Out/Over
- [x] Step Scanline/Frame
- [ ] Breakpoints
- [ ] Modify state
- [ ] Labels
- [ ] Hex Memory Editor & Debugger
- PPU Viewer
- [ ] Scanline Hit Configuration (For debugging IRQ Nametable changes)
- [x] Nametable Viewer (background rendering)
- [x] CHR Viewer (sprite tiles)
- [x] OAM Viewer (on screen sprites)
- [x] Palette Viewer
- [ ] APU Viewer (Displays audio status and registers)
- [x] Automated ROM tests (including [nestest](https://www.qmtpro.com/~nes/misc/nestest.txt))
- [ ] Detailed Documentation
- Logging
- [x] Environment logging
- [x] File logging
================================================
FILE: assets/linux/tetanes.desktop
================================================
[Desktop Entry]
Name=TetaNES
Exec=tetanes
Icon=icon
Type=Application
Categories=Game;
================================================
FILE: assets/macos/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleIdentifier</key>
<string>tech.lukeworks.tetanes</string>
<key>CFBundleName</key>
<string>tetanes</string>
<key>CFBundleDisplayName</key>
<string>TetaNES</string>
<key>CFBundleExecutable</key>
<string>tetanes</string>
<key>CFBundleIconFile</key>
<string>Icon</string>
<key>CFBundleVersion</key>
<string>%VERSION%</string>
</dict>
</plist>
================================================
FILE: cliff.toml
================================================
[changelog]
header = """
<!-- markdownlint-disable-file no-duplicate-heading -->
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n
"""
body = """
{% if version %}\
{% if previous.version %}\
## [{{ version | trim_start_matches(pat="v") }}](<REPO>/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% endif %}\
{% else %}\
## [unreleased]
{% endif %}\
{% macro commit(commit) -%}
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}{% if commit.breaking %}[**breaking**] {% endif %}\
{{ commit.message | upper_first }} - ([{{ commit.id | truncate(length=7, end="") }}](<REPO>/commit/{{ commit.id }}))\
{% endmacro -%}
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits
| filter(attribute="scope")
| sort(attribute="scope") %}
{{ self::commit(commit=commit) }}\
{% endfor %}
{% raw %}\n{% endraw %}\
{%- for commit in commits %}
{%- if not commit.scope -%}
{{ self::commit(commit=commit) }}
{% endif -%}
{% endfor -%}
{% endfor %}\n
"""
trim = true
footer = ""
postprocessors = [
{ pattern = '<REPO>', replace = "https://github.com/lukexor/tetanes" },
]
[git]
conventional_commits = true
filter_unconventional = true
split_commits = false
commit_preprocessors = [
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))" },
]
commit_parsers = [
{ message = "^feat", group = "<!-- 0 -->⛰️ Features" },
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
{ message = "^chore\\(release\\): prepare for", skip = true },
{ message = "^chore\\(deps\\)", skip = true },
{ message = "^build\\(deps\\)", skip = true },
{ message = "^chore\\(pr\\)", skip = true },
{ message = "^chore\\(pull\\)", skip = true },
{ message = "^chore|ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
]
protect_breaking_commits = false
filter_commits = true
tag_pattern = "v[0-9].*"
skip_tags = "beta|alpha"
ignore_tags = "rc"
topo_order = false
sort_commits = "newest"
================================================
FILE: deny.toml
================================================
[graph]
all-features = true
no-default-features = false
[output]
feature-depth = 1
[advisories]
version = 2
db-path = "~/.cargo/advisory-db"
db-urls = ["https://github.com/rustsec/advisory-db"]
yanked = "warn"
ignore = [
{ id = "RUSTSEC-2024-0436", reason = "paste is a downstream dependency of many crates with no upgrade path" },
{ id = "RUSTSEC-2025-0141", reason = "need to plan an upgrade path from bincode to rkyv" },
]
[licenses]
version = 2
allow = [
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
"Apache-2.0 WITH LLVM-exception", # https://spdx.org/licenses/LLVM-exception.html
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
"BSL-1.0", # https://tldrlegal.com/license/boost-software-license-1.0-explained
"CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/
"CDLA-Permissive-2.0", # https://cdla.dev/permissive-2-0/. Used by webpki-roots on Linux.
"ISC", # https://www.tldrlegal.com/license/isc-license
"MIT", # https://tldrlegal.com/license/mit-license
"MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/FAQ/ - see Q11.
"OFL-1.1", # https://spdx.org/licenses/OFL-1.1.html
"OpenSSL", # https://openssl-library.org/source/license/index.html
"Ubuntu-font-1.0", # https://ubuntu.com/legal/font-licence
"Unicode-3.0", # https://spdx.org/licenses/Unicode-3.0.html
"Zlib", # https://tldrlegal.com/license/zlib-libpng-license-(zlib)
]
confidence-threshold = 0.8
exceptions = []
[licenses.private]
ignore = false
registries = []
[bans]
multiple-versions = "allow"
wildcards = "deny"
highlight = "all"
workspace-default-features = "allow"
external-default-features = "allow"
allow = []
deny = []
skip = []
skip-tree = []
[sources]
unknown-registry = "deny"
unknown-git = "deny"
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
allow-git = []
[sources.allow-org]
github = []
gitlab = []
bitbucket = []
================================================
FILE: docs/apu/apu_ref.txt
================================================
NES APU Sound Hardware Reference
--------------------------------
This reference covers Nintendo Entertainment System (NES) sound hardware in as
much detail as I know. It is intended primarily to assist in the implementation
of emulators and might also be useful as a programmer reference.
Tables, diagrams, and formulas are formatted for a mono-spaced font, like
Courier.
The latest version is kept at http://www.slack.net/~ant/nes-emu/apu_ref.txt
-----
Intro
-----
This reference is based on the results of tests I have run on a 1988-model US
NTSC NES which contains the G revision of the 2A03 CPU/APU and the NES-CPU-07
version of the main board. PAL hardware will be covered once tests are
performed on it. Feel free to incorporate this information in references and
other documentation.
While implementing a NES sound emulator, even after reading the available
documentation I still had many unanswered questions, so I made a simple
development cartridge to test on a real NES. This was very successful and
revealed many new details. My notes consisted of differences from existing
documentation, but this didn't seem to be a very reliable way to release my
findings, so I decided to write a concise reference.
For those familiar with NESSOUND.TXT and DMC.TXT, the following differences
should be specifically noted:
Corrections:
- DMC table entry $D should be $2A0 instead of $2A8
- Frame sequencer
- Square's duty generator
Clarifications:
- DMC
- Triangle's linear counter
- Length Counter operation and status register behavior
It should go without saying that the model presented here probably doesn't
match the actual logic gate arrangement in the NES. It makes no difference how
the hardware is implemented, as long as its behavior matches what is described
here
Corrections, questions and additions are welcome. I keep up with the forum at
http://nesdev.parodius.com/ and can be contacted via blargg at the mail.com
domain.
-----
To Do
-----
- See if comprehensive emulator test ROM is even practical.
- Probe complete power-up state and reset state.
- Test PAL hardware to determine APU frame rate, DMC and noise period tables.
- Check complete behavior of each unit of each channel to be sure common units
behave the same for all channels.
- Double-check details on NES hardware again.
- Determine post-DAC filtering done before output.
--------
Overview
--------
The APU is composed of five channels: square 1, square 2, triangle, noise,
delta modulation channel (DMC). Each has a variable-rate timer clocking a
waveform generator, and various modulators driven by low-frequency clocks from
a frame sequencer. The DMC plays samples while the other channels play
waveforms. The waveform channels have duration control, some have a volume
envelope unit, and a couple have a frequency sweep unit.
Square 1/Square 2
$4000/4 ddle nnnn duty, loop env/disable length, env disable, vol/env
period
$4001/5 eppp nsss enable sweep, period, negative, shift
$4002/6 pppp pppp period low
$4003/7 llll lppp length index, period high
Triangle
$4008 clll llll control, linear counter load
$400A pppp pppp period low
$400B llll lppp length index, period high
Noise
$400C --le nnnn loop env/disable length, env disable, vol/env period
$400E s--- pppp short mode, period index
$400F llll l--- length index
DMC
$4010 il-- ffff IRQ enable, loop, frequency index
$4011 -ddd dddd DAC
$4012 aaaa aaaa sample address
$4013 llll llll sample length
Common
$4015 ---d nt21 length ctr enable: DMC, noise, triangle, pulse 2, 1
$4017 fd-- ---- 5-frame cycle, disable frame interrupt
Status (read)
$4015 if-d nt21 DMC IRQ, frame IRQ, length counter statuses
------
Basics
------
Hexadecimal values are prefixed by a $ except for some single-hex-digit
sequences where it's clear that they are hex. Bits are numbered from 0 to 7,
corresponding with the least to most significant bits of a byte; bit n has a
binary weight of 2^n.
A flag is a two-state variable that can be either set or clear. When
implemented in a bit, clear = 0 and set = 1.
A divider outputs a clock every n input clocks, where n is the divider's
period. It contains a counter which is decremented on the arrival of each
clock. When it reaches 0, it is reloaded with the period and an output clock is
generated. Resetting a divider reloads its counter without generating an output
clock. Changing a divider's period doesn't affect its current count.
A sequencer generates a series of values or events based on the repetition of a
series of steps, starting with the first. When clocked the next step of the
sequence is generated.
In the block diagrams, the triangular symbol is a control gate; if control is
non-zero, the input is passed unchanged to the output, otherwise the output is
0.
control
|
v
|\
in -->| >-- out
|/
Except for the status register, all other registers are write-only. The "value
of the register" refers to the last value written to the register.
The NTSC NES has a master clock based on a 21.47727 MHz crystal which is
divided by 12 to obtain a ~1.79 MHz clock source. Both clocks are used by the
APU.
The CPU's IRQ line is level-sensitive, so the APU's interrupt flags must be
cleared once a CPU IRQ is acknowledged, otherwise the CPU will immediately be
interrupt again once its inhibit flag is cleared.
In general, the APU is a collection of many independent units which are always
running in parallel. Modification of a channel's parameter usually affects only
one sub-unit and doesn't take effect until that unit's next internal cycle
begins.
Each section begins with an overview and an optional block diagram, which
provide a framework for the information that follows. In order to reduce
ambiguity, there is very little re-statement of information.
---------------
Frame Sequencer
---------------
The frame sequencer contains a divider and a sequencer which clocks various
units.
The divider generates an output clock rate of just under 240 Hz, and appears to
be derived by dividing the 21.47727 MHz system clock by 89490. The sequencer is
clocked by the divider's output.
On a write to $4017, the divider and sequencer are reset, then the sequencer is
configured. Two sequences are available, and frame IRQ generation can be
disabled.
mi-- ---- mode, IRQ disable
If the mode flag is clear, the 4-step sequence is selected, otherwise the
5-step sequence is selected and the sequencer is immediately clocked once.
f = set interrupt flag
l = clock length counters and sweep units
e = clock envelopes and triangle's linear counter
mode 0: 4-step effective rate (approx)
---------------------------------------
- - - f 60 Hz
- l - l 120 Hz
e e e e 240 Hz
mode 1: 5-step effective rate (approx)
---------------------------------------
- - - - - (interrupt flag never set)
l - l - - 96 Hz
e e e e - 192 Hz
At any time if the interrupt flag is set and the IRQ disable is clear, the
CPU's IRQ line is asserted.
--------------
Length Counter
--------------
A length counter allows automatic duration control. Counting can be halted and
the counter can be disabled by clearing the appropriate bit in the status
register, which immediately sets the counter to 0 and keeps it there.
The halt flag is in the channel's first register. For the square and noise
channels, it is bit 5, and for the triangle, bit 7:
--h- ---- halt (noise and square channels)
h--- ---- halt (triangle channel)
Note that the bit position for the halt flag is also mapped to another flag in
the Length Counter (noise and square) or Linear Counter (triangle).
Unless disabled, a write the channel's fourth register immediately reloads the
counter with the value from a lookup table, based on the index formed by the
upper 5 bits:
iiii i--- length index
bits bit 3
7-4 0 1
-------
0 $0A $FE
1 $14 $02
2 $28 $04
3 $50 $06
4 $A0 $08
5 $3C $0A
6 $0E $0C
7 $1A $0E
8 $0C $10
9 $18 $12
A $30 $14
B $60 $16
C $C0 $18
D $48 $1A
E $10 $1C
F $20 $1E
See the clarifications section for a possible explanation for the values left
column of the table.
When clocked by the frame sequencer, if the halt flag is clear and the counter
is non-zero, it is decremented.
---------------
Status Register
---------------
The status register at $4015 allows control and query of the channels' length
counters, and query of the DMC and frame interrupts. It is the only register
which can also be read.
When $4015 is read, the status of the channels' length counters and bytes
remaining in the current DMC sample, and interrupt flags are returned.
Afterwards the Frame Sequencer's frame interrupt flag is cleared.
if-d nt21
IRQ from DMC
frame interrupt
DMC sample bytes remaining > 0
triangle length counter > 0
square 2 length counter > 0
square 1 length counter > 0
When $4015 is written to, the channels' length counter enable flags are set,
the DMC is possibly started or stopped, and the DMC's IRQ occurred flag is
cleared.
---d nt21 DMC, noise, triangle, square 2, square 1
If d is set and the DMC's DMA reader has no more sample bytes to fetch, the DMC
sample is restarted. If d is clear then the DMA reader's sample bytes remaining
is set to 0.
------------------
Envelope Generator
------------------
An envelope generator can generate a constant volume or a saw envelope with
optional looping. It contains a divider and a counter.
A channel's first register controls the envelope:
--ld nnnn loop, disable, n
Note that the bit position for the loop flag is also mapped to a flag in the
Length Counter.
The divider's period is set to n + 1.
When clocked by the frame sequencer, one of two actions occurs: if there was a
write to the fourth channel register since the last clock, the counter is set
to 15 and the divider is reset, otherwise the divider is clocked.
When the divider outputs a clock, one of two actions occurs: if loop is set and
counter is zero, it is set to 15, otherwise if counter is non-zero, it is
decremented.
When disable is set, the channel's volume is n, otherwise it is the value in
the counter. Unless overridden by some other condition, the channel's DAC
receives the channel's volume value.
-----
Timer
-----
All channels use a timer which is a divider driven by the ~1.79 MHz clock.
The noise channel and DMC use lookup tables to set the timer's period. For the
square and triangle channels, the third and fourth registers form an 11-bit
value and the divider's period is set to this value *plus one*.
llll llll low 8 bits of period (third register)
---- -hhh upper 3 bits of period (fourth register)
----------
Sweep Unit
----------
The sweep unit can adjust a square channel's period periodically. It contains a
divider and a shifter.
A channel's second register configures the sweep unit:
eppp nsss enable, period, negate, shift
The divider's period is set to p + 1.
The shifter continuously calculates a result based on the channel's period. The
channel's period (from the third and fourth registers) is first shifted right
by s bits. If negate is set, the shifted value's bits are inverted, and on the
second square channel, the inverted value is incremented by 1. The resulting
value is added with the channel's current period, yielding the final result.
When the sweep unit is clocked, the divider is *first* clocked and then if
there was a write to the sweep register since the last sweep clock, the divider
is reset.
When the channel's period is less than 8 or the result of the shifter is
greater than $7FF, the channel's DAC receives 0 and the sweep unit doesn't
change the channel's period. Otherwise, if the sweep unit is enabled and the
shift count is greater than 0, when the divider outputs a clock, the channel's
period in the third and fourth registers are updated with the result of the
shifter.
--------------
Square Channel
--------------
+---------+ +---------+
| Sweep |--->|Timer / 2|
+---------+ +---------+
| |
| v
| +---------+ +---------+
| |Sequencer| | Length |
| +---------+ +---------+
| | |
v v v
+---------+ |\ |\ |\ +---------+
|Envelope |------->| >----------->| >----------->| >-------->| DAC |
+---------+ |/ |/ |/ +---------+
There are two square channels beginning at registers $4000 and $4004. Each
contains the following: Envelope Generator, Sweep Unit, Timer with
divide-by-two on the output, 8-step sequencer, Length Counter.
$4000/$4004: duty, envelope
$4001/$4005: sweep unit
$4002/$4006: period low
$4003/$4007: reload length counter, period high
In addition to the envelope, the first register controls the duty cycle of the
square wave, without resetting the position of the sequencer:
dd-- ---- duty cycle select
d waveform sequence
---------------------
_ 1
0 - ------ 0 (12.5%)
__ 1
1 - ----- 0 (25%)
____ 1
2 - --- 0 (50%)
_ _____ 1
3 -- 0 (25% negated)
When the fourth register is written to, the sequencer is restarted.
The sequencer is clocked by the divided timer output.
When the sequencer output is low, the DAC receives 0.
--------------
Linear Counter
--------------
The Linear Counter serves as a second more-accurate duration counter for the
triangle channel. It contains a counter and an internal halt flag.
Register $4008 contains a control flag and reload value:
crrr rrrr control flag, reload value
Note that the bit position for the control flag is also mapped to a flag in the
Length Counter.
When register $400B is written to, the halt flag is set.
When clocked by the frame sequencer, the following actions occur in order:
1) If halt flag is set, set counter to reload value, otherwise if counter
is non-zero, decrement it.
2) If control flag is clear, clear halt flag.
----------------
Triangle Channel
----------------
+---------+ +---------+
|LinearCtr| | Length |
+---------+ +---------+
| |
v v
+---------+ |\ |\ +---------+ +---------+
| Timer |------->| >----------->| >------->|Sequencer|--->| DAC |
+---------+ |/ |/ +---------+ +---------+
The triangle channel contains the following: Timer, 32-step sequencer, Length
Counter, Linear Counter, 4-bit DAC.
$4008: length counter disable, linear counter
$400A: period low
$400B: length counter reload, period high
When the timer generates a clock and the Length Counter and Linear Counter both
have a non-zero count, the sequencer is clocked.
The sequencer feeds the following repeating 32-step sequence to the DAC:
F E D C B A 9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9 A B C D E F
At the lowest two periods ($400B = 0 and $400A = 0 or 1), the resulting
frequency is so high that the DAC effectively outputs a value half way between
7 and 8.
-------------
Noise Channel
-------------
+---------+ +---------+ +---------+
| Timer |--->| Random | | Length |
+---------+ +---------+ +---------+
| |
v v
+---------+ |\ |\ +---------+
|Envelope |------->| >----------->| >------->| DAC |
+---------+ |/ |/ +---------+
The noise channel starts at register $400C and contains the following: Length
Counter, Envelope Generator, Timer, 15-bit right shift register with feedback,
4-bit DAC.
$400C: envelope
$400E: mode, period
$400F: reload length counter
Register $400E sets the random generator mode and timer period based on a 4-bit
index into a period table:
m--- iiii mode, period index
i timer period
----------------
0 $004
1 $008
2 $010
3 $020
4 $040
5 $060
6 $080
7 $0A0
8 $0CA
9 $0FE
A $17C
B $1FC
C $2FA
D $3F8
E $7F2
F $FE4
The shift register is clocked by the timer and the vacated bit 14 is filled
with the exclusive-OR of *pre-shifted* bits 0 and 1 (mode = 0) or bits 0 and 6
(mode = 1), resulting in 32767-bit and 93-bit sequences, respectively.
When bit 0 of the shift register is set, the DAC receives 0.
On power-up, the shift register is loaded with the value 1.
------------------------------
Delta Modulation Channel (DMC)
------------------------------
+----------+ +---------+
|DMA Reader| | Timer |
+----------+ +---------+
| |
| v
+----------+ +---------+ +---------+ +---------+
| Buffer |----| Output |---->| Counter |---->| DAC |
+----------+ +---------+ +---------+ +---------+
The DMC can output samples composed of 1-bit deltas and its DAC can be directly
changed. It contains the following: DMA reader, interrupt flag, sample buffer,
Timer, output unit, 7-bit counter tied to 7-bit DAC.
$4010: mode, frequency
$4011: DAC
$4012: address
$4013: length
On power-up, the DAC counter contains 0.
Register $4010 sets the interrupt enable, loop, and timer period. If the new
interrupt enabled status is clear, the interrupt flag is cleared.
il-- ffff interrupt enabled, loop, frequency index
f period
----------
0 $1AC
1 $17C
2 $154
3 $140
4 $11E
5 $0FE
6 $0E2
7 $0D6
8 $0BE
9 $0A0
A $08E
B $080
C $06A
D $054
E $048
F $036
A write to $4011 sets the counter and DAC to a new value:
-ddd dddd new DAC value
Sample Buffer
-------------
The sample buffer either holds a single sample byte or is empty. It is filled
by the DMA reader and can only be emptied by the output unit, so once loaded
with a sample it will be eventually output.
DMA Reader
----------
The DMA reader fills the sample buffer with successive bytes from the current
sample, whenever it becomes empty. It has an address counter and a bytes remain
counter.
When the DMC sample is restarted, the address counter is set to register $4012
* $40 + $C000 and the bytes counter is set to register $4013 * $10 + 1.
When the sample buffer is in an empty state and the bytes counter is non-zero,
the following occur: The sample buffer is filled with the next sample byte read
from memory at the current address, subject to whatever mapping hardware is
present (the same as CPU memory accesses). The address is incremented; if it
exceeds $FFFF, it is wrapped around to $8000. The bytes counter is decremented;
if it becomes zero and the loop flag is set, the sample is restarted (see
above), otherwise if the bytes counter becomes zero and the interrupt enabled
flag is set, the interrupt flag is set.
When the DMA reader accesses a byte of memory, the CPU is suspended for 4 clock
cycles.
Output Unit
-----------
The output unit continually outputs complete sample bytes or silences of equal
duration. It contains an 8-bit right shift register, a counter, and a silence
flag.
When an output cycle is started, the counter is loaded with 8 and if the sample
buffer is empty, the silence flag is set, otherwise the silence flag is cleared
and the sample buffer is emptied into the shift register.
On the arrival of a clock from the timer, the following actions occur in order:
1. If the silence flag is clear, bit 0 of the shift register is applied to
the DAC counter: If bit 0 is clear and the counter is greater than 1, the
counter is decremented by 2, otherwise if bit 0 is set and the counter is less
than 126, the counter is incremented by 2.
1) The shift register is clocked.
2) The counter is decremented. If it becomes zero, a new cycle is started.
----------
DAC Output
----------
The DACs for each channel are implemented in a way that causes non-linearity
and interaction between channels, so calculation of the resulting amplitude is
somewhat involved.
The normalized audio output level is the sum of two groups of channels:
output = square_out + tnd_out
95.88
square_out = -----------------------
8128
----------------- + 100
square1 + square2
159.79
tnd_out = ------------------------------
1
------------------------ + 100
triangle noise dmc
-------- + ----- + -----
8227 12241 22638
where triangle, noise, dmc, square1 and square2 are the values fed to their
DACs. The dmc ranges from 0 to 127 and the others range from 0 to 15. When the
sub-denominator of a group is zero, its output is 0. The output ranges from 0.0
to 1.0.
Implementation Using Lookup Table
---------------------------------
The formulas can be efficiently implemented using two lookup tables: a 31-entry
table for the two square channels and a 203-entry table for the remaining
channels (due to the approximation of tnd_out, the numerators are adjusted
slightly to preserve the normalized output range).
square_table [n] = 95.52 / (8128.0 / n + 100)
square_out = square_table [square1 + square2]
The latter table is approximated (within 4%) by using a base unit close to the
DMC's DAC.
tnd_table [n] = 163.67 / (24329.0 / n + 100)
tnd_out = tnd_table [3 * triangle + 2 * noise + dmc]
Linear Approximation
--------------------
A linear approximation can also be used, which results in slightly louder DMC
samples, but otherwise fairly accurate operation since the wave channels use a
small portion of the transfer curve. The overall volume will be reduced due to
the headroom required by the DMC approximation.
square_out = 0.00752 * (square1 + square2)
tnd_out = 0.00851 * triangle + 0.00494 * noise + 0.00335 * dmc
This linear approximation neglects the attenuating effect the DMC has when its
DAC is in the upper level. This factor can be calculated using the main formula
to form a ratio, and precalculated into a 128-entry lookup table.
tnd_out(triangle=15,dmc=d) - tnd_out(triangle=0,dmc=d)
attenuation(d) = ------------------------------------------------------
tnd_out(triangle=15,dmc=0)
-------------------
Unreliable Behavior
-------------------
(The following behaviors probably don't need to be emulated due to their
unreliability since stable code will avoid invoking it, and since their
behavior is somewhat difficult to precisely predict.)
If the frame IRQ is set just as register $4015 is being read, it seems to be
ignored (similar to polling $2002 for the vbl flag).
The DMC's DMA reader seems to check for an empty buffer every few CPU cycles,
rather than every cycle or continuously.
Writing to the DAC register ($4011) while a sample is playing sometimes has no
effect, probably because the DMC's output unit is clocking the counter at the
same moment as the write.
--------------
Clarifications
--------------
(The following are meant only as re-statements of the main content, rather than
additions of new content.)
Because the envelope loop and length counter disable flags are mapped to the
same bit, the length counter can't be used while the envelope is in loop mode.
Similar applies to the triangle channel, where the linear counter and length
counter are both controlled by the same bit in register $4008.
Unlike the other waveform channels, the triangle channel is silenced by
stopping its waveform at whatever phase it's at, rather than causing zero to be
sent to its DAC.
The length counter table seems to be set up for standard note durations for 4/4
time at 160 bpm and 180 bpm. If bit 3 is 0, the following results (Dn is bit n
of the fourth channel register):
180bpm 160bpm
D6-D4 D7=0 D7=1 note
-------------------------------
$00 10 12 16th
$01 20 24 8th
$02 40 48 4th (one beat)
$03 80 96 half
$04 160 192 whole
$05 60 72 4th dotted
$06 14 16 8th triplet (*3 = a 4th)
$07 26 32 4th triplet (*3 = a half)
-------------
Collaborators
-------------
Brad Taylor's NESSOUND.TXT and DMC.TXT as a starting point for testing.
NTSC NES for testing on.
Nesdev forum for feedback.
xodnizel for testing results, correction to DMC table, feedback.
Bloopaws/Draci for feedback, possible explanation of length counter table
values.
-------
History
-------
2003.12.01
Made development cartridge and started testing on NES hardware.
2003.12.14
Started project.
2003.12.20
A few draft sections were posted to Nesdev or e-mailed privately.
2004.01.02
Draft version posted to Nesdev. Corrected tnd_table formula.
2004.01.02
Corrected incorrect "correction" to tnd_table formula. Double-checked them.
2004.01.03
Corrected envelope flag name to "disable" (it was named "enable").
Added effective frequencies of frame sequencer outputs.
Added Overview, Unreliable Behavior, Clarifications, and Collaborators
sections.
2004.01.04
Adjusted linear approximation (difficult to find a compromise).
A few minor edits.
2004.01.30
First release. Probably won't be doing much with it for a while.
================================================
FILE: docs/apu/audio_psuedo_code.txt
================================================
LENGTH_TABLE // used by $4003 WRITE Pulse1/2
APU
sequencer_mode // $4017 WRITE D7, clock_frame_sequencer()
sequencer_phase // $4017 WRITE 0, clock_frame_sequencer()
sequencer_counter // $4017 WRITE clocks_to_next_phase(), clock_frame_sequencer()
irq_pending // $4017 WRITE if !irq_enabled false, clock_frame_sequencer()
// $4015 READ irq_pending = false
irq_enabled // $4017 WRITE !D6, clock_frame_sequencer()
clock() // Clocked every CPU cycle
clock_frame_sequencer()
clocks_to_next_phase() // used in $4017 WRITE
clock_quarter_frame() // used in $4017 WRITE if sequencer_mode
clock_half_frame() // used in $4017 WRITE if sequencer_mode
sample() //
Pulse1/2
DUTY_TABLE // used by duty_cycle/duty_counter
enabled // $4015 WRITE D0
duty_cycle // $4000 WRITE D7..D6, clock(), output()
duty_counter // $4003 WRITE 0, output()
freq_timer // $4002 WRITE D7..D0, $4003 D2..D0 << 8, clock_half_frame()
// sweep_forced_silent()
freq_counter // $4003 WRITE freq_timer, clock()
length_enabled // $4000 WRITE !D5, clock_half_frame()
length_counter // $4003 WRITE if enabled LENGTH_TABLE[ D7..D3 ], output()
// $4015 WRITE if !enabled 0, clock_half_frame()
// $4015 READ
decay_enabled // $4000 WRITE !D4, output()
decay_loop // $4000 WRITE D5, clock_quarter_frame()
decay_reset // $4003 WRITE true, clock_quarter_frame()
decay_volume // $4000 WRITE D3..D0, output(), clock_quarter_frame()
decay_constant_volume // output(), clock_quarter_frame()
decay_counter // clock_quarter_frame()
sweep_enabled // $4001 WRITE D7 && sweep_shift != 0, clock_half_frame()
sweep_reload // $4001 WRITE true, clock_half_frame()
sweep_timer // $4001 WRITE D6..D4, clock_half_frame()
sweep_counter // clock_half_frame()
sweep_negate // $4001 WRITE D3, clock_half_frame(), sweep_forced_silent()
sweep_shift // $4001 WRITE D2..D0, clock_half_frame(), sweep_forced_silent()
clock() // Clocked every APU cycle (CPU Cycle % 2 == 0)
clock_quarter_frame() //
clock_half_frame() //
output() //
sweep_forced_silent() // output(), clock_half_frame()
Triangle
enabled //
ultrasonic //
step //
freq_timer //
freq_counter //
length_enabled //
length_counter //
linear_control //
linear_load //
linear_reload //
clock() //
clock_quarter_frame() //
clock_half_frame() //
output() //
Noise
FREQ_TABLE //
enabled //
freq_timer //
freq_counter //
shift // u16: default to 1
shift_mode //
length_counter //
decay_enabled //
decay_reset //
decay_loop //
decay_volume //
decay_constant_volume //
decay_counter //
clock() //
clock_quarter_frame() //
clock_half_frame() //
output() //
DMC
addr //
addr_load //
length //
length_load //
irq_pending //
loops //
sample_buffer //
output //
output_bits //
output_shift //
output_silent //
freq_timer //
freq_counter //
clock() //
output() //
========================================================
$4000 write:
duty_table = dutytables[ v.76 ] // duty_cycle
length_enabled = !v.5 // length_counter.enabled
// envelope
decay_loop = v.5 // looping
decay_enabled = !v.4 // enabled
decay_V = v.3210 // volume
========================================================
$4001 write:
sweep_timer = v.654 // divider_period
sweep_negate = v.3
sweep_shift = v.210
sweep_reload = true
sweep_enabled = v.7 && sweep_shift != 0
========================================================
$4002 write:
freq_timer = v (low 8 bits) // timer_period
========================================================
$4003 write:
freq_timer = v.210 (high 3 bits)
if( channel_enabled )
length_counter = lengthtable[ v.76543 ]
; phase is also reset here (important for games like SMB)
freq_counter = freq_timer // timer
duty_counter = 0 // sequencer_step
; decay is also flagged for reset here
decay_reset_flag = true // envelope start
========================================================
$4015 write:
channel_enabled = v.0
if( !channel_enabled )
length_counter = 0
; ... other channels and DMC here ...
========================================================
$4017 write:
sequencer_mode = v.7 ; switch between 5-step (1) and 4-step (0) mode
irq_enabled = !v.6
next_seq_phase = 0
sequencer_counter = ClocksToNextSequence()
; see: http://wiki.nesdev.com/w/index.php/APU_Frame_Counter
; for example, this will be 3728.5 APU cycles, or 7457 CPU cycles.
; It might be easier to work in CPU cycles so you don't have to deal with
; half cycles.
if(sequencer_mode)
{
Clock_QuarterFrame() ; see below
Clock_HalfFrame()
}
if(!irq_enabled)
irq_pending = false ; acknowledge Frame IRQ
========================================================
$4015 read:
output = 0
if( length_counter != 0 ) output |= 0x01
; ... other channels length counters here
if( irq_pending )
output |= 0x40
; ... DMC IRQ state read back here
irq_pending = false ; IRQ acknowledged on $4015 read
return output
========================================================
Every APU Cycle:
;;;;;;;;;;;;;;;;;;;;;;;;;;;
; clock pulse wave
if( freq_counter > 0 ) // timer
--freq_counter
else
{
freq_counter = freq_timer
duty_counter = (duty_counter + 1) & 7
}
; ... clock other channels here
;;;;;;;;;;;;;;;;;;;;;;;;;;;
; clock frame sequencer
if( sequencer_counter > 0 )
--sequencer_counter
else
{
; see http://wiki.nesdev.com/w/index.php/APU_Frame_Counter for more details on here
; I'm just giving the basic idea here to conceptualize it
if( next_seq_phase causes a Quarter Frame Clock )
Clock_QuarterFrame();
if( next_seq_phase causes a Half Frame Clock )
Clock_HalfFrame();
if( irq_enabled && next_seq_phase causes an IRQ )
irq_pending = true ; raise IRQ
++next_seq_phase
if( next_seq_phase > max phases for this mode )
next_seq_phase = 0
sequencer_counter = ClocksToNextSequence()
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; determine audio output
if( duty_table[ duty_counter ] ; current duty phase is high
&& length_counter != 0 ; length counter is nonzero (channel active)
&& !IsSweepForcingSilence() ; sweep unit is not forcing channel to be silent
)
{
; output current volume
if(decay_enabled) output = decay_hidden_vol
else output = decay_V
}
else ; low duty, or channel is silent
output = 0
; ... mix other channels with output here
========================================================
Clock_QuarterFrame:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; quarter frame clocks Decay
if( decay_reset_flag )
{
decay_reset_flag = false
decay_hidden_vol = 0xF
decay_counter = decay_V
}
else
{
if( decay_counter > 0 )
--decay_counter
else
{
decay_counter = decay_V
if( decay_hidden_vol > 0 )
--decay_hidden_vol
else if( decay_loop )
decay_hidden_vol = 0xF
}
}
========================================================
Clock_HalfFrame:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; half frame clocks Sweep
if( sweep_reload )
{
sweep_counter = sweep_timer
; note there's an edge case here -- see http://wiki.nesdev.com/w/index.php/APU_Sweep
; for details. You can probably ignore it for now
sweep_reload = false
}
else if( sweep_counter > 0 )
--sweep_counter
else
{
sweep_counter = sweep_timer
if( sweep_enabled && !IsSweepForcingSilence() )
{
if(sweep_negate)
freq_timer -= (freq_timer >> sweep_shift) + 1 ; note: +1 for Pulse1 only. Pulse2
has no +1
else
freq_timer += (freq_timer >> sweep_shift)
}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; half frame also clocks length
if( length_enabled && length_counter > 0 )
--length_counter
========================================================
IsSweepForcingSilence:
if( freq_timer < 8 )
return true
else if( !sweep_negate
&&
freq_timer + (freq_timer >> sweep_shift) >= 0x800 )
return true
else
return false
Pulse 2: Identical to Pulse 1 with the following changes:
- use $4004-4007 instead of $4000-4003
- $4015 reads/writes use bit 1 instead of bit 0
- no '+ 1' when doing sweep negate
Triangle:
A few things to note about the triangle:
- It's clocked at twice the rate of other channels (use CPU clock instead of APU clock)
- To silence it, you stop clocking the tri-step unit, but do not change its output. This is in
contrast to other channels where you silence them by forcing output to zero.
- There is no volume control, but Tri might appear quieter sometimes due to interference from the
DMC. See http://wiki.nesdev.com/w/index.php/APU_Mixer for details
- When the freq timer is < 2, it goes "ultrasonic" and is effectively silenced by forcing output to
"7.5" (this causes a pop).
$4015 read / write: Same as Pulse1, only use bit 2 instead of bit 0
Note 4015 touches length counter only, it does not do anything with linear counter
========================================================
$4008 write:
linear_control = v.7
length_enabled = !v.7
linear_load = v.6543210
========================================================
$400A write:
freq_timer = v (low 8 bits)
========================================================
$400B write:
freq_timer = v.210 (high 3 bits)
if( channel_enabled )
length_counter = lengthtable[ v.76543 ]
linear_reload = true
========================================================
Every **CPU** Cycle:
; Note the Triangle is clocked at twice the rate of other channels!
; It is clocked by CPU cycle and not by APU cycle!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; clock tri wave
ultrasonic = false
if( freq_timer < 2 && freq_counter == 0 )
ultrasonic = true
clock_triunit = true
if( length_counter == 0 ) clock_triunit = false
if( linear_counter == 0 ) clock_triunit = false
if( ultrasonic ) clock_triunit = false
if( clock_triunit )
{
if( freq_counter > 0 )
--freq_counter
else
{
freq_counter = freq_timer
tri_step = (tri_step + 1) & 0x1F ; tri-step bound to 00..1F range
}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; determine audio output
; the xor here creates the 'triangle' shape
if( ultrasonic ) output = 7.5
else if( tri_step & 0x10 ) output = tri_step ^ 0x1F
else output = tri_step
========================================================
Clock_QuarterFrame:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; quarter frame clocks Linear
if( linear_reload )
linear_counter = linear_load
else if( linear_counter > 0 )
--linear_counter
if( !linear_control )
linear_reload = false
========================================================
Clock_HalfFrame:
; clock Length counter, same as Pulse
Noise:
Notes:
- noise_shift must never be zero or the noise channel will never produce any output. Initialize it
with 1 at bootup / hard reset.
- with below implementation, noise_shift must not be signed-16 bit (unsigned is OK, or something
larget than 16 bit is OK). If signed, the right-shift will feed in unwanted 1s.
$4015 read / write: Same as Pulse1, only use bit 3 instead of bit 0
========================================================
$400C write:
; same as $4000, only ignore bits 6 and 7 because noise has no duty
========================================================
$400E write:
freq_timer = noise_freq_table[ v.3210 ] ; see http://wiki.nesdev.com/w/index.php/APU_Noise for
freq table
shift_mode = v.7
========================================================
$400F write:
if( channel_enabled )
length_counter = lengthtable[ v.76543 ]
decay_reset_flag = true
========================================================
Every APU Cycle:
;;;;;;;;;;;;;;;;;;;;;;;;;;;
; clock noise shift
if( freq_counter > 0 )
--freq_counter
else
{
freq_counter = freq_timer
; note, set bit fifteen here, not bits 1 and 5
if( shift_mode ) noise_shift.15 = noise_shift.6 ^ noise_shift.0
else noise_shift.15 = noise_shift.1 ^ noise_shift.0
noise_shift >>= 1
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; determine audio output
if( noise_shift.0 == 0 ; current noise output is low (output vol when low --
opposite of pulse)
&& length_counter != 0 ; length counter is nonzero (channel active)
)
{
; output current volume
if(decay_enabled) output = decay_hidden_vol
else output = decay_V
}
else ; high shift output, or channel is silent
output = 0
========================================================
Clock_QuarterFrame:
; clock Decay, same as Pulse
========================================================
Clock_HalfFrame:
; clock Length counter, same as Pulse
DMC
$4010 write:
dmcirq_enabled = v.7
dmc_loop = v.6
freq_timer = dmc_freq_table[ v.3210 ] ; see http://wiki.nesdev.com/w/index.php/APU_DMC for freq table
if( !dmcirq_enabled )
dmcirq_pending = false ; acknowledge IRQ if disabled
========================================================
$4011 write:
output = v.6543210 ; note there is some edge case weirdness here, see wiki for details
========================================================
$4012 write:
addrload = $C000 | v<<6
========================================================
$4013 write:
lengthload = (v<<4) + 1
========================================================
$4015 write:
if( v.4 )
{
if( length == 0 )
{
length = lengthload
addr = addrload
}
}
else
length = 0
dmcirq_pending = false ; acknowledge DMC IRQ on write
========================================================
$4015 read:
v.4 = (length > 0)
v.7 = dmcirq_pending
; ... other channels and frame IRQ set other bits
========================================================
Every ?CPU? cycle????
( not sure if DMC runs on APU cycles or CPU cycles. It doesn't really matter because all the
frequencies are even. The wiki lists freqs in CPU cycles, so.... *shrug* )
;;;;;;;;;;;;;;;;;;;;;;;;
; Clock DMC unit
if( freq_counter > 0 )
--freq_counter
else
{
freq_counter = freq_timer
if( !output_unit_silent )
{
if( (output_shift & 1) && output < $7E ) output += 2
if(!(output_shift & 1) && output > $01 ) output -= 2
}
--bits_in_output_unit
output_shift >>= 1
if( bits_in_output_unit == 0 )
{
bits_in_output_unit = 8
output_shift = sample_buffer
output_unit_silent = is_sample_buffer_empty
is_sample_buffer_empty = true
}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;
; Perform DMA if necessary
if( length > 0 && is_sample_buffer_empty )
{
sample_buffer = DMAReadFromCPU( addr )
; note: this DMA halts the CPU for up to 4 cycles.
; See wiki for timing details. Note that all commercial games will work
; fine if you ignore these stolen cycles, but some tech
; demos and test ROMs will glitch/fail. So getting these stolen cycles
; correct is not super important unless you're putting a lot of emphasis
; on accuracy.
is_sample_buffer_empty = false
addr = (addr + 1) | $8000 ; <- wrap $FFFF to $8000
--length
if(length == 0)
{
if( dmc_loop )
{
length = lengthload
addr = addrload
}
else if( dmcirq_enabled )
dmcirq_pending = true ; raise IRQ
}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Determine channel output
; output is always 'output' ... the 7 bit value written to $4011 and modified by the DMC unit
Specifically there are 3 filters:
1 lowpass:
out[i]=(in[i]-out[i-1])*0.815686
and 2 highpass:
out[i]=out[i-1]*0.996039+in[i]-in[i-1]
out[i]=out[i-1]*0.999835+in[i]-in[i-1]
// 'sample' is your output sample as generated by your APU
// 'output' is what you will actually output
//
// initialize all intermediate vars to 0.0
// low pass
LP_In = sample
LP_Out = (LP_In - LP_Out) * 0.815686
// high pass A
HPA_Out = HPA_Out*0.996039 + LP_Out - HPA_Prev
HPA_Prev = LP_Out
// high pass B
HPB_Out = HPB_Out*0.999835 + HPA_Out - HPB_Prev
HPB_Prev = HPA_Out
output = HPB_Out
// scale output to be within min/max bounds
Spit out a line of text at each of the following events:
- 4008-400B writes
- 4017 writes
- linear clocks
- Frame start
On each line, include the contents of the Linear Counter so you can see how it's being updated. If you can see where your counting is going wrong, you can see where the problem in your code is.
We wary of denormal numbers. Set very tiny floating point values to 0 to avoid performance issues.
================================================
FILE: docs/apu/blargg_tests_readme.txt
================================================
NES APU Frame Counter Update
----------------------------
I have run more tests on the NES APU and come up with new information
about the exact timing of the frame counter and length counter, and some
subtle behavior. The information here either extends or contradicts what
is stated in the NES APU reference and on the nesdev wiki.
Not documented here is a delay when changing modes by writing to $4017.
This is quite complex and I haven't fully worked out its exact
operation. Once determined, documented, and tested, the information here
should still be valid. This delay when changing modes involves the
current mode running a few clocks before switching to the new mode, so
it only affects the rare case where $4017 is written within a few clocks
of a frame counter step. This delay does not cause the steps to occur
any later than shown below; it only causes the first few clocks of the
new mode to be transparent, allowing the previous mode to "show
through".
Also not documented is the exact operation of the envelope, sweep, and
triangle's linear counter when register writes occur close to clocking.
Refer to tests.txt for a description of the test ROMs included.
I have not yet fully updated my APU emulator and tested it with this
information, so report any problems you have with implementation.
Shay <hotpop.com@blargg> (swap to e-mail)
Clock Jitter
------------
Changes to the mode by writing to $4017 only occur on *even* internal
APU clocks; if written on an odd clock, the first step of the mode is
delayed by one clock. At power-up and reset, the APU is randomly in an
odd or even cycle with respect to the first clock of the first
instruction executed by the CPU.
; assume even APU and CPU clocks occur together
lda #$00
sta $4017 ; mode begins in one clock
sta <0 ; delay 3 clocks
sta $4017 ; mode begins immediately
Mode 0 Timing
-------------
-5 lda #$00
-3 sta $4017
0 (write occurs here)
1
2
3
...
Step 1
7459 Clock linear
...
Step 2
14915 Clock linear & length
...
Step 3
22373 Clock linear
...
Step 4
29830 Set frame irq
29831 Clock linear & length and set frame irq
29832 Set frame irq
...
Step 1
37289 Clock linear
...
etc.
Mode 1 Timing
-------------
-5 lda #$80
-3 sta $4017
0 (write occurs here)
Step 0
1 Clock linear & length
2
...
Step 1
7459 Clock linear
...
Step 2
14915 Clock linear & length
...
Step 3
22373 Clock linear
...
Step 4
29829 (do nothing)
...
Step 0
37283 Clock linear & length
...
etc.
Length Halt
-----------
Write to halt flag is delayed by one clock:
$10->$4000 clear halt flag
0 $00->$4017 begin mode 0
14914 $30->$4000 set halt flag
14915 Length not clocked
$10->$4000 clear halt flag
0 $00->$4017 begin mode 0
14915 $30->$4000 set halt flag
Length clocked
$30->$4000 set halt flag
0 $00->$4017 begin mode 0
14914 $10->$4000 clear halt flag
14915 Length clocked
$30->$4000 set halt flag
0 $00->$4017 begin mode 0
14915 $10->$4000 clear halt flag
Length not clocked
Length Reload
-------------
Length reload is completely ignored if written during length clocking
and length counter is non-zero before clocking:
$38->$4003 make length non-zero
0 $00->$4017
14914 Write to $4003
Length reloaded
14915 Length clocked
$38->$4003 make length non-zero
0 $00->$4017
14915 Write to $4003
Length not reloaded
Length clocked
$00->$4015 clear length counter
$01->$4015
0 $00->$4017
14915 Write to $4003
Length reloaded
Length not clocked
Misc
----
- The frame IRQ flag is cleared only when $4015 is read or $4017 is
written with bit 6 set ($40 or $c0).
- The IRQ handler is invoked at minimum 29833 clocks after writing $00
to $4017 (assuming the frame IRQ flag isn't already set, and nothing
else generates an IRQ during that time).
- After reset or power-up, APU acts as if $4017 were written with $00
from 9 to 12 clocks before first instruction begins. It is as if this
occurs (this generates a 10 clock delay):
lda #$00
sta $4017 ; 1
lda <0 ; 9 delay
nop
nop
nop
reset:
...
- As shown, the frame irq flag is set three times in a row. Thus when
polling it, always read $4015 an extra time after the flag is found to
be set, to be sure it's clear afterwards,
wait: bit $4015 ; V flag reflects frame IRQ flag
bvc wait
bit $4015 ; be sure irq flag is clear
or better yet, clear it before polling it:
bit $4015 ; clear flag first
wait: bit $4015 ; V flag reflects frame IRQ flag
bvc wait
================================================
FILE: docs/apu/mixer_readme.txt
================================================
NES APU Mixer Tests
-------------------
These tests verify proper operation of the NES APU's sound channel
mixer, including relative volumes of channels and non-linear mixing.
Tests MUST be run from a freshly-powered NES, as this is the only way to
ensure that the triangle wave doesn't interfere.
All tests beep, play a test sound, then beep again. For all but the
noise test, there should be near silence between the beeps. For the
noise test, noise will fade in and out. There shouldn't be any
noticeable tone when heard through a speaker (through headphones, faint
tones might be audible).
Internal operation
------------------
The tests have the channel under test generate a tone, then generate the
inverse waveform using the DMC DAC, canceling to (near) silence if
everything is correct.
The DMC test verifies that non-linearity of the DMC DAC. The noise and
triangle tests verify relative volume of the noise and triangle to the
DMC, and that the DMC DAC affects attenuation of them properly. Finally,
the square test verifies relative volume of the squares to the DMC,
non-linearity of the square DACs, how one square affects the other
(slightly), and that the square DAC non-linearity is separate from the
DMC.
Flashes, clicks, other glitches
-------------------------------
If a test prints "passed", it passed, even if there were some flashes or
odd sounds. Only a test which prints "done" at the end requires that you
watch/listen while it runs in order to determine whether it passed. Such
tests involve things which the CPU cannot directly test.
Alternate output
----------------
Tests generally print information on screen, but also report the final
result audibly, and output text to memory, in case the PPU doesn't work
or there isn't one, as in an NSF or a NES emulator early in development.
After the tests are done, the final result is reported as a series of
beeps (see below). For NSF builds, any important diagnostic bytes are
also reported as beeps, before the final result.
Output at $6000
---------------
All text output is written starting at $6004, with a zero-byte
terminator at the end. As more text is written, the terminator is moved
forward, so an emulator can print the current text at any time.
The test status is written to $6000. $80 means the test is running, $81
means the test needs the reset button pressed, but delayed by at least
100 msec from now. $00-$7F means the test has completed and given that
result code.
To allow an emulator to know when one of these tests is running and the
data at $6000+ is valid, as opposed to some other NES program, $DE $B0
$G1 is written to $6001-$6003.
Audible output
--------------
A byte is reported as a series of tones. The code is in binary, with a
low tone for 0 and a high tone for 1, and with leading zeroes skipped.
The first tone is always a zero. A final code of 0 means passed, 1 means
failure, and 2 or higher indicates a specific reason. See the source
code of the test for more information about the meaning of a test code.
They are found after the set_test macro. For example, the cause of test
code 3 would be found in a line containing set_test 3. Examples:
Tones Binary Decimal Meaning
- - - - - - - - - - - - - - - - - - - -
low 0 0 passed
low high 01 1 failed
low high low 010 2 error 2
NSF versions
------------
Many NSF-based tests require that the NSF player either not interrupt
the init routine with the play routine, or if it does, not interrupt the
play routine again if it hasn't returned yet. This is because many tests
need to run for a while without returning.
NSF versions also make periodic clicks to prevent the NSF player from
thinking the track is silent and thus ending the track before it's done
testing.
--
Shay Green <gblargg@gmail.com>
================================================
FILE: docs/apu/test_readme.txt
================================================
NES APU Tests
-------------
These ROMs test many aspects of the APU that are visible to the CPU.
Really obsucre things are not tested here.
1-len_ctr
---------
Tests length counter operation for the four main channels
2) Problem with length counter load or $4015
3) Problem with length table, timing, or $4015
4) Writing $80 to $4017 should clock length immediately
5) Writing 0 to $4017 shouldn't clock length immediately
6) Disabling via $4015 should clear length counter
7) When disabled via $4015, length shouldn't allow reloading
8) Halt bit should suspend length clocking
2-len_table
-----------
Verifies all length table entries
3-irq_flag
----------
Verifies basic operation of frame irq flag
2) Flag shouldn't be set in $4017 mode $40
3) Flag shouldn't be set in $4017 mode $80
4) Flag should be set in $4017 mode $00
5) Reading flag should clear it
6) Writing $00 or $80 to $4017 shouldn't affect flag
7) Writing $40 or $C0 to $4017 should clear flag
4-jitter
--------
Tests for APU clock jitter. Also tests basic timing of frame irq flag
since it's needed to determine jitter.
3) Frame irq is set too late
4) Even jitter not handled properly
5) Odd jitter not handled properly
5-len_timing
------------
Verifies timing of length counter clocks in both modes
2) First length of mode 0 is too soon
3) First length of mode 0 is too late
4) Second length of mode 0 is too soon
5) Second length of mode 0 is too late
6) Third length of mode 0 is too soon
7) Third length of mode 0 is too late
8) First length of mode 1 is too soon
9) First length of mode 1 is too late
10) Second length of mode 1 is too soon
11) Second length of mode 1 is too late
12) Third length of mode 1 is too soon
13) Third length of mode 1 is too late
6-irq_flag_timing
-----------------
Frame interrupt flag is set three times in a row 29831 clocks after
writing $00 to $4017.
3) Flag first set too late
4) Flag last set too soon
5) Flag last set too late
7-dmc_basics
------------
Verifies basic DMC operation
2) DMC isn't working well enough to test further
3) Starting DMC should reload length from $4013
4) Writing $10 to $4015 should restart DMC if previous sample finished
5) Writing $10 to $4015 should not affect DMC if previous sample is
still playing
6) Writing $00 to $4015 should stop current sample
7) Changing $4013 shouldn't affect current sample length
8) Shouldn't set DMC IRQ flag when flag is disabled
9) Should set IRQ flag when enabled and sample ends
10) Reading IRQ flag shouldn't clear it
11) Writing to $4015 should clear IRQ flag
12) Disabling IRQ flag should clear it
13) Looped sample shouldn't end until $00 is written to $4015
14) Looped sample shouldn't ever set IRQ flag
15) Clearing loop flag and then setting again shouldn't stop loop
16) Clearing loop flag should end sample once it reaches end
17) Looped sample should reload length from $4013 each time it reaches
end
18) $4013=0 should give 1-byte sample
19) There should be a one-byte buffer that's filled immediately if empty
8-dmc_rates
-----------
Verifies the DMC's 16 rates
Flashes, clicks, other glitches
-------------------------------
If a test prints "passed", it passed, even if there were some flashes or
odd sounds. Only a test which prints "done" at the end requires that you
watch/listen while it runs in order to determine whether it passed. Such
tests involve things which the CPU cannot directly test.
Alternate output
----------------
Tests generally print information on screen, but also report the final
result audibly, and output text to memory, in case the PPU doesn't work
or there isn't one, as in an NSF or a NES emulator early in development.
After the tests are done, the final result is reported as a series of
beeps (see below). For NSF builds, any important diagnostic bytes are
also reported as beeps, before the final result.
Output at $6000
---------------
All text output is written starting at $6004, with a zero-byte
terminator at the end. As more text is written, the terminator is moved
forward, so an emulator can print the current text at any time.
The test status is written to $6000. $80 means the test is running, $81
means the test needs the reset button pressed, but delayed by at least
100 msec from now. $00-$7F means the test has completed and given that
result code.
To allow an emulator to know when one of these tests is running and the
data at $6000+ is valid, as opposed to some other NES program, $DE $B0
$G1 is written to $6001-$6003.
Audible output
--------------
A byte is reported as a series of tones. The code is in binary, with a
low tone for 0 and a high tone for 1, and with leading zeroes skipped.
The first tone is always a zero. A final code of 0 means passed, 1 means
failure, and 2 or higher indicates a specific reason. See the source
code of the test for more information about the meaning of a test code.
They are found after the set_test macro. For example, the cause of test
code 3 would be found in a line containing set_test 3. Examples:
Tones Binary Decimal Meaning
- - - - - - - - - - - - - - - - - - - -
low 0 0 passed
low high 01 1 failed
low high low 010 2 error 2
NSF versions
------------
Many NSF-based tests require that the NSF player either not interrupt
the init routine with the play routine, or if it does, not interrupt the
play routine again if it hasn't returned yet. This is because many tests
need to run for a while without returning.
NSF versions also make periodic clicks to prevent the NSF player from
thinking the track is silent and thus ending the track before it's done
testing.
--
Shay Green <gblargg@gmail.com>
================================================
FILE: docs/apu/volume_readme.txt
================================================
Volume tests for NES
_____________________________________________________________________
Background
The NES has four tone generator channels and one digital sample
playback channel:
1. Rectangular pulse ("square") wave
2. Another square wave
3. 32-step triangle wave
4. Binary noise generated by a linear feedback shift register
5. Delta pulse code modulation; also allows writes of raw LPCM to
the counter for use as a generic 7-bit DAC
Unlike systems such as the GBA, which digitally add all channels
before passing them to a DAC, the NES has a separate DAC for each
channel and mixes them analog. Nonlinearities in this mixing can
cause one channel to affect the volume of another channel.
The NES uses an unsigned DAC, meaning that each channel can generate
only positive signal values. Such a DAC generates a lot of DC, and
the NES has a high-pass filter on its audio output to block the DC.
Different emulators use different time constants on their DC filters,
which human listeners generally can't perceive. So you can't just
measure the maximum voltage; you have to measure the difference
between the high and low values.
Different emulators use different amounts of headroom in the 16-bit
range, depending on what Famicom expansion audio chips are present.
So you have to compare relative volumes, not absolute volumes.
_____________________________________________________________________
The test pattern
This program demonstrates the channel balance among implementations
of the NES architecture.
The pattern consists of a set of 12 tones, as close to 1000 Hz as
the NES allows:
1. Channel 1, 1/8 duty
2. Channel 1, 1/4 duty
3. Channel 1, 1/2 duty
4. Channel 1, 3/4 duty
5. Channels 1 and 2, 1/8 duty
6. Channels 1 and 2, 1/4 duty
7. Channels 1 and 2, 1/2 duty
8. Channels 1 and 2, 3/4 duty
9. Channel 3
10. Channel 4, long LFSR period
11. Channel 4, short LFSR period
12. Channel 5, amplitude 30
When the user presses A on controller 1, the pattern plays three
times, with channel 5 held steady at 0, 48, and 96. The high point
of tone 12 each time is 30 units above the level for that time,
that is, 30, 78, and 126 respectively.
_____________________________________________________________________
Recordings
The files in the 'recordings' folder are recordings of volumes.nes
run in various environments. They were recorded at 44100 Hz and then
encoded using OggDropXPd 1.90 (libvorbis 1.2.0) at -q 7.00.
* nes-001.ogg: Nintendo Entertainment System (NTSC U/C) with PowerPak
* nestopia.ogg: Nestopia 1.40
* nintendulator.ogg: Nintendulator snapshot 2009-02-28
* fceux.ogg: FCEUX 2.0.4-interim 2008-11-24
_____________________________________________________________________
Legal
Copyright (c) 2009 Damian Yerrick
The program and manual are under the following license:
This work is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this work.
Permission is granted to anyone to use this work for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this work must not be misrepresented; you
must not claim that you wrote the original work. If you use
this work in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such,
and must not be misrepresented as being the original work.
3. This notice may not be removed or altered from any
source distribution.
The term "source" refers to the preferred form of a work for making
changes to it.
================================================
FILE: docs/cartridge_board_list.txt
================================================
10 Yard Fight Nintendo E NROM
110 in 1 (Canada?) Supervision
1942 Capcom C+ NROM
1943 Capcom C+ UNROM
6 in 1 Caltron A
720 Mindscape C SGROM
8 Eyes Taxan C- TLROM
A Boy and His Blob Absolute C SLROM
Abadox Milton Bradley C SLROM
Action 52 Active Enterprises A 023-N507
Addams Family Ocean B SLROM
Adv of Bayou Billy Konami C- SLROM
Adv of Dino Riki Hudson C CNROM
Adv of Lolo HAL C+ SEROM
Adv of Lolo 2 HAL B- TEROM
Adv of Lolo 3 HAL B+ SLROM
Adv of Tom Saywer Seta B SLROM
Adventure Island Hudson D CNROM
Adventure Island 2 Hudson B- TLROM
Adventure Island 3 Hudson B TLROM
After Burner Tengen C+ 800042-01
Air Fortress HAL C SJROM
Airwolf Acclaim C SHROM
Al Unser Racing Data East C SKROM
Aladdin Deck Enhancer Camerica A+
Alfred Chicken Mindscape B+ UNROM
Alien 3 LJN B+ 55741
Alien Syndrome Tengen C+
All Pro Basketball Vic Tokai C SLROM
Alpha Mission SNK C CNROM
Amagon American Sammy C UNROM
American Gladiators Gametek B- SLROM
Anticipation Nintendo C SEROM
Arch Rivals Acclaim C AMROM
Archon Activision B UNROM
Arkanoid Taito B+ CNROM
Arkanoid Controller Taito A
Arkistas Ring American Sammy B CNROM
Astyanax Jaleco C TLROM
Athena SNK C UNROM
Athletic World Bandai B- CNROM
Attack of the Killer Tomatoes T*HQ B- SLROM
Baby Boomer Color Dreams B-
Back to the Future LJN D CNROM
Back to the Future 2 & 3 LJN C SLROM
Bad Dudes Data East C TLROM
Bad News Baseball Tecmo B SLROM
Bad Street Brawler Mattel C SGROM
Balloon Fight Nintendo C NROM
Bandit Kings/China Koei B- ETROM
Barbie Hi Tech B SLROM
Bard's Tale FCI B- SNROM
Base Wars Ultra C TKROM
Baseball Nintendo E NROM
Baseball Simulator Culture Brain C+ SKROM
Baseball Stars SNK C+ SKROM
Baseball Stars 2 Romstar B+ TKROM
Bases Loaded Jaleco E SFROM
Bases Loaded 2 Jaleco D SL3ROM
Bases Loaded 3 Jaleco C TLROM
Bases Loaded 4 Jaleco C+ TLROM
Batman Sunsoft D NES_B4
Batman Return/Joker Sunsoft B- NES_BTR
Batman Returns Konami B+ TLROM
Battle Chess Data East B+ SKROM
Battle of Olympus Broderbund C SGROM
Battle Tank Absolute B CNROM
Battle Toads Tradewest D AOROM
Battle Toads & Double Dragon Tradewest B+ AOROM
Battleship Mindscape B CNROM
Bee 52 Camerica B+
Beetlejuice LJN C+ AMROM
Best of the Best Electro Brain B+ UOROM
Bible Adventures Wisdom Tree B+
Bible Adventures (blue label) Wisdom Tree B+
Bible Buffet Wisdom Tree B+
Big Bird Hide & Speak Hi Tech B SLROM
Big Foot Acclaim B- SLROM
Big Nose Freaks Out Camerica A
Big Nose Freaks Out (Aladdin Cart) Camerica A+
Big Nose the Caveman Camerica B
Bill & Ted's Excellent Adventure LJN C+ SLROM
Bill Elliott's NASCAR Challenge Konami C+ TSROM
Bionic Commando Capcom C SGROM
Black Bass Hot B B UNROM
Blackjack American Video A-
Blades of Steel Konami D UNROM
Blaster Master Sunsoft D SL2ROM
Blue Marlin Hot B B+ TLROM
Blues Brothers Titus B UNROM
Bo Jackson Baseball Data East C+ TSROM
Bomberman Hudson C NROM
Bomberman 2 Hudson B SNROM
Bonks Adventure Hudson B+ TLROM
Boulder Dash JVC B+ SEROM
Break Time FCI B+ SFROM
Breakthru Data East C (SLROM)
Bubble Bath Babes Panesian A+
Bubble Bobble Taito D SFROM
Bubble Bobble 2 Taito B TLROM
Bucky O'Hare Konami B TLROM
Bugs Bunny Birthday Blowout Kemco B TSROM
Bugs Bunny Crazy Castle Kemco B+ SBROM
Bump & Jump Vic Tokai B- CNROM
Burai Fighter Taxan B- TEROM
Burger Time Data East B- NROM
Cabal Milton Bradley C AMROM
Caesars Palace Virgin B UNROM
California Games Milton Bradley C UNROM
Captain America Data East B TLROM
Captain Comic Color Dreams B
Captain Planet Mindscape B TLROM
Captain Skyhawk Milton Bradley C AMROM
Casino Kid Sofel C+ UNROM
Casino Kid 2 Sofel B+ UNROM
Castelian Triffix B UNROM
Castle of Deceit Bunch Games B
Castle of Dragon Seta B UNROM
Castlequest Nexoft B CNROM
Castlevania Konami E UNROM
Castlevania 2 Konami D SLROM
Castlevania 3 Konami C- ELROM
Caveman Games Data East B- SLROM
Challenge of the Dragon Color Dreams B
Championship Bowling Romstar B CNROM
Championship Pool Mindscape B+ UNROM
Cheetahman II Active Enterprises A
Chessmaster Hi Tech B SJROM
Chiller American Game Carts Inc B+
Chubby Cherub Bandai B NROM
Circus Caper Toho B SLROM
City Connection Jaleco B CNROM
Clash at Demonhead Vic Tokai B- SLROM
Classic Concentration Gametek B- UNROM
Cliffhanger Imagesoft B TLROM
Clu Clu Land Nintendo B- NES_RROM
Cobra Command Data East C SLROM
Cobra Triangle Nintendo B- ANROM
Codename Viper Capcom C TLROM
Color A Dinosaur Virgin A- UNROM
Commando Capcom C UNROM
Conan Mindscape B UNROM
Conflict Vic Tokai C+ SKROM
Conquest Crystal Palace Asmik C+ TLROM
Contra Konami E UNROM
Contra Force Konami B TLROM
Cool World Ocean B SLROM
Cowboy Kid Romstar A- TLROM
Crash & the Boys Street Challange American Technos B TLROM
Crash Dummies LJN B 55741
Crystal Mines Color Dreams B+
Crystalis SNK C TKROM
Cyberball Jaleco B TLROM
Cybernoid Acclaim C CNROM
Dance Aerobics Nintendo C+ SBROM
Darkman Ocean B SLROM
Darkwing Duck Capcom B+ SLROM
Dash Galaxy Data East C CNROM
Day Dreamin' Davey HAL B+ SLROM
Days of Thunder Mindscape C+ TLROM
Deadly Towers Broderbund C+ BNROM
Death Race American Game Carts Inc B+
Deathbots American Video B+
Defender 2 HAL B NROM
Defender of the Crown Ultra C+ SGROM
Defenders of Dynacron City JVC B TLROM
Deja Vu Kemco C+ TKROM
Demon Sword Taito C+ SL1ROM
Desert Commander Kemco C- SKROM
Destination Earthstar Acclaim C CNROM
Destiny/Emporer Capcom B- SNROM
Dick Tracy Bandai C+ UNROM
Die Hard Activision B+ SLROM
Dig Dug 2 Bandai B NROM
Digger Milton Bradley B+ AMROM
Dirty Harry Mindscape C+ TLROM
Disney Adventure Capcom B SLROM
Dizzy the Adventurer (Aladdin Cart) Camerica A+
Donkey Kong Nintendo B- NROM
Donkey Kong 3 Nintendo B- NROM
Donkey Kong Classics Nintendo B- CNROM
Donkey Kong Jr Nintendo B- NROM
Donkey Kong Jr Math Nintendo A- NROM
Double Dare Gametek B AOROM
Double Dragon Tradewest E SLROM
Double Dragon 2 Acclaim D TL1ROM
Double Dragon 3 Acclaim C+ TLROM
Double Dribble Konami D UNROM
Double Strike American Video B+
Dr. Chaos FCI C UNROM
Dr. Jeckyl/Mr. Hyde Bandai C+ SFROM (SFDOROM)
Dr. Mario Nintendo D SEROM
Dracula Imagesoft B TSROM
Dragon Fighter Sofel B SLROM
Dragon Power Bandai C GNROM
Dragon Spirit Bandai B TLROM
Dragon Strike FCI C TLROM
Dragon Warrior Nintendo E SAROM
Dragon Warrior 2 Enix C+ SNROM
Dragon Warrior 3 Enix B+ SUROM
Dragon Warrior 4 Enix B SUROM
Dragon's Lair Imagesoft B UNROM
Duck Hunt Nintendo F NROM
Duck Tales Capcom C+ UNROM
Duck Tales 2 Capcom B+ UNROM
Dudes with Attitude American Video B+
Dungeon Magic Taito C SKROM
Dusty Diamond All Star Softball Broderbund B- SLROM
Dyno Warz Bandai C+ SLROM
Elevator Action Taito B- NROM
Eliminator Boat Duel Electro Brain C+ SLROM
Empire Strikes Back JVC B TLROM
Excitebike Nintendo E NROM
Exodus Wisdom Tree B+
F 117 Stealth Microprose B+ TLROM
F 15 City War American Video B
F 15 Strike Eagle Microprose B+ TLROM
F1 Built to Win Seta B+ SKROM
Family Feud Gametek B+ SHROM
Fantastic Adv Dizzy (Aladdin Cart) Camerica A+
Fantastic Adventures of Dizzy Camerica B-
Fantasy Zone Tengen C
Faria Nexoft B+ SKROM
Fast Break Tradewest C SCROM
Faxanadu Nintendo C- SGROM
Felix the Cat Hudson B+ TSROM
Ferrari Grand Prix Acclaim B SLROM
Fester's Quest Sunsoft C SLROM
Fighting Golf SNK C SLROM
Final Fantasy Nintendo C+ SNROM
Fire & Ice Tecmo A- TLROM
Fire Hawk Camerica B
Firehouse Rescue Gametek B CNROM
Fist of the North Star Taxan C+ UNROM
Flight of the Intruder Mindscape B UNROM
Flintstones Taito C+ TLROM
Flintstones 2 Taito A+
Flying Dragon Culture Brain C UNROM
Flying Warriors Culture Brain C SLROM
Frankenstein Bandai B+ SLRROM
Freedom Force Sunsoft D SLROM
Friday The 13th LJN C- CNROM
Fun House Hi Tech B+ UNROM
G I Joe Taxan B+ TLROM
G I Joe Atlantis Factor Capcom C+ TLROM
Galactic Crusader Bunch Games B
Galaga Bandai B NROM
Galaxy 5000 Activision A- TLROM
Game Action Replay STD A-
Game Genie Galoob D
Gargoyle's Quest 2 Capcom B+ TLROM
Gauntlet (licensed) Tengen C DRROM
Gauntlet (unlicensed) Tengen C
Gauntlet 2 Mindscape C TSROM
Gemfire Koei B- EKROM
Genghis Kahn Koei C+ SOROM
George Forman Acclaim B 55741
Ghost & Goblins Capcom C UNROM
Ghost Lion Kemco B SKROM
Ghostbusters Activision B- CNROM
Ghostbusters 2 Activision B SLROM
Ghoul School Electro Brain B- SLROM
Gilligans Island Bandai B+ UNROM
Goal Jaleco D SL3ROM
Goal 2 Jaleco B- TLSROM
Godzilla Toho C+ SLROM
Godzilla 2 Toho B SLROM
Gold Metal Challenge Capcom B+ TKROM
Golf Nintendo E NROM
Golf Grand Slam Atlus B+ SLROM
Golf Power Virgin B+ SNROM
Golgo 13 Top Secret Episode Vic Tokai D SLROM
Goonies 2 Konami C+ 351258
Gotcha LJN C CNROM
Gradius Konami C- CNROM
Great Waldo Search T*HQ B- SLROM
Gremlins 2 Sunsoft B- TLROM
Guardian Legend Broderbund C+ UNROM
Guerrilla War SNK C SLROM
Gum Shoe Nintendo C+ GNROM
Gun Nac Ascii B TLROM
Gunsmoke Capcom B- UNROM
Gyromite Nintendo E NROM
Gyruss Ultra C+ CNROM
Harlem Globetrotters Gametek B SLROM
Hatris Bullet Proof A SNROM
Heavy Barrel Data East C TLROM
Heavy Shreddin' Parker Brothers B- SLROM
Heroes of the Lance FCI C SKROM
High Speed Tradewest B- TQROM
Hillsfar FCI B SNROM
Hogan's Alley Nintendo D NROM
Hollywood Squares Gametek B+ UNROM
Home Alone T*HQ B- TSROM
Home Alone 2 T*HQ B- TLROM
Hook Imagesoft B SLROM
Hoops Jaleco D SLROM
Hot Slots Panesian A+
Hudson Hawk Imagesoft B SLROM
Hunt for Red October Hi Tech C TLROM
Hydlide FCI D NROM
I Can Remember Gametek B CNROM
Ice Climber Nintendo C+ NROM
Ice Hockey Nintendo D NROM
Ikari Warriors SNK C- UNROM
Ikari Warriors 2 SNK C- SGROM
Ikari Warriors 3 SNK B- SLROM
Image Fight Irem B TSROM
Immortal Electronic Arts B TLROM
Impossible Mission 2 American Video A-
Impossible Mission 2 SEI B+
Indiana Jones Last Crusade UBI Soft B+ UNROM
Indiana Jones Last Crusade Taito B+ SGROM
Indiana Jones Temple of Doom Tengen B-
Indiana Jones Temple of Doom Mindscape C TFROM
Indy Heat Tradewest B AMROM
Infiltrator Mindscape B TLROM
Iron Tank SNK C SLROM
Isolated Warrior NTVIC B TLROM
Jack Nicklaus Golf Konami B- 351258
Jackal Konami C UNROM
Jackie Chan Kung Fu Hudson B TLROM
James Bond Jr T*HQ B- TLROM
Jaws LJN D CNROM
Jeopardy Gametek C AOROM
Jeopardy 25th Anniversary Gametek C+ ANROM
Jeopardy Jr Gametek C ANROM
Jeopardy Super Gametek B SLROM
Jetsons Taito B+ TLROM
Jimmy Connors Tennis UBI Soft B+ UNROM
Joe & Mac Data East B+ TLROM
Jordan vs. Bird Milton Bradley C UNROM
Joshua Wisdom Tree B+
Journey to Silius Sunsoft C+ SLROM
Joust HAL B CNROM
Jungle Book Virgin A- TLROM
Jurassic Park Ocean B TSROM
Karate Champ Data East C CNROM
Karate Kid LJN C CNROM
Karnov Data East C DEIROM
Kick Master Taito C+ TLROM
Kickle Cubicle Irem B+ TLROM
Kid Icarus Nintendo C- SNROM
Kid Klown Kemco B TSROM
Kid Kool Vic Tokai C+ UNROM
Kid Niki Data East C SGROM
King Neptune's Adventure Color Dreams B+
King of Kings Wisdom Tree B+
King of Kings (cartoon mule) Wisdom Tree B+
King of the Ring Acclaim B+ 55741
Kings Knight Square C+ CNROM
Kings of the Beach Ultra C CNROM
Kings Quest 5 Konami B+ TSROM
Kirbys Adventure Nintendo B- TKROM
Kiwi Kraze Taito B+ TLROM
Klash Ball Sofel B- UNROM
Klax Tengen B+
Knight Rider Acclaim B- SCIROM
Krazy Kreatures American Video B+
Krion Conquest Vic Tokai B TLROM
Krusty's Fun House Acclaim C+ TLROM
Kung Fu Nintendo E NROM
Kung Fu Heroes Culture Brain C CNROM
L'Empereur Koei B ETROM
Laser Invasion Konami B- ELROM
Last Action Hero Imagesoft B+ TLROM
Last Ninja Jaleco B- TLROM
Last Starfighter Mindscape C+ CNROM
Legacy/Wizard Broderbund C TFROM
Legend of Kage Taito C- CNROM
Legendary Wings Capcom B UNROM
Legends/Diamond Bandai B+ TLROM
Lemmings Sunsoft B- SLROM
Lethal Weapon Ocean B SLROM
Life Force Konami C 351258
Linus Spacehead Camerica B
Linus Spacehead (Aladdin Cart) Camerica A+
Little League Baseball SNK B- SLROM
Little Mermaid Capcom B UNROM
Little Nemo Capcom B+ TLROM
Little Ninja Brothers Culture Brain C TLROM
Little Sampson Taito B TLROM
Lode Runner Broderbund B NROM
Lone Ranger Konami B- TLROM
Loopz Mindscape B+ UNROM
Low G Man Taxan C TLROM
Lunar Pool FCI C+ NROM
M C Kids Virgin C+ TSROM
M.U.L.E. Mindscape C+ SNROM
Mach Rider Nintendo C- NROM
Mad Max Mindscape C+ TLROM
Mafat Conspiracy Vic Tokai C TLROM
Magic Darts Romstar B SLRROM
Magic of Scheherazade Culture Brain C- SLROM
Magician Taxan A- TKROM
Magmax FCI C- NROM
Major League Baseball LJN E CNROM
Maniac Mansion Jaleco C+ SNROM
Mappyland Taxan C+ TFROM
Marble Madness Milton Bradley C+ ANROM
Mario Brothers Nintendo B- NROM
Mario Is Missing Mindscape B TLROM
Mario Time Machine Mindscape A- TLROM
Marvel's X-Men LJN B- UNROM
Master Chu & the Drunkard Hu Color Dreams B
Maxi 15 American Video A
Mechanized Attack SNK C SCROM
Mega Man Capcom B- UNROM
Mega Man 2 Capcom C SGROM
Mega Man 3 Capcom C TLROM
Mega Man 4 Capcom B- TGROM
Mega Man 5 Capcom B TLROM
Mega Man 6 Nintendo B+ TGROM
Menace Beach Color Dreams A-
Mendel Palace Hudson B TLROM
Mermaids of Atlantis American Video B+
Metal Fighter Color Dreams B
Metal Gear Ultra D UNROM
Metal Mech Jaleco B- SLROM
Metal Storm Irem B+ TLROM
Metroid Nintendo D SNROM
Michael Andretti World GP American Sammy B- TLROM
Mickey Mousecapade Capcom C+ CNROM
Mickey Numbers Hi Tech B+ TLROM
Mickey's Safari in Letterland Hi Tech B+ 55741
Micro Machines Camerica A-
Micro Machines (Aladdin Cart) Camerica A+
Mig 29 Camerica C+
Might & Magic American Sammy B+ TKROM
Mighty Bombjack Tecmo B CNROM
Mighty Final Fight Capcom B+ TLROM
Mike Tyson's Punch Out Nintendo D PNROM
Millipede HAL B NROM
Milon's Secret Castle Hudson C CNROM
Miracle Piano Mindscape A- SJROM
Mission Cobra Bunch Games A-
Mission Impossible Ultra C+ 352026
Monopoly Parker Brothers B- SLROM
Monster in my Pocket Konami B+ TLROM
Monster Party Bandai B SLROM
Monster Truck Rally INTV B CNROM
Moon Ranger Bunch Games C
Motor City Patrol Matchbox C+ SLROM
Ms. Pacman Namco A NROM
Ms. Pacman Tengen B+
Muppet Adventure Hi Tech B SGROM
Muscle Bandai B- NROM
Mutant Virus American Software B+ SLROM
Mystery Quest Taxan C+ CNROM
NARC Acclaim C AMROM
NES Open Golf Nintendo B- SNROM
NFL Football LJN C UNROM
Nigel Mansell Gametek B+ SLROM
Nightmare on Elm Street LJN B AMROM
Nightshade Ultra C+ TLROM
Ninja Crusaders American Sammy B TGROM
Ninja Gaiden Tecmo E SLROM
Ninja Gaiden 2 Tecmo C- TLROM
Ninja Gaiden 3 Tecmo C+ TLROM
Ninja Kid Bandai C+ CNROM
Nombunagas Ambition Koei C SOROM
Nombunagas Ambition 2 Koei B ETROM
North & South Kemco B+ TSROM
Operation Secret Storm Color Dreams A
Operation Wolf Taito D SLROM
ORB-3D Hi Tech C SCROM
Othello Acclaim C NROM
Overlord Virgin B- SN1
P'radikus Conflict Color Dreams B
Pac Man Namco A 56504
Pac Man (licensed) Tengen B- NROM
Pac Man (unlicensed) Tengen B-
Pac Mania Tengen A
Palamedes Hot B B+ SEROM
Panic Resturant Taito B TLROM
Paperboy Mindscape C CNROM
Paperboy 2 Mindscape B UOROM
Parodius (England) Palcom
Pebble Beach Golf Bandai C+ CNROM
Peek A Boo Poker Panesian A+
Perfect Fit Gametek B CNROM
Pesterminator Color Dreams B
Peter Pan & the Pirates T*HQ B- SFROM
Phantom Fighter FCI C+ SGROM
Pictionary LJN B- SLROM
Pinball Nintendo D NROM
Pinball Quest Jaleco B SLROM
Pinbot Nintendo B TQROM
Pipe Dream Bullet Proof B CNROM
Pirates Ultra B SKROM
Platoon Sunsoft D SLROM
Play Action Football Nintendo D TLSROM
Pool of Radiance FCI B+ TKROM
Popeye Nintendo B- NROM
POW SNK C SLROM
Power Blade Taito B- TLROM
Power Blade 2 Taito B- TLROM
Power Punch 2 American Softworks B TLROM
Predator Activision C+ SLROM
Prince of Persia Virgin C+ UNROM
Princess Tomato Hudson B+ SGROM
Pro Am Nintendo D SEROM
Pro Am 2 Tradewest B AOROM
Pro Sport Hockey Jaleco B+ TLSROM
Pro Wrestling Nintendo D UNROM
Puggsly's Scavenger Hunt Ocean B SLROM
Punch Out Nintendo C+ PNROM
Punisher LJN B TLROM
Puss N Boots Electro Brain B- UNROM
Puzzle American Video A-
Puzznic Taito B+ CNROM
Pyramid American Video A-
Q*Bert Ultra B- CNROM
Qix Taito A SNROM
Quantum Fighter HAL B TLROM
Quarterback Tradewest D CNROM
Quattro Adventure Camerica B+
Quattro Adventure (Aladdin Cart) Camerica A+
Quattro Arcade Camerica A-
Quattro Sports Camerica B
Quattro Sports (Aladdin Cart) Camerica A+
Race America Absolute B+ SLROM
Racket Attack Jaleco C- SLROM
Rad Gravity Activision B- SLROM
Rad Racer Nintendo D SGROM
Rad Racer 2 Square B- TVROM
Rad Racket American Video A-
Raid 2020 Color Dreams B
Raid on Bungling Bay Broderbund B NROM
Rainbow Island Taito B+ UNROM
Rally Bike Romstar B UNROM
Rambo Acclaim E UNROM
Rampage Data East C+ TFROM
Rampart Jaleco B TLROM
RBI Baseball (licensed) Tengen C+ DEROM
RBI Baseball (unlicensed) Tengen C
RBI Baseball 2 Tengen C+
RBI Baseball 3 Tengen B-
Remote Control Hi Tech C SLROM
Ren + Stimpy Buckaroos T*HQ B- TLROM
Renegade Taito C- UNROM
Rescue Kemco C- SLROM
Rescue Rangers Capcom C+ SLROM
Rescue Rangers 2 Capcom B+ SLROM
Ring King Data East C DEROM
River City Ransom American Technos C TLROM
Road Blasters Mindscape C+ SLROM
Road Runner Tengen B+
Robin Hood Virgin C+ SGROM
Robo Cop Data East C TL1ROM
Robo Cop 2 Data East B- SLROM
Robo Cop 3 Ocean B SLROM
Robo Demons Color Dreams B
Robo Warrior Jaleco B UNROM
Rock N Ball NTVIC B TFROM
Rocket Ranger Kemco B- SGROM
Rocketeer Bandai B- SGROM
Rockin Kats Atlus B+ TLROM
Rocky & Bullwinkle T*HQ B- TLROM
Roger Clemens LJN C 53361
Roller Games Ultra B- TLROM
Rollerball HAL B SFROM
Rollerblade Racer Hi Tech B 53361
Rolling Thunder Tengen C+
Romance/3 Kingdoms Koei C SOROM
Romance/3 Kingdoms 2 Koei B EWROM
Roundball Mindscape B- TSROM
Rush N Attack Konami D UNROM
Rygar Tecmo C UNROM
SCAT Natsume B SLROM
Secret Scout Color Dreams A
Section Z Capcom C UNROM
Seicross FCI D NROM
Sesame Street 1-2-3 Hi Tech C+ SEROM (SCROROM)
Sesame Street 123/ABC Hi Tech B SLROM
Sesame Street A-B-C Hi Tech C+ SEROM
Sesame Street Countdown Hi Tech A- SLROM
Shadow of the Ninja Natsume B TLROM
Shadowgate Kemco C+ TKROM
Shatterhand Jaleco B- TLROM
Shingen the Ruler Hot B C SNROM
Shinobi Tengen C+
Shockwave American Game Carts Inc B
Shooting Range Bandai B CNROM
Short Order/Eggsplode Nintendo B+ SBROM
Side Pocket Data East B UNROM
Silent Assault Color Dreams B
Silent Service Ultra C- 351258
Silk Worm American Sammy C SLROM
Silver Surfer Arcadia B TSROM
Simpsons Bart Meets Radioactive Man Acclaim B+ 55741
Simpsons Bart Vs Space Mutants Acclaim D SLROM
Simpsons Bart Vs World Acclaim B- 53361
Skate or Die Ultra D 351258
Skate or Die 2 Electronic Arts B SLROM
Ski or Die Ultra B- 351908
Skull & Crossbones Tengen B
Sky Kid Sunsoft B- SCEOROM
Sky Shark Taito D SL1ROM
Slalom Nintendo B- NROM
Smash TV Acclaim C+ 51555
Snake Rattle & Roll Nintendo C+ SEROM
Snakes Revenge Ultra C SLROM
Snoopy Silly Sports Kemco B+ SLROM
Snow Brothers Capcom B SLROM
Soccer Nintendo D NROM
Solar Jetman Tradewest C AOROM
Solitaire American Video A-
Soloman's Key Tecmo C+ CNROM
Solstice Imagesoft C ANROM
Space Shuttle Absolute B+ SGROM
Spelunker Broderbund C+ NROM
Spiderman LJN B- 53361
Spiritual Warfare Wisdom Tree B+
Spot Arcadia B- SNROM
Spy Hunter Sunsoft C CNROM
Spy vs. Spy Kemco B- NROM
Sqoon Irem B+ NROM
Stack Up Nintendo A- HVC
Stadium Events Bandai B
Stanley Electro Brain B TLROM
Star Force Tecmo C+ CNROM
Star Soldier Taxan C- CNROM
Star Trek 25th Anniversary Ultra B TLROM
Star Trek: The Next Generation Absolute A- UNROM
Star Tropics Nintendo C- HKROM
Star Voyager Acclaim C+ CNROM
Star Wars JVC B TSROM
Starship Hector Hudson B- UNROM
Stealth ATF Activision C+ SLROM
Stinger Konami B- UNROM
Street Cop Bandai A- SLROM
Street Fighter 2010 Capcom B TLROM
Strider Capcom C SGROM
Stunt Kids Camerica A-
Sunday Funday Wisdom Tree B+
Super C Konami C 352026
Super Cars Electro Brain B+ UNROM
Super Dodge Ball Imagesoft C SLROM
Super Glove Ball Mattel C UNROM
Super Mario Brothers Nintendo F NROM
Super Mario Brothers 2 Nintendo D TSROM
Super Mario Brothers 3 Nintendo D TSROM
Super Mario/Duck Hunt Nintendo F MH
Super Mario/Duck Hunt/Track Meet Nintendo C COB
Super Off Road Tradewest C- AMROM
Super Pitfall Activision C+ UNROM
Super Spike V'Ball Nintendo C- TLROM
Super Spike/World Cup Nintendo B- COB
Super Sprint Tengen C COB
Super Spy Hunter Sunsoft B+ TLROM
Super Team Games Nintendo C- CNROM
Superman Kemco B+ SLROM
Swamp Thing T*HQ C+ SLROM
Sword Master Activision A- TLROM
Swords & Serpents Acclaim B+ UNROM
T&C Surf Design LJN E CNROM
Taboo Tradewest C+ SEROM
Tag Team Wrestling Data East C NROM
Taggin Dragon Bunch Games B
Talespin Capcom B SLROM
Target Renegade Taito C+ SLROM
Tecmo Baseball Tecmo C SGROM
Tecmo Basketball Tecmo C+ TKROM
Tecmo Bowl Tecmo E SLROM
Tecmo Cup Soccer Tecmo B+ SLROM
Tecmo Super Bowl Tecmo C- TKROM
Tecmo Wrestling Tecmo C SLROM
Teenage Turtles Ultra E 351908
Teenage Turtles 2 Ultra D TLROM
Teenage Turtles 3 Konami B- TLROM
Teenage Turtles Tournament Fighters Konami B+ TLROM
Tennis Nintendo D NROM
Terminator Mindscape B+ TLROM
Terminator 2 Judgement Day LJN B- 53361
Terra Cresta Vic Tokai B+ UNROM
Tetris Tengen A- (CNROM compatible)
Tetris Nintendo D SEROM
Tetris 2 Nintendo B- TSROM
Three Stooges Activision C+ SLROM
Thrilla Safari LJN B 53361
Thunder & Lightning Romstar B+ GNROM
Thunderbirds Activision C+ SLROM
Thundercade American Sammy C+ UNROM
Tiger Heli Acclaim C+ CNROM
Tiles of Fate American Video B+
Time Lord Milton Bradley C AMROM
Times of Lore Toho B- UNROM
Tiny Toon Konami C+ TLROM
Tiny Toon Cartoon Workshop Konami B+ TSROM
Tiny Toons 2 Konami B+ TLROM
To The Earth Nintendo C+ TEROM
Toki Taito B+ TLROM
Tom & Jerry Hi Tech B+ TLROM
Tombs & Treasures Infocom A- SGROM
Toobin Tengen B
Top Gun Konami D 351298
Top Gun 2 Konami D 352026
Top Players Tennis Asmik C+ SLROM
Total Recall Acclaim D UNROM
Totally Rad Jaleco B- TLROM
Touchdown Fever SNK B+ SFROM
Toxic Crusader Bandai B TLROM
Track & Field Konami D CNROM
Track & Field 2 Konami D SLROM
Treasure Master American Softworks A- SLROM
Trick Shooting Nintendo C+ SCROM
Trog Acclaim B+ UNROM
Trojan Capcom C UNROM
Trolls on Treasure Island American Video A-
Twin Cobra American Sammy C+ TLROM
Twin Eagle Romstar C+ UNROM
Ultima/Exodus FCI C- SNROM
Ultima/Quest Avatar FCI B SNROM
Ultima/War Destiny FCI B+ SNROM
Ultimate Air Combat Activision B+ TLROM
Ultimate Basketball American Sammy C TLROM
Ultimate League Soccer American Video A-
Ultimate Stuntman Camerica C+
Uncharted Waters Koei B- ETROM
Uninvited Kemco B+ TKROM
Untouchables Ocean B- SLROM
Urban Champion Nintendo D NROM
Vegas Dream HAL B SKROM
Venice Beach Volleyball American Video A-
Vice Project Doom American Sammy C+ TLROM
Videomation T*HQ B+ CPROM
Vindicators Tengen C
Volleyball Nintendo C NROM
Wacky Races Atlus B+ TLROM
Wall Street Kid Sofel B UNROM
Wally Bear and the No Gang American Video A-
Wario Woods Nintendo B+ TKROM
Wayne Gretzky T*HQ C+ UNROM
Wayne's World T*HQ B TLROM
Werewolf Data East C TLROM
Wheel/Fortune Gametek C AOROM
Wheel/Fortune/Family Gametek C ANROM
Wheel/Fortune/Junior Gametek C ANROM
Wheel/Fortune/Vanna Gametek B AOROM
Where in Time is Carmen Konami B TSROM
Where's Waldo T*HQ C+ TSROM
Who Framed Roger Rabbit LJN B- ANROM
Whomp Em Jaleco B TLROM
Widget Atlus B TLROM
Wild Gunman Nintendo C NROM
Willow Capcom B- SLROM
Win Lose or Draw Hi Tech C SGROM
Winter Games Acclaim B- UNROM
Wizardry Nexoft C+ SKROM
Wizardry 2 Knight of Diamonds Ascii B+ TKROM
Wizards & Warriors Acclaim D ANROM
Wizards & Warriors 2 - Ironsword Acclaim D AOROM
Wizards & Warriors 3 Acclaim C- 54425
Wolverine LJN B- TLROM
World Champ Romstar B+ TLROM
World Championship Wrestling FCI D TLROM
World Class Track Meet Nintendo D CNROM
World Cup Soccer Nintendo C- TLROM
World Games Milton Bradley B- ANROM
World Runner 3D Acclaim C- UNROM
Wrath of the Black Manta Taito C+ SLROM
Wrecking Crew Nintendo C+ NROM
Wrestlemania Acclaim D ANROM
Wrestlemania Challenge LJN C- UNROM
Wrestlemania Steel Cage LJN C+ 53361
Wurm Asmik B- TLROM
Xenophobe Sunsoft B- SFROM
Xevious Bandai C+ NROM
Xexyz Hudson C+ SLROM
Yo Noid Capcom C+ SLROM
Yoshi Nintendo C- SFROM
Yoshi's Cookie Nintendo B- TFROM
Young Indy Jaleco B+ TLROM
Zanac FCI C+ UNROM
Zelda Nintendo E SNROM
Zelda 2 Nintendo D SKROM
Zen Konami B TLROM
Zoda's Revenge, Startropics 2 Nintendo B- HKROM
Zombie Nation Meldac B TLROM
================================================
FILE: docs/cpu/branch_timing_readme.txt
================================================
NES 6502 Branch Timing Test ROMs
--------------------------------
These ROMs test timing of the branch instruction, including edge cases
which an emulator might get wrong. When run on a NES they all give a
passing result. Each ROM runs several tests and reports the result on
screen and by beeping a number of times. See below for the meaning of
failure codes for each test. THE TESTS MUST BE RUN (*AND* *PASS*) IN
ORDER, because some earlier ROMs test things that later ones assume will
work properly.
Source code for each test is included, and most tests are clearly
divided into sections. Support code is also included, but it runs on a
custom devcart and assembler so it will require some effort to assemble.
Contact me if you'd like assistance porting them to your setup.
Branch Timing Summary
---------------------
An untaken branch takes 2 clocks. A taken branch takes 3 clocks. A taken
branch that crosses a page takes 4 clocks. Page crossing occurs when the
high byte of the branch target address is different than the high byte
of address of the next instruction:
branch_target:
...
bne branch_target
next_instruction:
nop
...
branch_target:
1.Branch_Basics
---------------
Tests branch timing basics and PPU NMI timing, which is needed for the
tests
2) NMI period is too short
3) NMI period is too long
4) Branch not taken is too long
5) Branch not taken is too short
6) Branch taken is too long
7) Branch taken is too short
2.Backward_Branch
-----------------
Tests backward (negative) branch timing.
2) Branch from $E4FD to $E4FC is too long
3) Branch from $E4FD to $E4FC is too short
4) Branch from $E5FE to $E5FD is too long
5) Branch from $E5FE to $E5FD is too short
6) Branch from $E700 to $E6FF is too long
7) Branch from $E700 to $E6FF is too short
8) Branch from $E801 to $E800 is too long
9) Branch from $E801 to $E800 is too short
3.Forward_Branch
----------------
Tests forward (positive) branch timing.
2) Branch from $E5FC to $E5FF is too long
3) Branch from $E5FC to $E5FF is too short
4) Branch from $E6FD to $E700 is too long
5) Branch from $E6FD to $E700 is too short
6) Branch from $E7FE to $E801 is too long
7) Branch from $E7FE to $E801 is too short
8) Branch from $E8FF to $E902 is too long
9) Branch from $E8FF to $E902 is too short
--
Shay Green <gblargg@gmail.com>
================================================
FILE: docs/cpu/dummy_writes_readme.txt
================================================
NES Double-Write Behavior Tests
----------------------------------
These tests verify that the CPU is doing double-writes properly.
Double-write is a side effect of the NES CPU when it is executing
a read-modify-write instruction: It first reads the original value,
then writes back the same val
gitextract_bip8yc8i/
├── .cargo/
│ └── config.toml
├── .config/
│ └── nextest.toml
├── .git-blame-ignore-revs
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── defect-report.md
│ │ └── feature_request.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── cd.yml
│ ├── ci.yml
│ ├── outdated.yml
│ ├── release-pr.yml
│ ├── security.yml
│ └── triage.yml
├── .gitignore
├── .gitmodules
├── .prettierignore
├── .rgignore
├── Cargo.toml
├── Cross.toml
├── LICENSE-APACHE
├── LICENSE-MIT
├── Makefile.toml
├── README.md
├── ROADMAP.md
├── assets/
│ ├── linux/
│ │ └── tetanes.desktop
│ └── macos/
│ ├── .DS_Store
│ ├── Icon.icns
│ └── Info.plist
├── cliff.toml
├── deny.toml
├── docs/
│ ├── apu/
│ │ ├── apu_ref.txt
│ │ ├── audio_psuedo_code.txt
│ │ ├── blargg_tests_readme.txt
│ │ ├── mixer_readme.txt
│ │ ├── test_readme.txt
│ │ └── volume_readme.txt
│ ├── cartridge_board_list.txt
│ ├── cpu/
│ │ ├── branch_timing_readme.txt
│ │ ├── dummy_writes_readme.txt
│ │ ├── exec_space_readme.txt
│ │ ├── instr_misc_readme.txt
│ │ ├── instr_test_readme.txt
│ │ ├── instr_timing_readme.txt
│ │ ├── interrupts_readme.txt
│ │ ├── opcode_list.txt
│ │ └── reset_readme.txt
│ ├── genie_codes
│ ├── mapper/
│ │ ├── 000.txt
│ │ ├── 001.txt
│ │ ├── 002.txt
│ │ ├── 003.txt
│ │ ├── 004.txt
│ │ ├── 005.txt
│ │ ├── 007.txt
│ │ ├── 009.txt
│ │ ├── 010.txt
│ │ ├── 011.txt
│ │ ├── 013.txt
│ │ ├── 015.txt
│ │ ├── 016.txt
│ │ ├── 018.txt
│ │ ├── 019.txt
│ │ ├── 021.txt
│ │ ├── 022.txt
│ │ ├── 023.txt
│ │ ├── 024.txt
│ │ ├── 025.txt
│ │ ├── 026.txt
│ │ ├── 032.txt
│ │ ├── 033.txt
│ │ ├── 034.txt
│ │ ├── 044.txt
│ │ ├── 045.txt
│ │ ├── 046.txt
│ │ ├── 047.txt
│ │ ├── 048.txt
│ │ ├── 049.txt
│ │ ├── 050.txt
│ │ ├── 052.txt
│ │ ├── 057.txt
│ │ ├── 058.txt
│ │ ├── 060.txt
│ │ ├── 061.txt
│ │ ├── 062.txt
│ │ ├── 064.txt
│ │ ├── 065.txt
│ │ ├── 066.txt
│ │ ├── 067.txt
│ │ ├── 068.txt
│ │ ├── 069.txt
│ │ ├── 070.txt
│ │ ├── 071.txt
│ │ ├── 072.txt
│ │ ├── 073.txt
│ │ ├── 074.txt
│ │ ├── 075.txt
│ │ ├── 076.txt
│ │ ├── 077.txt
│ │ ├── 078.txt
│ │ ├── 079.txt
│ │ ├── 080.txt
│ │ ├── 082.txt
│ │ ├── 085.txt
│ │ ├── 086.txt
│ │ ├── 087.txt
│ │ ├── 088.txt
│ │ ├── 089.txt
│ │ ├── 090.txt
│ │ ├── 091.txt
│ │ ├── 092.txt
│ │ ├── 093.txt
│ │ ├── 094.txt
│ │ ├── 095.txt
│ │ ├── 096.txt
│ │ ├── 097.txt
│ │ ├── 105.txt
│ │ ├── 107.txt
│ │ ├── 112.txt
│ │ ├── 113.txt
│ │ ├── 115.txt
│ │ ├── 118.txt
│ │ ├── 119.txt
│ │ ├── 140.txt
│ │ ├── 152.txt
│ │ ├── 154.txt
│ │ ├── 159.txt
│ │ ├── 164.txt
│ │ ├── 165.txt
│ │ ├── 180.txt
│ │ ├── 182.txt
│ │ ├── 184.txt
│ │ ├── 185.txt
│ │ ├── 189.txt
│ │ ├── 191.txt
│ │ ├── 192.txt
│ │ ├── 193.txt
│ │ ├── 194.txt
│ │ ├── 200.txt
│ │ ├── 201.txt
│ │ ├── 203.txt
│ │ ├── 205.txt
│ │ ├── 207.txt
│ │ ├── 209.txt
│ │ ├── 210.txt
│ │ ├── 225.txt
│ │ ├── 226.txt
│ │ ├── 227.txt
│ │ ├── 228.txt
│ │ ├── 230.txt
│ │ ├── 231.txt
│ │ ├── 232.txt
│ │ ├── 233.txt
│ │ ├── 234.txt
│ │ ├── 240.txt
│ │ ├── 242.txt
│ │ ├── 243.txt
│ │ ├── 245.txt
│ │ ├── 246.txt
│ │ ├── __ READ THIS FIRST __.txt
│ │ ├── changes.txt
│ │ ├── mmc3_irq_tests_readme.txt
│ │ └── mmc3_test_readme.txt
│ ├── memory_mapping.txt
│ ├── nes_arch.txt
│ ├── nes_graphics.txt
│ ├── nes_tech.txt
│ └── ppu/
│ ├── blargg_tests_readme.txt
│ ├── nmi_sync_ntsc_readme.txt
│ ├── oam_read_readme.txt
│ ├── oam_stress_readme.txt
│ ├── open_bus_readme.txt
│ ├── ppu_2c02_ref.txt
│ ├── ppu_scrolling.txt
│ ├── read_buffer_test_readme.txt
│ ├── sprite_hit_readme.txt
│ ├── sprite_overflow_readme.txt
│ ├── tv_readme.txt
│ ├── vbl_nmi_readme.txt
│ └── vbl_nmi_timing_readme.txt
├── release-plz.toml
├── rust-toolchain.toml
├── static/
│ └── tetanes.xcf
├── tetanes/
│ ├── CHANGELOG.md
│ ├── Cargo.toml
│ ├── assets/
│ │ ├── main.css
│ │ ├── pixeloid-license.txt
│ │ └── roms/
│ │ ├── alter_ego.nes
│ │ ├── alter_ego.txt
│ │ ├── ao_demo.nes
│ │ ├── ao_demo.txt
│ │ ├── assimilate.nes
│ │ ├── assimilate.txt
│ │ ├── blade_buster.nes
│ │ ├── blade_buster.txt
│ │ ├── cheril_the_goddess.nes
│ │ ├── cheril_the_goddess.txt
│ │ ├── data_man_demo.nes
│ │ ├── dushlan.nes
│ │ ├── dushlan.txt
│ │ ├── from_below.nes
│ │ ├── from_below.txt
│ │ ├── lan_master.nes
│ │ ├── lan_master.txt
│ │ ├── lawn_mower.nes
│ │ ├── lawn_mower.txt
│ │ ├── mad_wizard.nes
│ │ ├── mad_wizard.txt
│ │ ├── micro_knight.nes
│ │ ├── micro_knight.txt
│ │ ├── nebs_n_debs.txt
│ │ ├── nebs_n_debs_demo.nes
│ │ ├── owlia.nes
│ │ ├── owlia.txt
│ │ ├── streemerz.nes
│ │ ├── streemerz.txt
│ │ ├── super_painter.nes
│ │ ├── super_painter.txt
│ │ ├── tiger_jenny.nes
│ │ ├── tiger_jenny.txt
│ │ ├── yun.nes
│ │ └── yun.txt
│ ├── build.rs
│ ├── index.html
│ ├── initializer.js
│ ├── shaders/
│ │ ├── crt-easymode.wgsl
│ │ └── gui.wgsl
│ ├── src/
│ │ ├── bin/
│ │ │ └── build_artifacts.rs
│ │ ├── error.rs
│ │ ├── lib.rs
│ │ ├── logging.rs
│ │ ├── main.rs
│ │ ├── nes/
│ │ │ ├── action.rs
│ │ │ ├── audio.rs
│ │ │ ├── config.rs
│ │ │ ├── emulation/
│ │ │ │ ├── replay.rs
│ │ │ │ └── rewind.rs
│ │ │ ├── emulation.rs
│ │ │ ├── event.rs
│ │ │ ├── input.rs
│ │ │ ├── renderer/
│ │ │ │ ├── clipboard.rs
│ │ │ │ ├── event.rs
│ │ │ │ ├── gui/
│ │ │ │ │ ├── keybinds.rs
│ │ │ │ │ ├── lib.rs
│ │ │ │ │ ├── ppu_viewer.rs
│ │ │ │ │ └── preferences.rs
│ │ │ │ ├── gui.rs
│ │ │ │ ├── painter.rs
│ │ │ │ ├── shader.rs
│ │ │ │ └── texture.rs
│ │ │ ├── renderer.rs
│ │ │ ├── rom.rs
│ │ │ └── version.rs
│ │ ├── nes.rs
│ │ ├── opts.rs
│ │ ├── platform.rs
│ │ ├── sys/
│ │ │ ├── info/
│ │ │ │ ├── os.rs
│ │ │ │ └── wasm.rs
│ │ │ ├── info.rs
│ │ │ ├── logging/
│ │ │ │ ├── os.rs
│ │ │ │ └── wasm.rs
│ │ │ ├── logging.rs
│ │ │ ├── platform/
│ │ │ │ ├── os.rs
│ │ │ │ └── wasm.rs
│ │ │ ├── platform.rs
│ │ │ ├── thread/
│ │ │ │ ├── os.rs
│ │ │ │ └── wasm.rs
│ │ │ └── thread.rs
│ │ ├── sys.rs
│ │ └── thread.rs
│ └── wix/
│ └── main.wxs
├── tetanes-core/
│ ├── CHANGELOG.md
│ ├── Cargo.toml
│ ├── README.md
│ ├── benches/
│ │ └── clock_frame.rs
│ ├── game_database.txt
│ ├── ntscpalette.pal
│ ├── src/
│ │ ├── action.rs
│ │ ├── apu/
│ │ │ ├── dmc.rs
│ │ │ ├── envelope.rs
│ │ │ ├── filter.rs
│ │ │ ├── frame_counter.rs
│ │ │ ├── length_counter.rs
│ │ │ ├── noise.rs
│ │ │ ├── pulse.rs
│ │ │ ├── timer.rs
│ │ │ └── triangle.rs
│ │ ├── apu.rs
│ │ ├── bus.rs
│ │ ├── cart.rs
│ │ ├── common.rs
│ │ ├── control_deck.rs
│ │ ├── cpu/
│ │ │ └── instr.rs
│ │ ├── cpu.rs
│ │ ├── debug.rs
│ │ ├── error.rs
│ │ ├── fs.rs
│ │ ├── genie.rs
│ │ ├── input.rs
│ │ ├── lib.rs
│ │ ├── mapper/
│ │ │ ├── bandai_fcg.rs
│ │ │ ├── m000_nrom.rs
│ │ │ ├── m001_sxrom.rs
│ │ │ ├── m002_uxrom.rs
│ │ │ ├── m003_cnrom.rs
│ │ │ ├── m004_txrom.rs
│ │ │ ├── m005_exrom.rs
│ │ │ ├── m007_axrom.rs
│ │ │ ├── m009_pxrom.rs
│ │ │ ├── m010_fxrom.rs
│ │ │ ├── m011_color_dreams.rs
│ │ │ ├── m018_jalecoss88006.rs
│ │ │ ├── m019_namco163.rs
│ │ │ ├── m024_m026_vrc6.rs
│ │ │ ├── m034_bnrom.rs
│ │ │ ├── m034_nina001.rs
│ │ │ ├── m066_gxrom.rs
│ │ │ ├── m069_sunsoft_fme7.rs
│ │ │ ├── m071_bf909x.rs
│ │ │ ├── m079_nina003_006.rs
│ │ │ └── vrc_irq.rs
│ │ ├── mapper.rs
│ │ ├── mem.rs
│ │ ├── ppu/
│ │ │ ├── ctrl.rs
│ │ │ ├── frame.rs
│ │ │ ├── mask.rs
│ │ │ ├── scroll.rs
│ │ │ ├── sprite.rs
│ │ │ └── status.rs
│ │ ├── ppu.rs
│ │ ├── sys/
│ │ │ ├── fs/
│ │ │ │ ├── os.rs
│ │ │ │ └── wasm.rs
│ │ │ ├── fs.rs
│ │ │ └── time.rs
│ │ ├── sys.rs
│ │ ├── time.rs
│ │ └── video.rs
│ └── test_roms/
│ ├── apu/
│ │ ├── apu_env.nes
│ │ ├── blargg_readme.txt
│ │ ├── clock_jitter.nes
│ │ ├── dmc.nes
│ │ ├── dmc_basics.nes
│ │ ├── dmc_buffer_retained.nes
│ │ ├── dmc_dma_2007_read.nes
│ │ ├── dmc_dma_2007_write.nes
│ │ ├── dmc_dma_4016_read.nes
│ │ ├── dmc_dma_double_2007_read.nes
│ │ ├── dmc_dma_read_write_2007.nes
│ │ ├── dmc_latency.nes
│ │ ├── dmc_pitch.nes
│ │ ├── dmc_rates.nes
│ │ ├── dmc_status.nes
│ │ ├── dmc_status_irq.nes
│ │ ├── dpcmletterbox.nes
│ │ ├── dpcmletterbox.txt
│ │ ├── irq_flag.nes
│ │ ├── irq_flag_timing.nes
│ │ ├── irq_timing.nes
│ │ ├── len_ctr.nes
│ │ ├── len_halt_timing.nes
│ │ ├── len_reload_timing.nes
│ │ ├── len_table.nes
│ │ ├── len_timing.nes
│ │ ├── len_timing_mode0.nes
│ │ ├── len_timing_mode1.nes
│ │ ├── lin_ctr.nes
│ │ ├── mixer.txt
│ │ ├── noise.nes
│ │ ├── noise_pitch.nes
│ │ ├── pal_clock_jitter.nes
│ │ ├── pal_irq_flag.nes
│ │ ├── pal_irq_flag_timing.nes
│ │ ├── pal_irq_timing.nes
│ │ ├── pal_len_ctr.nes
│ │ ├── pal_len_halt_timing.nes
│ │ ├── pal_len_reload_timing.nes
│ │ ├── pal_len_table.nes
│ │ ├── pal_len_timing_mode0.nes
│ │ ├── pal_len_timing_mode1.nes
│ │ ├── pal_readme.txt
│ │ ├── phase_reset.nes
│ │ ├── readme.txt
│ │ ├── reset.txt
│ │ ├── reset_4015_cleared.nes
│ │ ├── reset_4017_timing.nes
│ │ ├── reset_4017_written.nes
│ │ ├── reset_irq_flag_cleared.nes
│ │ ├── reset_len_ctrs_enabled.nes
│ │ ├── reset_timing.nes
│ │ ├── reset_works_immediately.nes
│ │ ├── square.nes
│ │ ├── square_pitch.nes
│ │ ├── sweep_cutoff.nes
│ │ ├── sweep_sub.nes
│ │ ├── test_1.nes
│ │ ├── test_10.nes
│ │ ├── test_2.nes
│ │ ├── test_3.nes
│ │ ├── test_4.nes
│ │ ├── test_5.nes
│ │ ├── test_6.nes
│ │ ├── test_7.nes
│ │ ├── test_8.nes
│ │ ├── test_9.nes
│ │ ├── tests.json
│ │ ├── triangle.nes
│ │ ├── triangle_pitch.nes
│ │ ├── volumes.nes
│ │ └── volumes.txt
│ ├── cpu/
│ │ ├── branch.txt
│ │ ├── branch_backward.nes
│ │ ├── branch_basics.nes
│ │ ├── branch_forward.nes
│ │ ├── dummy_reads.nes
│ │ ├── dummy_writes.txt
│ │ ├── dummy_writes_oam.nes
│ │ ├── dummy_writes_ppumem.nes
│ │ ├── exec_space.txt
│ │ ├── exec_space_apu.nes
│ │ ├── exec_space_ppuio.nes
│ │ ├── flag_concurrency.nes
│ │ ├── instr.txt
│ │ ├── instr_abs.nes
│ │ ├── instr_abs_xy.nes
│ │ ├── instr_basics.nes
│ │ ├── instr_branches.nes
│ │ ├── instr_brk.nes
│ │ ├── instr_imm.nes
│ │ ├── instr_imp.nes
│ │ ├── instr_ind_x.nes
│ │ ├── instr_ind_y.nes
│ │ ├── instr_jmp_jsr.nes
│ │ ├── instr_misc.nes
│ │ ├── instr_misc.txt
│ │ ├── instr_rti.nes
│ │ ├── instr_rts.nes
│ │ ├── instr_special.nes
│ │ ├── instr_stack.nes
│ │ ├── instr_timing.nes
│ │ ├── instr_timing.txt
│ │ ├── instr_zp.nes
│ │ ├── instr_zp_xy.nes
│ │ ├── int_branch_delays_irq.nes
│ │ ├── int_cli_latency.nes
│ │ ├── int_irq_and_dma.nes
│ │ ├── int_nmi_and_brk.nes
│ │ ├── int_nmi_and_irq.nes
│ │ ├── interrupts.txt
│ │ ├── nestest.nes
│ │ ├── nestest.txt
│ │ ├── overclock.nes
│ │ ├── ram_after_reset.nes
│ │ ├── regs_after_reset.nes
│ │ ├── reset.txt
│ │ ├── sprdma_and_dmc_dma.nes
│ │ ├── sprdma_and_dmc_dma_512.nes
│ │ ├── tests.json
│ │ ├── timing.txt
│ │ └── timing_test.nes
│ ├── input/
│ │ ├── tests.json
│ │ ├── zapper_flip.nes
│ │ ├── zapper_light.nes
│ │ ├── zapper_stream.nes
│ │ └── zapper_trigger.nes
│ ├── mapper/
│ │ ├── m004_txrom/
│ │ │ ├── a12_clocking.nes
│ │ │ ├── big_chr_ram.nes
│ │ │ ├── clocking.nes
│ │ │ ├── details.nes
│ │ │ ├── irq.txt
│ │ │ ├── rev_a.nes
│ │ │ ├── rev_b.nes
│ │ │ ├── scanline_timing.nes
│ │ │ └── tests.json
│ │ └── m005_exrom/
│ │ ├── basics.nes
│ │ ├── exram.nes
│ │ └── tests.json
│ ├── ppu/
│ │ ├── _240pee.nes
│ │ ├── blargg_readme.txt
│ │ ├── color.nes
│ │ ├── ntsc_torture.nes
│ │ ├── oam_read.nes
│ │ ├── oam_read.txt
│ │ ├── oam_stress.nes
│ │ ├── oam_stress.txt
│ │ ├── open_bus.nes
│ │ ├── open_bus.txt
│ │ ├── palette.nes
│ │ ├── palette_ram.nes
│ │ ├── read_buffer.nes
│ │ ├── read_buffer.txt
│ │ ├── scanline.nes
│ │ ├── spr_hit.txt
│ │ ├── spr_hit_alignment.nes
│ │ ├── spr_hit_basics.nes
│ │ ├── spr_hit_corners.nes
│ │ ├── spr_hit_double_height.nes
│ │ ├── spr_hit_edge_timing.nes
│ │ ├── spr_hit_flip.nes
│ │ ├── spr_hit_left_clip.nes
│ │ ├── spr_hit_right_edge.nes
│ │ ├── spr_hit_screen_bottom.nes
│ │ ├── spr_hit_timing_basics.nes
│ │ ├── spr_hit_timing_order.nes
│ │ ├── spr_overflow.txt
│ │ ├── spr_overflow_basics.nes
│ │ ├── spr_overflow_details.nes
│ │ ├── spr_overflow_emulator.nes
│ │ ├── spr_overflow_obscure.nes
│ │ ├── spr_overflow_timing.nes
│ │ ├── sprite_ram.nes
│ │ ├── tests.json
│ │ ├── tv.nes
│ │ ├── tv.txt
│ │ ├── vbl_nmi.txt
│ │ ├── vbl_nmi_basics.nes
│ │ ├── vbl_nmi_clear_timing.nes
│ │ ├── vbl_nmi_control.nes
│ │ ├── vbl_nmi_disable.nes
│ │ ├── vbl_nmi_even_odd_frames.nes
│ │ ├── vbl_nmi_even_odd_timing.nes
│ │ ├── vbl_nmi_frame_basics.nes
│ │ ├── vbl_nmi_off_timing.nes
│ │ ├── vbl_nmi_on_timing.nes
│ │ ├── vbl_nmi_set_time.nes
│ │ ├── vbl_nmi_suppression.nes
│ │ ├── vbl_nmi_timing.nes
│ │ ├── vbl_nmi_timing.txt
│ │ ├── vbl_timing.nes
│ │ └── vram_access.nes
│ └── spritecans.nes
├── tetanes-utils/
│ ├── Cargo.toml
│ └── src/
│ └── bin/
│ ├── generate_db.rs
│ └── list_boards.rs
└── vendored/
├── linuxdeploy-aarch64.AppImage
└── linuxdeploy-x86_64.AppImage
SYMBOL INDEX (2241 symbols across 98 files)
FILE: tetanes-core/benches/clock_frame.rs
function main (line 11) | fn main() {
FILE: tetanes-core/src/action.rs
type Action (line 22) | pub enum Action {
FILE: tetanes-core/src/apu.rs
type ParseChannelError (line 37) | pub struct ParseChannelError;
type Channel (line 42) | pub enum Channel {
type Error (line 52) | type Error = ParseChannelError;
method try_from (line 54) | fn try_from(value: usize) -> Result<Self, Self::Error> {
type Apu (line 72) | pub struct Apu {
constant DEFAULT_SAMPLE_RATE (line 105) | pub const DEFAULT_SAMPLE_RATE: f32 = 44_100.0;
constant MAX_CHANNEL_COUNT (line 107) | pub const MAX_CHANNEL_COUNT: usize = 6;
constant CYCLE_SIZE (line 108) | pub const CYCLE_SIZE: u32 = 10_000;
method new (line 111) | pub fn new(region: NesRegion) -> Self {
method default_channel_outputs (line 140) | pub fn default_channel_outputs() -> Box<[f32]> {
method add_mapper_output (line 145) | pub fn add_mapper_output(&mut self, output: f32) {
method process_outputs (line 153) | pub fn process_outputs(&mut self) {
method set_sample_rate (line 183) | pub fn set_sample_rate(&mut self, sample_rate: f32) {
method set_frame_speed (line 192) | pub fn set_frame_speed(&mut self, speed: f32) {
method channel_enabled (line 202) | pub const fn channel_enabled(&self, channel: Channel) -> bool {
method set_channel_enabled (line 214) | pub const fn set_channel_enabled(&mut self, channel: Channel, enabled:...
method toggle_channel (line 226) | pub const fn toggle_channel(&mut self, channel: Channel) {
method clock_lazy (line 237) | pub fn clock_lazy(&mut self) {
method clock_sync (line 250) | pub fn clock_sync(&mut self) {
method should_clock (line 266) | fn should_clock(&mut self) -> bool {
method channel_clock_to (line 277) | fn channel_clock_to(&mut self, channel: Channel, cycle: u32) {
method clock_to (line 301) | fn clock_to(&mut self, cycle: u32) {
method write_ctrl (line 344) | pub fn write_ctrl(&mut self, channel: Channel, val: u8) {
method write_sweep (line 365) | pub fn write_sweep(&mut self, channel: Channel, val: u8) {
method write_timer_lo (line 381) | pub fn write_timer_lo(&mut self, channel: Channel, val: u8) {
method write_timer_hi (line 409) | pub fn write_timer_hi(&mut self, channel: Channel, val: u8) {
method write_linear_counter (line 432) | pub fn write_linear_counter(&mut self, val: u8) {
method write_length (line 440) | pub fn write_length(&mut self, channel: Channel, val: u8) {
method write_dmc_output (line 454) | pub fn write_dmc_output(&mut self, val: u8) {
method write_dmc_addr (line 466) | pub fn write_dmc_addr(&mut self, val: u8) {
method read_status (line 475) | pub fn read_status(&mut self) -> u8 {
method peek_status (line 489) | pub fn peek_status(&self) -> u8 {
method write_status (line 519) | pub fn write_status(&mut self, val: u8) {
method write_frame_counter (line 531) | pub fn write_frame_counter(&mut self, val: u8) {
method irq_pending (line 539) | pub const fn irq_pending(&self) -> bool {
method dma_pending (line 545) | pub const fn dma_pending(&self) -> bool {
method clear_dma_pending (line 551) | pub const fn clear_dma_pending(&mut self) {
method fmt (line 590) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<...
method default (line 99) | fn default() -> Self {
method region (line 557) | fn region(&self) -> NesRegion {
method set_region (line 561) | fn set_region(&mut self, region: NesRegion) {
method reset (line 575) | fn reset(&mut self, kind: ResetKind) {
FILE: tetanes-core/src/apu/dmc.rs
type Dmc (line 17) | pub struct Dmc {
constant PERIOD_TABLE_NTSC (line 46) | const PERIOD_TABLE_NTSC: [u16; 16] = [
constant PERIOD_TABLE_PAL (line 49) | const PERIOD_TABLE_PAL: [u16; 16] = [
method new (line 53) | pub const fn new(region: NesRegion) -> Self {
method silent (line 78) | pub const fn silent(&self) -> bool {
method set_silent (line 82) | pub const fn set_silent(&mut self, silent: bool) {
method irq_pending_in (line 88) | pub fn irq_pending_in(&self, cycles_to_run: u32) -> bool {
method dma_addr (line 99) | pub const fn dma_addr(&self) -> u16 {
method init_sample (line 103) | fn init_sample(&mut self) {
method load_buffer (line 114) | pub fn load_buffer(&mut self, val: u8) {
method period (line 136) | const fn period(region: NesRegion, val: u8) -> u16 {
method write_timer (line 147) | pub const fn write_timer(&mut self, val: u8) {
method write_output (line 157) | pub const fn write_output(&mut self, val: u8) {
method write_addr (line 162) | pub fn write_addr(&mut self, val: u8) {
method write_length (line 167) | pub fn write_length(&mut self, val: u8) {
method set_enabled (line 172) | pub fn set_enabled(&mut self, enabled: bool, cycle: u32) {
method should_clock (line 184) | pub fn should_clock(&mut self) -> bool {
method default (line 40) | fn default() -> Self {
method output (line 197) | fn output(&self) -> f32 {
method cycle (line 207) | fn cycle(&self) -> u32 {
method clock (line 217) | fn clock(&mut self) {
method region (line 253) | fn region(&self) -> NesRegion {
method set_region (line 257) | fn set_region(&mut self, region: NesRegion) {
method reset (line 264) | fn reset(&mut self, kind: ResetKind) {
FILE: tetanes-core/src/apu/envelope.rs
type Envelope (line 13) | pub struct Envelope {
method new (line 23) | pub const fn new() -> Self {
method volume (line 36) | pub const fn volume(&self) -> u8 {
method restart (line 45) | pub const fn restart(&mut self) {
method write_ctrl (line 51) | pub const fn write_ctrl(&mut self, val: u8) {
method clock (line 59) | fn clock(&mut self) {
method reset (line 78) | fn reset(&mut self, _kind: ResetKind) {
FILE: tetanes-core/src/apu/filter.rs
type Consume (line 13) | pub trait Consume {
method consume (line 14) | fn consume(&mut self, sample: f32);
method consume (line 76) | fn consume(&mut self, sample: f32) {
method consume (line 115) | fn consume(&mut self, sample: f32) {
method consume (line 188) | fn consume(&mut self, sample: f32) {
method consume (line 294) | fn consume(&mut self, sample: f32) {
type FilterKind (line 20) | pub enum FilterKind {
type Iir (line 29) | pub struct Iir {
method identity (line 38) | pub const fn identity() -> Self {
method high_pass (line 48) | pub fn high_pass(sample_rate: f32, cutoff: f32) -> Self {
method low_pass (line 61) | pub fn low_pass(sample_rate: f32, cutoff: f32) -> Self {
method output (line 84) | fn output(&self) -> f32 {
type Fir (line 96) | pub struct Fir {
method low_pass (line 104) | pub fn low_pass(sample_rate: f32, cutoff: f32, window_size: usize) -> ...
method output (line 125) | fn output(&self) -> f32 {
function windowed_sinc_kernel (line 148) | pub fn windowed_sinc_kernel(sample_rate: f32, cutoff: f32, window_size: ...
type Filter (line 182) | pub enum Filter {
method from (line 206) | fn from(filter: Iir) -> Self {
method from (line 212) | fn from(filter: Fir) -> Self {
method output (line 197) | fn output(&self) -> f32 {
type SampledFilter (line 220) | pub struct SampledFilter {
method new (line 227) | pub fn new(filter: impl Into<Filter>, sample_rate: f32) -> Self {
type FilterChain (line 238) | pub struct FilterChain {
method new (line 245) | pub fn new(region: NesRegion, output_rate: f32) -> Self {
method output (line 311) | fn output(&self) -> f32 {
FILE: tetanes-core/src/apu/frame_counter.rs
type FrameCounter (line 13) | pub struct FrameCounter {
constant STEP4_CYCLES_NTSC (line 36) | const STEP4_CYCLES_NTSC: [u32; 6] = [7457, 14913, 22371, 29828, 29829,...
constant STEP5_CYCLES_NTSC (line 37) | const STEP5_CYCLES_NTSC: [u32; 6] = [7457, 14913, 22371, 29829, 37281,...
constant STEP4_CYCLES_PAL (line 38) | const STEP4_CYCLES_PAL: [u32; 6] = [8313, 16627, 24939, 33252, 33253, ...
constant STEP5_CYCLES_PAL (line 39) | const STEP5_CYCLES_PAL: [u32; 6] = [8313, 16627, 24939, 33253, 41565, ...
constant FRAME_TYPE (line 41) | const FRAME_TYPE: [FrameType; 6] = [
method new (line 50) | pub const fn new(region: NesRegion) -> Self {
method set_region (line 67) | pub const fn set_region(&mut self, region: NesRegion) {
method step_cycles (line 72) | const fn step_cycles(mode: u8, region: NesRegion) -> [u32; 6] {
method write (line 82) | pub fn write(&mut self, val: u8, cycle: u32) {
method should_clock (line 95) | pub const fn should_clock(&mut self, cycles: u32) -> bool {
method clock_with (line 112) | pub fn clock_with(&mut self, cycles: u32, mut on_clock: impl FnMut(Fra...
type FrameType (line 28) | pub enum FrameType {
method reset (line 177) | fn reset(&mut self, kind: ResetKind) {
FILE: tetanes-core/src/apu/length_counter.rs
type LengthCounter (line 16) | pub struct LengthCounter {
constant LENGTH_TABLE (line 27) | const LENGTH_TABLE: [u8; 32] = [
method new (line 32) | pub const fn new(channel: Channel) -> Self {
method write (line 45) | pub const fn write(&mut self, val: u8) {
method set_enabled (line 53) | pub const fn set_enabled(&mut self, enabled: bool) {
method reload (line 61) | pub const fn reload(&mut self) {
method write_ctrl (line 72) | pub const fn write_ctrl(&mut self, halt: bool) {
method clock (line 78) | fn clock(&mut self) {
method reset (line 86) | fn reset(&mut self, kind: ResetKind) {
FILE: tetanes-core/src/apu/noise.rs
type ShiftMode (line 18) | pub enum ShiftMode {
type Noise (line 30) | pub struct Noise {
constant PERIOD_TABLE_NTSC (line 47) | const PERIOD_TABLE_NTSC: [u16; 16] = [
constant PERIOD_TABLE_PAL (line 50) | const PERIOD_TABLE_PAL: [u16; 16] = [
method new (line 54) | pub const fn new(region: NesRegion) -> Self {
method is_muted (line 67) | pub const fn is_muted(&self) -> bool {
method silent (line 72) | pub const fn silent(&self) -> bool {
method set_silent (line 76) | pub const fn set_silent(&mut self, silent: bool) {
method period (line 80) | const fn period(region: NesRegion, val: u8) -> u16 {
method clock_quarter_frame (line 90) | pub fn clock_quarter_frame(&mut self) {
method clock_half_frame (line 94) | pub fn clock_half_frame(&mut self) {
method write_ctrl (line 100) | pub const fn write_ctrl(&mut self, val: u8) {
method write_timer (line 106) | pub const fn write_timer(&mut self, val: u8) {
method write_length (line 116) | pub const fn write_length(&mut self, val: u8) {
method set_enabled (line 121) | pub const fn set_enabled(&mut self, enabled: bool) {
method volume (line 125) | pub const fn volume(&self) -> u8 {
method default (line 41) | fn default() -> Self {
method output (line 135) | fn output(&self) -> f32 {
method cycle (line 145) | fn cycle(&self) -> u32 {
method clock (line 155) | fn clock(&mut self) {
method region (line 170) | fn region(&self) -> NesRegion {
method set_region (line 174) | fn set_region(&mut self, region: NesRegion) {
method reset (line 180) | fn reset(&mut self, kind: ResetKind) {
FILE: tetanes-core/src/apu/pulse.rs
type OutputFreq (line 18) | pub enum OutputFreq {
type PulseChannel (line 25) | pub enum PulseChannel {
type Pulse (line 35) | pub struct Pulse {
constant DUTY_TABLE (line 55) | const DUTY_TABLE: [[u8; 8]; 4] = [
method new (line 62) | pub const fn new(channel: PulseChannel, output_freq: OutputFreq) -> Se...
method is_muted (line 81) | pub fn is_muted(&self) -> bool {
method silent (line 89) | pub const fn silent(&self) -> bool {
method set_silent (line 93) | pub const fn set_silent(&mut self, silent: bool) {
method update_target_period (line 97) | const fn update_target_period(&mut self) {
method set_period (line 109) | const fn set_period(&mut self, period: u16) {
method clock_sweep (line 115) | const fn clock_sweep(&mut self) {
method clock_quarter_frame (line 134) | pub fn clock_quarter_frame(&mut self) {
method clock_half_frame (line 138) | pub fn clock_half_frame(&mut self) {
method write_ctrl (line 145) | pub const fn write_ctrl(&mut self, val: u8) {
method write_sweep (line 152) | pub const fn write_sweep(&mut self, val: u8) {
method write_timer_lo (line 162) | pub fn write_timer_lo(&mut self, val: u8) {
method write_timer_hi (line 167) | pub fn write_timer_hi(&mut self, val: u8) {
method set_enabled (line 174) | pub const fn set_enabled(&mut self, enabled: bool) {
method volume (line 178) | pub const fn volume(&self) -> u8 {
method default (line 49) | fn default() -> Self {
method output (line 188) | fn output(&self) -> f32 {
method cycle (line 200) | fn cycle(&self) -> u32 {
method clock (line 215) | fn clock(&mut self) {
method reset (line 223) | fn reset(&mut self, kind: ResetKind) {
type Sweep (line 238) | pub struct Sweep {
method new (line 251) | pub const fn new(channel: PulseChannel) -> Self {
method reset (line 267) | fn reset(&mut self, _kind: ResetKind) {
FILE: tetanes-core/src/apu/timer.rs
type TimerCycle (line 7) | pub trait TimerCycle {
method cycle (line 8) | fn cycle(&self) -> u32;
type Timer (line 15) | pub struct Timer {
method new (line 22) | pub const fn new(period: u16) -> Self {
method preload (line 30) | pub const fn preload(period: u16) -> Self {
method reload (line 36) | pub const fn reload(&mut self) {
method tick (line 40) | pub const fn tick(&mut self) -> bool {
method reset (line 52) | fn reset(&mut self, _kind: ResetKind) {
function timer (line 64) | fn timer() {
FILE: tetanes-core/src/apu/triangle.rs
type Triangle (line 20) | pub struct Triangle {
constant SEQUENCE (line 35) | const SEQUENCE: [u8; 32] = [
method new (line 40) | pub const fn new() -> Self {
method silent (line 51) | pub const fn silent(&self) -> bool {
method set_silent (line 55) | pub const fn set_silent(&mut self, silent: bool) {
method clock_quarter_frame (line 59) | pub fn clock_quarter_frame(&mut self) {
method clock_half_frame (line 63) | pub fn clock_half_frame(&mut self) {
method write_linear_counter (line 69) | pub const fn write_linear_counter(&mut self, val: u8) {
method write_timer_lo (line 76) | pub fn write_timer_lo(&mut self, val: u8) {
method write_timer_hi (line 81) | pub fn write_timer_hi(&mut self, val: u8) {
method set_enabled (line 87) | pub const fn set_enabled(&mut self, enabled: bool) {
method default (line 29) | fn default() -> Self {
method output (line 93) | fn output(&self) -> f32 {
method cycle (line 107) | fn cycle(&self) -> u32 {
method clock (line 117) | fn clock(&mut self) {
method reset (line 125) | fn reset(&mut self, kind: ResetKind) {
type LinearCounter (line 137) | pub struct LinearCounter {
method new (line 145) | pub const fn new() -> Self {
method write (line 154) | pub const fn write(&mut self, val: u8) {
method clock (line 160) | fn clock(&mut self) {
method reset (line 173) | fn reset(&mut self, _kind: ResetKind) {
FILE: tetanes-core/src/bus.rs
type Bus (line 51) | pub struct Bus {
method new (line 83) | pub fn new(region: NesRegion, ram_state: RamState) -> Self {
method load_cart (line 96) | pub fn load_cart(&mut self, cart: Cart) {
method unload_cart (line 100) | pub fn unload_cart(&mut self) {
method wram (line 107) | pub fn wram(&self) -> &[u8; size::WRAM] {
method add_genie_code (line 116) | pub fn add_genie_code(&mut self, genie_code: GenieCode) {
method remove_genie_code (line 122) | pub fn remove_genie_code(&mut self, code: &str) {
method clear_genie_codes (line 127) | pub fn clear_genie_codes(&mut self) {
method genie_read (line 131) | fn genie_read(&self, addr: u16, val: u8) -> u8 {
method audio_samples (line 139) | pub fn audio_samples(&self) -> &[f32] {
method clear_audio_samples (line 144) | pub fn clear_audio_samples(&mut self) {
method cpu_clock (line 149) | pub fn cpu_clock(&mut self) {
method default (line 72) | fn default() -> Self {
constant WRAM (line 79) | pub const WRAM: usize = 0x800;
method read (line 159) | fn read(&mut self, addr: u16) -> u8 {
method peek (line 183) | fn peek(&self, addr: u16) -> u8 {
method write (line 208) | fn write(&mut self, addr: u16, val: u8) {
method region (line 254) | fn region(&self) -> NesRegion {
method set_region (line 258) | fn set_region(&mut self, region: NesRegion) {
method reset (line 267) | fn reset(&mut self, kind: ResetKind) {
method save (line 277) | fn save(&self, path: impl AsRef<Path>) -> fs::Result<()> {
method load (line 281) | fn load(&mut self, path: impl AsRef<Path>) -> fs::Result<()> {
function load_cart_values (line 295) | fn load_cart_values() {
function load_cart_chr_rom (line 322) | fn load_cart_chr_rom() {
function load_cart_chr_ram (line 352) | fn load_cart_chr_ram() {
function genie_codes (line 388) | fn genie_codes() {
function clock (line 412) | fn clock() {
function read_write_ram (line 422) | fn read_write_ram() {
function read_write_ppu (line 445) | fn read_write_ppu() {
function read_write_apu (line 454) | fn read_write_apu() {
function write_apu_pulse (line 462) | fn write_apu_pulse() {
function write_apu_triangle (line 470) | fn write_apu_triangle() {
function write_apu_noise (line 477) | fn write_apu_noise() {
function write_dmc (line 484) | fn write_dmc() {
function read_write_input (line 491) | fn read_write_input() {
function read_write_mapper (line 497) | fn read_write_mapper() {
function reset (line 503) | fn reset() {
FILE: tetanes-core/src/cart.rs
constant PRG_ROM_BANK_SIZE (line 23) | const PRG_ROM_BANK_SIZE: usize = 0x4000;
constant CHR_ROM_BANK_SIZE (line 24) | const CHR_ROM_BANK_SIZE: usize = 0x2000;
type Result (line 26) | pub type Result<T> = std::result::Result<T, Error>;
type Error (line 30) | pub enum Error {
method io (line 47) | pub fn io(source: std::io::Error, context: impl Into<String>) -> Self {
type Cart (line 58) | pub struct Cart {
method empty (line 80) | pub fn empty() -> Self {
method from_path (line 103) | pub fn from_path<P: AsRef<Path>>(path: P, ram_state: RamState) -> Resu...
method from_rom (line 118) | pub fn from_rom<S, F>(name: S, mut rom_data: &mut F, ram_state: RamSta...
method name (line 245) | pub fn name(&self) -> &str {
method is_ines (line 250) | pub const fn is_ines(&self) -> bool {
method is_nes2 (line 258) | pub const fn is_nes2(&self) -> bool {
method battery_backed (line 264) | pub const fn battery_backed(&self) -> bool {
method ram_state (line 269) | pub const fn ram_state(&self) -> RamState {
method mirroring (line 274) | pub fn mirroring(&self) -> Mirroring {
method mapper_num (line 288) | pub fn mapper_num(&self) -> u16 {
method submapper_num (line 297) | pub fn submapper_num(&self) -> u8 {
method mapper_board (line 306) | pub fn mapper_board(&self) -> &'static str {
method chr_size (line 310) | pub fn chr_size(&self) -> usize {
method chr_rom_or_ram (line 337) | pub(crate) fn chr_rom_or_ram(
method prg_ram_or_default (line 360) | pub(crate) fn prg_ram_or_default(&self, size: usize) -> Memory<Box<[u8...
method calculate_ram_size (line 371) | fn calculate_ram_size(value: u8) -> Result<usize> {
method lookup_info (line 385) | fn lookup_info(prg_rom: &[u8], chr: &[u8]) -> Option<GameInfo> {
method fmt (line 425) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<...
method default (line 74) | fn default() -> Self {
method region (line 415) | fn region(&self) -> NesRegion {
method set_region (line 419) | fn set_region(&mut self, region: NesRegion) {
type GameInfo (line 443) | pub struct GameInfo {
type NesVariant (line 452) | pub enum NesVariant {
type NesHeader (line 467) | pub struct NesHeader {
method from_path (line 487) | pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
method load (line 501) | pub fn load<F: Read>(rom_data: &mut F) -> Result<Self> {
method mapper_board (line 634) | pub const fn mapper_board(mapper_num: u16) -> &'static str {
method fmt (line 898) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<...
FILE: tetanes-core/src/common.rs
constant SAVE_DIR (line 8) | pub const SAVE_DIR: &str = "save";
constant SRAM_DIR (line 10) | pub const SRAM_DIR: &str = "sram";
type ParseNesRegionError (line 15) | pub struct ParseNesRegionError;
type NesRegion (line 19) | pub enum NesRegion {
method as_slice (line 32) | pub const fn as_slice() -> &'static [Self] {
method is_auto (line 42) | pub const fn is_auto(&self) -> bool {
method is_ntsc (line 47) | pub const fn is_ntsc(&self) -> bool {
method is_pal (line 52) | pub const fn is_pal(&self) -> bool {
method is_dendy (line 57) | pub const fn is_dendy(&self) -> bool {
method aspect_ratio (line 62) | pub fn aspect_ratio(&self) -> f32 {
method as_str (line 71) | pub const fn as_str(&self) -> &'static str {
method fmt (line 82) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method as_ref (line 94) | fn as_ref(&self) -> &str {
type Error (line 100) | type Error = ParseNesRegionError;
method try_from (line 102) | fn try_from(value: &str) -> Result<Self, Self::Error> {
type Error (line 114) | type Error = ParseNesRegionError;
method try_from (line 116) | fn try_from(value: usize) -> Result<Self, Self::Error> {
type Regional (line 129) | pub trait Regional {
method region (line 131) | fn region(&self) -> NesRegion {
method set_region (line 136) | fn set_region(&mut self, _region: NesRegion) {}
type ResetKind (line 142) | pub enum ResetKind {
type Reset (line 150) | pub trait Reset {
method reset (line 152) | fn reset(&mut self, _kind: ResetKind) {}
type Clock (line 156) | pub trait Clock {
method clock (line 158) | fn clock(&mut self) {}
type Sample (line 162) | pub trait Sample {
method output (line 164) | fn output(&self) -> f32 {
type Sram (line 170) | pub trait Sram {
method save (line 172) | fn save(&self, _path: impl AsRef<Path>) -> crate::fs::Result<()> {
method load (line 177) | fn load(&mut self, _path: impl AsRef<Path>) -> crate::fs::Result<()> {
function hexdump (line 184) | pub fn hexdump(data: &[u8], addr_offset: usize) -> Vec<String> {
constant RESULT_DIR (line 263) | pub(crate) const RESULT_DIR: &str = "test_results";
type TestFrame (line 290) | struct TestFrame {
type RomTest (line 304) | struct RomTest {
function get_rom_tests (line 311) | fn get_rom_tests(directory: &str) -> anyhow::Result<(PathBuf, Vec<RomTes...
function load_control_deck (line 324) | fn load_control_deck<P: AsRef<Path>>(path: P) -> ControlDeck {
function on_frame_action (line 337) | fn on_frame_action(test_frame: &TestFrame, deck: &mut ControlDeck) {
function on_snapshot (line 362) | fn on_snapshot(
function test_rom (line 428) | pub(crate) fn test_rom(directory: &str, test_name: &str) -> anyhow::Resu...
FILE: tetanes-core/src/control_deck.rs
type Result (line 28) | pub type Result<T> = std::result::Result<T, Error>;
type Error (line 33) | pub enum Error {
method io (line 73) | pub fn io(source: std::io::Error, context: impl Into<String>) -> Self {
type MapperRevisionsConfig (line 96) | pub struct MapperRevisionsConfig {
method set (line 105) | pub const fn set(&mut self, rev: MapperRevision) {
type Config (line 117) | pub struct Config {
constant BASE_DIR (line 149) | pub const BASE_DIR: &'static str = "tetanes";
constant SRAM_DIR (line 151) | pub const SRAM_DIR: &'static str = "sram";
constant SRAM_EXTENSION (line 153) | pub const SRAM_EXTENSION: &'static str = "sram";
method default_data_dir (line 158) | pub fn default_data_dir() -> PathBuf {
method sram_dir (line 165) | pub fn sram_dir(&self) -> PathBuf {
method default (line 171) | fn default() -> Self {
type LoadedRom (line 191) | pub struct LoadedRom {
type ControlDeck (line 203) | pub struct ControlDeck {
method new (line 236) | pub fn new() -> Self {
method with_config (line 241) | pub fn with_config(cfg: Config) -> Self {
method sram_path (line 282) | pub fn sram_path(&self, name: &str) -> PathBuf {
method load_rom (line 293) | pub fn load_rom<S: ToString, F: Read>(&mut self, name: S, rom: &mut F)...
method load_rom_path (line 324) | pub fn load_rom_path(&mut self, path: impl AsRef<std::path::Path>) -> ...
method unload_rom (line 340) | pub fn unload_rom(&mut self) -> Result<()> {
method load_cpu (line 355) | pub fn load_cpu(&mut self, cpu: Cpu) {
method set_mapper_revision (line 361) | pub const fn set_mapper_revision(&mut self, rev: MapperRevision) {
method set_mapper_revisions (line 369) | pub const fn set_mapper_revisions(&mut self, revs: MapperRevisionsConf...
method update_mapper_revisions (line 376) | const fn update_mapper_revisions(&mut self) {
method set_concurrent_dpad (line 409) | pub fn set_concurrent_dpad(&mut self, enabled: bool) {
method set_ram_state (line 415) | pub const fn set_ram_state(&mut self, ram_state: RamState) {
method set_headless_mode (line 422) | pub const fn set_headless_mode(&mut self, mode: HeadlessMode) {
method set_emulate_ppu_warmup (line 432) | pub const fn set_emulate_ppu_warmup(&mut self, enabled: bool) {
method add_debugger (line 438) | pub fn add_debugger(&mut self, debugger: Debugger) {
method remove_debugger (line 445) | pub fn remove_debugger(&mut self, debugger: Debugger) {
method loaded_rom (line 454) | pub const fn loaded_rom(&self) -> Option<&LoadedRom> {
method cart_region (line 462) | pub fn cart_region(&self) -> Option<NesRegion> {
method cart_battery_backed (line 469) | pub fn cart_battery_backed(&self) -> Option<bool> {
method wram (line 476) | pub fn wram(&self) -> &[u8] {
method save_sram (line 485) | pub fn save_sram(&self, path: impl AsRef<Path>) -> Result<()> {
method load_sram (line 506) | pub fn load_sram(&mut self, path: impl AsRef<Path>) -> Result<()> {
method save_state (line 528) | pub fn save_state(&mut self, path: impl AsRef<Path>) -> Result<()> {
method load_state (line 541) | pub fn load_state(&mut self, path: impl AsRef<Path>) -> Result<()> {
method frame_buffer_raw (line 560) | pub fn frame_buffer_raw(&mut self) -> &[u16] {
method frame_buffer (line 566) | pub fn frame_buffer(&mut self) -> &[u8] {
method frame_buffer_into (line 580) | pub fn frame_buffer_into(&self, buffer: &mut [u8]) {
method frame_number (line 591) | pub const fn frame_number(&self) -> u32 {
method audio_samples (line 598) | pub fn audio_samples(&self) -> &[f32] {
method clear_audio_samples (line 604) | pub fn clear_audio_samples(&mut self) {
method clock_rate (line 611) | pub const fn clock_rate(&self) -> f32 {
method clock_instr (line 620) | pub fn clock_instr(&mut self) -> Result<()> {
method clock_seconds (line 634) | pub fn clock_seconds(&mut self, seconds: f32) -> Result<u32> {
method clock_seconds_output (line 653) | pub fn clock_seconds_output(
method clock_frame (line 679) | pub fn clock_frame(&mut self) -> Result<()> {
method clock_frame_output (line 712) | pub fn clock_frame_output<T>(
method clock_frame_into (line 733) | pub fn clock_frame_into(
method clock_frame_ahead (line 755) | pub fn clock_frame_ahead<T>(
method clock_frame_ahead_into (line 797) | pub fn clock_frame_ahead_into(
method clock_scanline (line 838) | pub fn clock_scanline(&mut self) -> Result<()> {
method cpu_corrupted (line 855) | pub const fn cpu_corrupted(&self) -> bool {
method cpu (line 861) | pub const fn cpu(&self) -> &Cpu {
method cpu_mut (line 867) | pub const fn cpu_mut(&mut self) -> &mut Cpu {
method ppu (line 873) | pub const fn ppu(&self) -> &Ppu {
method ppu_mut (line 879) | pub const fn ppu_mut(&mut self) -> &mut Ppu {
method bus (line 885) | pub const fn bus(&self) -> &Bus {
method bus_mut (line 891) | pub const fn bus_mut(&mut self) -> &mut Bus {
method apu (line 897) | pub const fn apu(&self) -> &Apu {
method apu_mut (line 903) | pub const fn apu_mut(&mut self) -> &Apu {
method mapper (line 909) | pub const fn mapper(&self) -> &Mapper {
method mapper_mut (line 915) | pub const fn mapper_mut(&mut self) -> &mut Mapper {
method four_player (line 921) | pub const fn four_player(&self) -> FourPlayer {
method set_four_player (line 927) | pub fn set_four_player(&mut self, four_player: FourPlayer) {
method joypad (line 933) | pub const fn joypad(&mut self, slot: Player) -> &Joypad {
method joypad_mut (line 939) | pub const fn joypad_mut(&mut self, slot: Player) -> &mut Joypad {
method zapper_connected (line 945) | pub const fn zapper_connected(&self) -> bool {
method connect_zapper (line 951) | pub const fn connect_zapper(&mut self, enabled: bool) {
method zapper_pos (line 958) | pub const fn zapper_pos(&self) -> (u16, u16) {
method trigger_zapper (line 965) | pub fn trigger_zapper(&mut self) {
method aim_zapper (line 971) | pub const fn aim_zapper(&mut self, x: u16, y: u16) {
method set_filter (line 977) | pub const fn set_filter(&mut self, filter: VideoFilter) {
method set_sample_rate (line 983) | pub fn set_sample_rate(&mut self, sample_rate: f32) {
method set_frame_speed (line 989) | pub fn set_frame_speed(&mut self, speed: f32) {
method add_genie_code (line 1000) | pub fn add_genie_code(&mut self, genie_code: String) -> Result<()> {
method remove_genie_code (line 1007) | pub fn remove_genie_code(&mut self, genie_code: &str) {
method clear_genie_codes (line 1013) | pub fn clear_genie_codes(&mut self) {
method channel_enabled (line 1020) | pub const fn channel_enabled(&self, channel: Channel) -> bool {
method set_apu_channel_enabled (line 1026) | pub const fn set_apu_channel_enabled(&mut self, channel: Channel, enab...
method toggle_apu_channel (line 1032) | pub const fn toggle_apu_channel(&mut self, channel: Channel) {
method is_running (line 1039) | pub const fn is_running(&self) -> bool {
method default (line 229) | fn default() -> Self {
method clock (line 1047) | fn clock(&mut self) {
method region (line 1054) | fn region(&self) -> NesRegion {
method set_region (line 1059) | fn set_region(&mut self, region: NesRegion) {
method reset (line 1071) | fn reset(&mut self, kind: ResetKind) {
FILE: tetanes-core/src/cpu.rs
type Cpu (line 73) | pub struct Cpu {
constant NTSC_MASTER_CLOCK_RATE (line 97) | const NTSC_MASTER_CLOCK_RATE: f32 = 21_477_272.0;
constant NTSC_CPU_CLOCK_RATE (line 98) | const NTSC_CPU_CLOCK_RATE: f32 = Self::NTSC_MASTER_CLOCK_RATE / 12.0;
constant PAL_MASTER_CLOCK_RATE (line 99) | const PAL_MASTER_CLOCK_RATE: f32 = 26_601_712.0;
constant PAL_CPU_CLOCK_RATE (line 100) | const PAL_CPU_CLOCK_RATE: f32 = Self::PAL_MASTER_CLOCK_RATE / 16.0;
constant DENDY_CPU_CLOCK_RATE (line 101) | const DENDY_CPU_CLOCK_RATE: f32 = Self::PAL_MASTER_CLOCK_RATE / 15.0;
constant PPU_OFFSET (line 106) | const PPU_OFFSET: u32 = 1;
constant NMI_VECTOR (line 108) | const NMI_VECTOR: u16 = 0xFFFA;
constant IRQ_VECTOR (line 109) | const IRQ_VECTOR: u16 = 0xFFFE;
constant RESET_VECTOR (line 110) | const RESET_VECTOR: u16 = 0xFFFC;
constant POWER_ON_STATUS (line 111) | const POWER_ON_STATUS: Status = Status::U.union(Status::I);
constant POWER_ON_SP (line 112) | const POWER_ON_SP: u8 = 0xFD;
constant SP_BASE (line 113) | const SP_BASE: u16 = 0x0100;
method new (line 116) | pub fn new(bus: Bus) -> Self {
method load (line 141) | pub fn load(&mut self, mut cpu: Self) {
method region_clock_rate (line 150) | pub const fn region_clock_rate(region: NesRegion) -> f32 {
method clock_rate (line 161) | pub const fn clock_rate(&self) -> f32 {
method next_instr (line 167) | pub fn next_instr(&self) -> InstrRef {
method start_oam_dma (line 174) | pub fn start_oam_dma(&mut self, addr: u16) {
method irq (line 193) | pub fn irq(&mut self) {
method handle_interrupts (line 232) | fn handle_interrupts(&mut self) {
method start_cycle (line 286) | fn start_cycle(&mut self, increment: u8) {
method end_cycle (line 295) | fn end_cycle(&mut self, increment: u8) {
method start_dma_cycle (line 304) | fn start_dma_cycle(&mut self) {
method handle_dma (line 317) | fn handle_dma(&mut self, addr: u16) {
method clear_irq_flags (line 398) | fn clear_irq_flags(&mut self, flags: IrqFlags) {
method irq_flags (line 404) | fn irq_flags(&self, flags: IrqFlags) -> bool {
method set_status (line 412) | fn set_status(&mut self, status: Status) {
method status_bit (line 418) | const fn status_bit(&self, reg: Status) -> u8 {
method set_acc (line 424) | fn set_acc(&mut self, val: u8) {
method set_x (line 431) | fn set_x(&mut self, val: u8) {
method set_y (line 438) | fn set_y(&mut self, val: u8) {
method set_sp (line 445) | const fn set_sp(&mut self, val: u8) {
method set_zn_status (line 451) | fn set_zn_status(&mut self, val: u8) {
method push_byte (line 460) | fn push_byte(&mut self, val: u8) {
method pop_byte (line 468) | fn pop_byte(&mut self) -> u8 {
method peek_stack (line 476) | pub fn peek_stack(&self) -> u8 {
method peek_stack_u16 (line 483) | pub fn peek_stack_u16(&self) -> u16 {
method push_word (line 491) | fn push_word(&mut self, val: u16) {
method pop_word (line 499) | fn pop_word(&mut self) -> u16 {
method fetch_byte (line 510) | fn fetch_byte(&mut self) -> u8 {
method fetch_operand (line 519) | fn fetch_operand(&mut self) -> u16 {
method fetch_word (line 541) | fn fetch_word(&mut self) -> u16 {
method read_operand (line 550) | fn read_operand(&mut self) -> u8 {
method read_word (line 564) | pub fn read_word(&mut self, addr: u16) -> u16 {
method peek_word (line 573) | pub fn peek_word(&self, addr: u16) -> u16 {
method disassemble (line 580) | pub fn disassemble(&mut self, pc: &mut u16) -> &str {
method trace_instr (line 739) | pub fn trace_instr(&mut self) {
method pages_differ (line 768) | const fn pages_differ(addr1: u16, addr2: u16) -> bool {
method page_crossed (line 775) | const fn page_crossed(addr: u16, offset: i16) -> bool {
method clock_sync (line 781) | pub fn clock_sync(&mut self) {
method fmt (line 909) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), f...
method clock (line 792) | fn clock(&mut self) {
method read (line 813) | fn read(&mut self, addr: u16) -> u8 {
method peek (line 824) | fn peek(&self, addr: u16) -> u8 {
method write (line 831) | fn write(&mut self, addr: u16, val: u8) {
method region (line 844) | fn region(&self) -> NesRegion {
method set_region (line 848) | fn set_region(&mut self, region: NesRegion) {
method reset (line 867) | fn reset(&mut self, kind: ResetKind) {
function cycle_timing (line 929) | fn cycle_timing() {
FILE: tetanes-core/src/cpu/instr.rs
type Instr (line 19) | pub enum Instr {
type AddrMode (line 33) | pub enum AddrMode {
type Op (line 51) | pub struct Op {
method run (line 58) | pub fn run(&self, cpu: &mut Cpu) {
method addr_mode (line 63) | pub const fn addr_mode(&self) -> AddrMode {
type InstrRef (line 80) | pub struct InstrRef {
method fmt (line 88) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<...
constant OPS (line 136) | pub const OPS: [Op; 256] = [
constant INSTR_REF (line 158) | pub const INSTR_REF: [InstrRef; 256] = [
method acc_imp (line 203) | pub fn acc_imp(&mut self) -> u16 {
method imm_rel_zp (line 294) | pub fn imm_rel_zp(&mut self) -> u16 {
method zpx (line 353) | pub fn zpx(&mut self) -> u16 {
method zpy (line 396) | pub fn zpy(&mut self) -> u16 {
method abs (line 449) | pub fn abs(&mut self) -> u16 {
method abx (line 525) | pub fn abx(&mut self, dummy_read: bool) -> u16 {
method aby (line 607) | pub fn aby(&mut self, dummy_read: bool) -> u16 {
method ind (line 641) | pub fn ind(&mut self) -> u16 {
method idx (line 707) | pub fn idx(&mut self) -> u16 {
method idy (line 792) | pub fn idy(&mut self, dummy_read: bool) -> u16 {
method lda (line 815) | pub fn lda(&mut self) {
method ldx (line 821) | pub fn ldx(&mut self) {
method ldy (line 827) | pub fn ldy(&mut self) {
method sta (line 834) | pub fn sta(&mut self) {
method stx (line 839) | pub fn stx(&mut self) {
method sty (line 844) | pub fn sty(&mut self) {
method tax (line 850) | pub fn tax(&mut self) {
method tay (line 855) | pub fn tay(&mut self) {
method tsx (line 860) | pub fn tsx(&mut self) {
method txa (line 865) | pub fn txa(&mut self) {
method txs (line 870) | pub const fn txs(&mut self) {
method tya (line 875) | pub fn tya(&mut self) {
method adc (line 883) | pub fn adc(&mut self) {
method sbc (line 889) | pub fn sbc(&mut self) {
method add (line 895) | fn add(&mut self, val: u8) {
method inc (line 909) | pub fn inc(&mut self) {
method dec (line 919) | pub fn dec(&mut self) {
method inx (line 930) | pub fn inx(&mut self) {
method iny (line 935) | pub fn iny(&mut self) {
method dex (line 941) | pub fn dex(&mut self) {
method dey (line 947) | pub fn dey(&mut self) {
method and (line 955) | pub fn and(&mut self) {
method eor (line 961) | pub fn eor(&mut self) {
method ora (line 967) | pub fn ora(&mut self) {
method asla (line 974) | fn asla(&mut self) {
method aslm (line 980) | fn aslm(&mut self) {
method asl (line 989) | fn asl(&mut self, val: u8) -> u8 {
method lsra (line 998) | pub fn lsra(&mut self) {
method lsrm (line 1004) | pub fn lsrm(&mut self) {
method lsr (line 1013) | fn lsr(&mut self, val: u8) -> u8 {
method rola (line 1022) | pub fn rola(&mut self) {
method rolm (line 1028) | pub fn rolm(&mut self) {
method rol (line 1037) | pub fn rol(&mut self, val: u8) -> u8 {
method rora (line 1047) | pub fn rora(&mut self) {
method rorm (line 1053) | pub fn rorm(&mut self) {
method ror (line 1062) | fn ror(&mut self, val: u8) -> u8 {
method bit (line 1072) | pub fn bit(&mut self) {
method bcc (line 1083) | pub fn bcc(&mut self) {
method bcs (line 1088) | pub fn bcs(&mut self) {
method beq (line 1093) | pub fn beq(&mut self) {
method bmi (line 1098) | pub fn bmi(&mut self) {
method bne (line 1103) | pub fn bne(&mut self) {
method bpl (line 1108) | pub fn bpl(&mut self) {
method bvc (line 1113) | pub fn bvc(&mut self) {
method bvs (line 1118) | pub fn bvs(&mut self) {
method branch (line 1123) | fn branch(&mut self, branch: bool) {
method jmpa (line 1156) | pub const fn jmpa(&mut self) {
method jmpi (line 1173) | pub fn jmpi(&mut self) {
method jsr (line 1197) | pub fn jsr(&mut self) {
method rti (line 1219) | pub fn rti(&mut self) {
method rts (line 1239) | pub fn rts(&mut self) {
method clc (line 1250) | pub fn clc(&mut self) {
method sec (line 1255) | pub fn sec(&mut self) {
method cld (line 1260) | pub fn cld(&mut self) {
method sed (line 1265) | pub fn sed(&mut self) {
method cli (line 1270) | pub fn cli(&mut self) {
method sei (line 1275) | pub fn sei(&mut self) {
method clv (line 1280) | pub fn clv(&mut self) {
method cpa (line 1288) | pub fn cpa(&mut self) {
method cpx (line 1294) | pub fn cpx(&mut self) {
method cpy (line 1300) | pub fn cpy(&mut self) {
method cmp (line 1306) | fn cmp(&mut self, reg: u8, val: u8) {
method php (line 1324) | pub fn php(&mut self) {
method plp (line 1340) | pub fn plp(&mut self) {
method pha (line 1356) | pub fn pha(&mut self) {
method pla (line 1371) | pub fn pla(&mut self) {
method brk (line 1394) | pub fn brk(&mut self) {
method nop (line 1439) | pub fn nop(&mut self) {
method hlt (line 1447) | pub fn hlt(&mut self) {
method isb (line 1465) | pub fn isb(&mut self) {
method dcp (line 1478) | pub fn dcp(&mut self) {
method atx (line 1491) | pub fn atx(&mut self) {
method axs (line 1499) | pub fn axs(&mut self) {
method las (line 1509) | pub fn las(&mut self) {
method lax (line 1518) | pub fn lax(&mut self) {
method sya (line 1528) | pub fn sya(&mut self) {
method sxa (line 1535) | pub fn sxa(&mut self) {
method shaa (line 1542) | pub fn shaa(&mut self) {
method shaz (line 1549) | pub fn shaz(&mut self) {
method sya_sxa_axa (line 1559) | fn sya_sxa_axa(&mut self, base_addr: u16, index_reg: u8, val_reg: u8) {
method sax (line 1586) | pub fn sax(&mut self) {
method xaa (line 1592) | pub fn xaa(&mut self) {
method rra (line 1599) | pub fn rra(&mut self) {
method tas (line 1612) | pub fn tas(&mut self) {
method arr (line 1621) | pub fn arr(&mut self) {
method sre (line 1634) | pub fn sre(&mut self) {
method alr (line 1647) | pub fn alr(&mut self) {
method rla (line 1656) | pub fn rla(&mut self) {
method anc (line 1669) | pub fn anc(&mut self) {
method slo (line 1677) | pub fn slo(&mut self) {
FILE: tetanes-core/src/debug.rs
type Debugger (line 6) | pub enum Debugger {
method from (line 11) | fn from(debugger: PpuDebugger) -> Self {
type PpuDebugger (line 18) | pub struct PpuDebugger {
method fmt (line 41) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method default (line 25) | fn default() -> Self {
method eq (line 35) | fn eq(&self, other: &Self) -> bool {
FILE: tetanes-core/src/error.rs
type Result (line 6) | pub type Result<T> = std::result::Result<T, Error>;
type Error (line 10) | pub enum Error {
method io (line 30) | pub fn io(source: std::io::Error, context: impl Into<String>) -> Self {
FILE: tetanes-core/src/fs.rs
constant SAVE_FILE_MAGIC_LEN (line 13) | const SAVE_FILE_MAGIC_LEN: usize = 8;
constant SAVE_FILE_MAGIC (line 14) | const SAVE_FILE_MAGIC: [u8; SAVE_FILE_MAGIC_LEN] = *b"TETANES\x1a";
constant SAVE_VERSION (line 16) | const SAVE_VERSION: &str = "1";
type Result (line 18) | pub type Result<T> = std::result::Result<T, Error>;
type Error (line 22) | pub enum Error {
method io (line 47) | pub fn io(source: std::io::Error, context: impl Into<String>) -> Self {
method custom (line 54) | pub fn custom(error: impl Into<String>) -> Self {
function write_header (line 64) | pub(crate) fn write_header(f: &mut impl Write) -> std::io::Result<()> {
function validate_header (line 74) | pub(crate) fn validate_header(f: &mut impl Read) -> Result<()> {
function encode (line 96) | pub fn encode(mut writer: &mut impl Write, data: &[u8]) -> std::io::Resu...
function decode (line 103) | pub fn decode(data: impl Read) -> std::io::Result<Vec<u8>> {
function save (line 110) | pub fn save<T>(path: impl AsRef<Path>, value: &T) -> Result<()>
function save_raw (line 126) | pub fn save_raw(path: impl AsRef<Path>, value: &[u8]) -> Result<()> {
function load (line 137) | pub fn load<T>(path: impl AsRef<Path>) -> Result<T>
function load_bytes (line 150) | pub fn load_bytes<T>(bytes: &[u8]) -> Result<T>
function load_raw (line 163) | pub fn load_raw(path: impl AsRef<Path>) -> Result<Vec<u8>> {
function clear_dir (line 172) | pub fn clear_dir(path: impl AsRef<Path>) -> Result<()> {
function exists (line 176) | pub fn exists(path: &Path) -> bool {
function filename (line 180) | pub fn filename(path: &Path) -> &str {
function compute_crc32 (line 189) | pub fn compute_crc32(data: &[u8]) -> u32 {
function compute_combine_crc32 (line 193) | pub fn compute_combine_crc32(crc32: u32, data: &[u8]) -> u32 {
function compute_crc32_buffer (line 198) | fn compute_crc32_buffer(crc32: u32, buffer: &[u8]) -> u32 {
constant CRC_TABLE (line 204) | const CRC_TABLE: [u32; 256] = [
function save_header (line 244) | fn save_header() {
function crc32 (line 254) | fn crc32() {
FILE: tetanes-core/src/genie.rs
type Result (line 9) | pub type Result<T> = std::result::Result<T, Error>;
type Error (line 13) | pub struct Error {
method new (line 19) | fn new(code: impl Into<String>, kind: ErrorKind) -> Self {
method kind (line 26) | pub const fn kind(&self) -> ErrorKind {
type ErrorKind (line 33) | pub enum ErrorKind {
type GenieCode (line 42) | pub struct GenieCode {
method new (line 55) | pub fn new(code: String) -> Result<Self> {
method from_raw (line 62) | pub fn from_raw(code: String, hex: &[u8]) -> Self {
method generate_genie_map (line 89) | fn generate_genie_map() -> HashMap<char, u8> {
method parse (line 111) | pub fn parse(code: &str) -> Result<Box<[u8]>> {
method code (line 131) | pub fn code(&self) -> &str {
method addr (line 136) | pub const fn addr(&self) -> u16 {
method read (line 141) | pub const fn read(&self, val: u8) -> u8 {
method fmt (line 151) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
FILE: tetanes-core/src/input.rs
type ParsePlayerError (line 17) | pub struct ParsePlayerError;
type Player (line 21) | pub enum Player {
method fmt (line 30) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method as_ref (line 42) | fn as_ref(&self) -> &str {
type Error (line 53) | type Error = ParsePlayerError;
method try_from (line 55) | fn try_from(value: usize) -> Result<Self, Self::Error> {
type InputRegisters (line 66) | pub trait InputRegisters {
method read (line 67) | fn read(&mut self, player: Player, ppu: &Ppu) -> u8;
method peek (line 68) | fn peek(&self, player: Player, ppu: &Ppu) -> u8;
method write (line 69) | fn write(&mut self, val: u8);
method read (line 187) | fn read(&mut self, player: Player, ppu: &Ppu) -> u8 {
method peek (line 220) | fn peek(&self, player: Player, ppu: &Ppu) -> u8 {
method write (line 253) | fn write(&mut self, val: u8) {
type FourPlayer (line 74) | pub enum FourPlayer {
method as_slice (line 82) | pub const fn as_slice() -> &'static [Self] {
method as_str (line 86) | pub const fn as_str(&self) -> &'static str {
method as_ref (line 96) | fn as_ref(&self) -> &str {
method fmt (line 102) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Err (line 113) | type Err = &'static str;
method from_str (line 114) | fn from_str(s: &str) -> Result<Self, Self::Err> {
type Input (line 128) | pub struct Input {
method new (line 137) | pub fn new(region: NesRegion) -> Self {
method joypad (line 151) | pub const fn joypad(&self, player: Player) -> &Joypad {
method joypad_mut (line 155) | pub const fn joypad_mut(&mut self, player: Player) -> &mut Joypad {
method set_region (line 159) | pub fn set_region(&mut self, region: NesRegion) {
method set_concurrent_dpad (line 163) | pub fn set_concurrent_dpad(&mut self, enabled: bool) {
method connect_zapper (line 169) | pub const fn connect_zapper(&mut self, connected: bool) {
method set_four_player (line 173) | pub fn set_four_player(&mut self, four_player: FourPlayer) {
method clear (line 178) | pub fn clear(&mut self) {
method clock (line 264) | fn clock(&mut self) {
method reset (line 287) | fn reset(&mut self, kind: ResetKind) {
type JoypadBtn (line 298) | pub enum JoypadBtn {
method as_ref (line 322) | fn as_ref(&self) -> &str {
method from (line 356) | fn from(button: JoypadBtn) -> Self {
type Joypad (line 374) | pub struct Joypad {
method new (line 382) | pub const fn new() -> Self {
method button (line 392) | pub const fn button(&self, button: JoypadBtnState) -> bool {
method set_button (line 396) | pub fn set_button(&mut self, button: impl Into<JoypadBtnState>, presse...
method from_bytes (line 413) | pub const fn from_bytes(val: u16) -> Self {
method read (line 423) | pub const fn read(&mut self) -> u8 {
method peek (line 432) | pub const fn peek(&self) -> u8 {
method write (line 440) | pub const fn write(&mut self, val: u8) {
method index (line 449) | pub const fn index(&self) -> u8 {
method clear (line 453) | pub const fn clear(&mut self) {
method reset (line 459) | fn reset(&mut self, _kind: ResetKind) {
type Zapper (line 468) | pub struct Zapper {
method x (line 483) | pub const fn x(&self) -> u16 {
method y (line 489) | pub const fn y(&self) -> u16 {
method trigger (line 494) | pub fn trigger(&mut self) {
method aim (line 501) | pub const fn aim(&mut self, x: u16, y: u16) {
method clear (line 506) | pub const fn clear(&mut self) {
method new (line 512) | fn new(region: NesRegion) -> Self {
method read (line 525) | fn read(&self, ppu: &Ppu) -> u8 {
method triggered (line 533) | fn triggered(&self) -> u8 {
method light_sense (line 537) | fn light_sense(&self, ppu: &Ppu) -> u8 {
method clock (line 562) | fn clock(&mut self) {
method reset (line 570) | fn reset(&mut self, _kind: ResetKind) {
FILE: tetanes-core/src/lib.rs
function print_layouts (line 105) | fn print_layouts() {
FILE: tetanes-core/src/mapper.rs
type Error (line 59) | pub enum Error {
type MapperRevision (line 68) | pub enum MapperRevision {
method fmt (line 77) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Mapper (line 97) | pub enum Mapper {
method none (line 343) | pub const fn none() -> Self {
method is_none (line 348) | pub const fn is_none(&self) -> bool {
method output (line 291) | fn output(&self) -> f32 {
method reset (line 304) | fn reset(&mut self, kind: ResetKind) {
method clock (line 312) | fn clock(&mut self) {
method region (line 319) | fn region(&self) -> NesRegion {
method set_region (line 324) | fn set_region(&mut self, region: NesRegion) {
method save (line 331) | fn save(&self, path: impl AsRef<Path>) -> fs::Result<()> {
method load (line 336) | fn load(&mut self, path: impl AsRef<Path>) -> fs::Result<()> {
method default (line 354) | fn default() -> Self {
type Map (line 360) | pub trait Map: Clock + Regional + Reset + Sram {
method chr_read (line 222) | fn chr_read(&mut self, addr: u16, ciram: &CIRam) -> u8 {
method chr_peek (line 228) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_read (line 234) | fn prg_read(&mut self, addr: u16) -> u8 {
method prg_peek (line 240) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 246) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 252) | fn prg_write(&mut self, addr: u16, val: u8) {
method ppu_read (line 257) | fn ppu_read(&mut self, addr: u16) {
method ppu_write (line 262) | fn ppu_write(&mut self, addr: u16, val: u8) {
method irq_pending (line 267) | fn irq_pending(&self) -> bool {
method dma_pending (line 272) | fn dma_pending(&self) -> bool {
method clear_dma_pending (line 277) | fn clear_dma_pending(&mut self) {
method mirroring (line 283) | fn mirroring(&self) -> Mirroring {
method chr_read (line 363) | fn chr_read(&mut self, addr: u16, ciram: &CIRam) -> u8 {
method chr_peek (line 369) | fn chr_peek(&self, _addr: u16, _ciram: &CIRam) -> u8;
method prg_read (line 375) | fn prg_read(&mut self, addr: u16) -> u8 {
method prg_peek (line 381) | fn prg_peek(&self, _addr: u16) -> u8;
method chr_write (line 386) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 393) | fn prg_write(&mut self, _addr: u16, _val: u8) {}
method ppu_read (line 396) | fn ppu_read(&mut self, _addr: u16) {}
method ppu_write (line 399) | fn ppu_write(&mut self, _addr: u16, _val: u8) {}
method irq_pending (line 402) | fn irq_pending(&self) -> bool {
method clear_dma_pending (line 407) | fn clear_dma_pending(&mut self) {}
method dma_pending (line 410) | fn dma_pending(&self) -> bool {
method mirroring (line 416) | fn mirroring(&self) -> Mirroring;
method chr_peek (line 420) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 427) | fn prg_peek(&self, _addr: u16) -> u8 {
method mirroring (line 431) | fn mirroring(&self) -> Mirroring {
FILE: tetanes-core/src/mapper/bandai_fcg.rs
type Regs (line 19) | pub struct Regs {
type MemoryOp (line 34) | pub enum MemoryOp {
type BandaiFCG (line 45) | pub struct BandaiFCG {
constant PRG_WINDOW (line 64) | const PRG_WINDOW: usize = 16 * 1024;
constant PRG_RAM_SIZE (line 65) | const PRG_RAM_SIZE: usize = 8 * 1024;
constant CHR_ROM_WINDOW (line 66) | const CHR_ROM_WINDOW: usize = 1024;
constant CHR_RAM_SIZE (line 67) | const CHR_RAM_SIZE: usize = 8 * 1024;
method load (line 69) | pub fn load(
method write_chr_bank (line 161) | fn write_chr_bank(&mut self, addr: u16, val: u8) {
method write_prg_bank (line 184) | fn write_prg_bank(&mut self, val: u8) {
method write_mirroring (line 190) | const fn write_mirroring(&mut self, val: u8) {
method write_irq_ctrl (line 199) | const fn write_irq_ctrl(&mut self, val: u8) {
method write_irq_latch (line 213) | fn write_irq_latch(&mut self, addr: u16, val: u8) {
method write_eeprom_ctrl (line 231) | fn write_eeprom_ctrl(&mut self, val: u8) {
method prg_ram_enabled (line 243) | pub const fn prg_ram_enabled(&self) -> bool {
method chr_peek (line 290) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_read (line 300) | fn prg_read(&mut self, addr: u16) -> u8 {
method prg_peek (line 326) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 336) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 346) | fn prg_write(&mut self, addr: u16, val: u8) {
method irq_pending (line 371) | fn irq_pending(&self) -> bool {
method mirroring (line 377) | fn mirroring(&self) -> Mirroring {
method clock (line 383) | fn clock(&mut self) {
method save (line 400) | fn save(&self, path: impl AsRef<Path>) -> fs::Result<()> {
method load (line 410) | fn load(&mut self, path: impl AsRef<Path>) -> fs::Result<()> {
type BarcodeReader (line 426) | pub struct BarcodeReader {
method new (line 435) | pub fn new() -> Self {
method read (line 445) | pub const fn read(&self) -> u8 {
method input (line 455) | pub const fn input(&mut self, barcode: u64, digit_count: u32) {
method barcode (line 460) | pub fn barcode(&self) -> String {
method init (line 468) | pub fn init(&mut self) {
method clock (line 592) | fn clock(&mut self) {
type EepromModel (line 599) | pub enum EepromModel {
type EepromMode (line 606) | pub enum EepromMode {
type Eeprom (line 619) | pub struct Eeprom {
method new (line 634) | pub fn new(model: EepromModel) -> Self {
method read (line 654) | pub const fn read(&self) -> u8 {
method write (line 658) | pub fn write(&mut self, scl: u8, sda: u8) {
method write_scl (line 849) | pub fn write_scl(&mut self, scl: u8) {
method write_sda (line 853) | pub fn write_sda(&mut self, sda: u8) {
method write_bit (line 857) | pub const fn write_bit(&mut self, dest: u8, val: u8) -> Option<u8> {
method read_bit (line 868) | pub const fn read_bit(&mut self) {
method sram_extension (line 879) | pub const fn sram_extension(&self) -> &str {
method save (line 888) | fn save(&self, path: impl AsRef<Path>) -> fs::Result<()> {
method load (line 893) | fn load(&mut self, path: impl AsRef<Path>) -> fs::Result<()> {
function bandai_fcg_barcode_formatting (line 904) | fn bandai_fcg_barcode_formatting() {
function bandai_fcg_ean13_checksum (line 915) | fn bandai_fcg_ean13_checksum() {
function bandai_fcg_ean13_structure (line 931) | fn bandai_fcg_ean13_structure() {
function bandai_fcg_ean8_structure (line 951) | fn bandai_fcg_ean8_structure() {
FILE: tetanes-core/src/mapper/m000_nrom.rs
type Nrom (line 17) | pub struct Nrom {
constant PRG_RAM_SIZE (line 28) | const PRG_RAM_SIZE: usize = 8 * 1024;
constant CHR_RAM_SIZE (line 29) | const CHR_RAM_SIZE: usize = 8 * 1024;
method load (line 32) | pub fn load(
method chr_peek (line 62) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 72) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 86) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 96) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 104) | fn mirroring(&self) -> Mirroring {
method reset (line 110) | fn reset(&mut self, kind: ResetKind) {
FILE: tetanes-core/src/mapper/m001_sxrom.rs
type Revision (line 20) | pub enum Revision {
type Regs (line 31) | pub struct Regs {
method fmt (line 403) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Sxrom (line 48) | pub struct Sxrom {
constant PRG_RAM_WINDOW (line 64) | const PRG_RAM_WINDOW: usize = 8 * 1024;
constant PRG_ROM_WINDOW (line 65) | const PRG_ROM_WINDOW: usize = 16 * 1024;
constant CHR_WINDOW (line 66) | const CHR_WINDOW: usize = 4 * 1024;
constant PRG_RAM_SIZE (line 67) | const PRG_RAM_SIZE: usize = 32 * 1024;
constant CHR_RAM_SIZE (line 68) | const CHR_RAM_SIZE: usize = 8 * 1024;
constant SHIFT_REG_RESET (line 70) | const SHIFT_REG_RESET: u8 = 0x80;
constant MIRRORING_MASK (line 71) | const MIRRORING_MASK: u8 = 0x03;
constant SLOT_SELECT_MASK (line 72) | const SLOT_SELECT_MASK: u8 = 0x04;
constant PRG_MODE_MASK (line 73) | const PRG_MODE_MASK: u8 = 0x08;
constant CHR_MODE_MASK (line 74) | const CHR_MODE_MASK: u8 = 0x10;
constant DEFAULT_PRG_MODE (line 76) | const DEFAULT_PRG_MODE: u8 = 0x0C;
constant CHR_BANK_MASK (line 77) | const CHR_BANK_MASK: u8 = 0x1F;
constant PRG_BANK_MASK (line 78) | const PRG_BANK_MASK: u8 = 0x0F;
constant PRG_RAM_DISABLED (line 79) | const PRG_RAM_DISABLED: u8 = 0x10;
method load (line 82) | pub fn load(
method reset_buffer (line 136) | const fn reset_buffer(&mut self) {
method process_register_write (line 142) | const fn process_register_write(&mut self, addr: u16, val: u8) {
method update_state (line 172) | pub fn update_state(&mut self) {
method prg_ram_enabled (line 213) | pub fn prg_ram_enabled(&self) -> bool {
method set_revision (line 217) | pub const fn set_revision(&mut self, revision: Revision) {
method fmt (line 387) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method chr_peek (line 230) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 240) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 252) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 262) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 346) | fn mirroring(&self) -> Mirroring {
method reset (line 352) | fn reset(&mut self, kind: ResetKind) {
method clock (line 365) | fn clock(&mut self) {
method save (line 374) | fn save(&self, path: impl AsRef<Path>) -> fs::Result<()> {
method load (line 379) | fn load(&mut self, path: impl AsRef<Path>) -> fs::Result<()> {
FILE: tetanes-core/src/mapper/m002_uxrom.rs
type Uxrom (line 17) | pub struct Uxrom {
constant PRG_ROM_WINDOW (line 26) | const PRG_ROM_WINDOW: usize = 16 * 1024;
constant CHR_RAM_SIZE (line 27) | const CHR_RAM_SIZE: usize = 8 * 1024;
method load (line 30) | pub fn load(
method chr_peek (line 56) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 66) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 75) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 85) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 93) | fn mirroring(&self) -> Mirroring {
FILE: tetanes-core/src/mapper/m003_cnrom.rs
type Cnrom (line 18) | pub struct Cnrom {
constant CHR_ROM_WINDOW (line 27) | const CHR_ROM_WINDOW: usize = 8 * 1024;
method load (line 30) | pub fn load(
method chr_peek (line 55) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 65) | fn prg_peek(&self, addr: u16) -> u8 {
method prg_write (line 78) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 86) | fn mirroring(&self) -> Mirroring {
FILE: tetanes-core/src/mapper/m004_txrom.rs
type Revision (line 37) | pub enum Revision {
type Regs (line 50) | pub struct Regs {
type Txrom (line 65) | pub struct Txrom {
constant PRG_WINDOW (line 82) | const PRG_WINDOW: usize = 8 * 1024;
constant CHR_WINDOW (line 83) | const CHR_WINDOW: usize = 1024;
constant CHR_WINDOW_76 (line 84) | const CHR_WINDOW_76: usize = 2048;
constant FOUR_SCREEN_RAM_SIZE (line 86) | const FOUR_SCREEN_RAM_SIZE: usize = 4 * 1024;
constant PRG_RAM_SIZE (line 87) | const PRG_RAM_SIZE: usize = 8 * 1024;
constant CHR_RAM_SIZE (line 88) | const CHR_RAM_SIZE: usize = 8 * 1024;
constant PRG_MODE_MASK (line 90) | const PRG_MODE_MASK: u8 = 0x40;
constant CHR_INVERSION_MASK (line 91) | const CHR_INVERSION_MASK: u8 = 0x80;
method new (line 94) | pub fn new(
method load (line 131) | pub fn load(
method bank_register (line 149) | pub const fn bank_register(&self, index: usize) -> u8 {
method set_revision (line 153) | pub const fn set_revision(&mut self, rev: Revision) {
method apply_prg_write_masks (line 158) | const fn apply_prg_write_masks(&self, addr: &mut u16, val: &mut u8) {
method update_prg_banks (line 171) | pub fn update_prg_banks(&mut self) {
method set_chr_banks (line 187) | pub fn set_chr_banks(&mut self, f: impl Fn(&mut Banks, &mut [u8])) {
method update_chr_banks (line 191) | pub fn update_chr_banks(&mut self) {
method update_banks (line 235) | pub fn update_banks(&mut self) {
method is_a12_rising_edge (line 240) | const fn is_a12_rising_edge(&mut self, addr: u16) -> bool {
method chr_read (line 273) | fn chr_read(&mut self, addr: u16, ciram: &CIRam) -> u8 {
method chr_peek (line 280) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 296) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 306) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 321) | fn prg_write(&mut self, mut addr: u16, mut val: u8) {
method ppu_read (line 410) | fn ppu_read(&mut self, addr: u16) {
method irq_pending (line 434) | fn irq_pending(&self) -> bool {
method mirroring (line 440) | fn mirroring(&self) -> Mirroring {
method reset (line 446) | fn reset(&mut self, _kind: ResetKind) {
method clock (line 454) | fn clock(&mut self) {
method save (line 461) | fn save(&self, path: impl AsRef<Path>) -> fs::Result<()> {
method load (line 466) | fn load(&mut self, path: impl AsRef<Path>) -> fs::Result<()> {
FILE: tetanes-core/src/mapper/m005_exrom.rs
type PrgMode (line 28) | pub enum PrgMode {
type ChrMode (line 38) | pub enum ChrMode {
type ChrBank (line 48) | pub enum ChrBank {
type ExRamMode (line 66) | pub struct ExRamMode {
method new (line 80) | pub const fn new() -> Self {
method set (line 89) | pub const fn set(&mut self, val: u8) {
method default (line 74) | fn default() -> Self {
type Nametable (line 105) | pub enum Nametable {
type NametableMapping (line 115) | pub struct NametableMapping {
method new (line 127) | pub const fn new() -> Self {
method set (line 134) | pub fn set(&mut self, val: u8) {
method default (line 121) | fn default() -> Self {
type Fill (line 154) | pub struct Fill {
method new (line 166) | pub const fn new() -> Self {
method default (line 160) | fn default() -> Self {
type Side (line 177) | pub enum Side {
type VSplit (line 185) | pub struct VSplit {
method new (line 202) | pub const fn new() -> Self {
method default (line 196) | fn default() -> Self {
type Regs (line 218) | pub struct Regs {
method new (line 244) | pub const fn new() -> Self {
method default (line 238) | fn default() -> Self {
type IrqState (line 269) | pub struct IrqState {
type PpuStatus (line 279) | pub struct PpuStatus {
type Exrom (line 291) | pub struct Exrom {
constant PRG_WINDOW (line 315) | const PRG_WINDOW: usize = 0x2000;
constant PRG_RAM_SIZE (line 316) | const PRG_RAM_SIZE: usize = 0x10000;
constant EXRAM_SIZE (line 317) | const EXRAM_SIZE: usize = 0x0400;
constant CHR_WINDOW (line 318) | const CHR_WINDOW: usize = 0x0400;
constant ROM_SELECT_MASK (line 320) | const ROM_SELECT_MASK: usize = 0x80;
constant BANK_MASK (line 321) | const BANK_MASK: usize = 0x7F;
constant SPR_FETCH_START (line 323) | const SPR_FETCH_START: u32 = 64;
constant SPR_FETCH_END (line 324) | const SPR_FETCH_END: u32 = 81;
constant ATTR_MIRROR (line 328) | const ATTR_MIRROR: [u8; 4] = [0x00, 0x55, 0xAA, 0xFF];
method load (line 360) | pub fn load(
method update_prg_banks (line 418) | pub fn update_prg_banks(&mut self) {
method set_prg_bank_range (line 445) | pub fn set_prg_bank_range(&mut self, start: usize, end: usize, bank: u...
method rom_select (line 455) | pub fn rom_select(&self, addr: u16) -> bool {
method update_chr_banks (line 504) | pub fn update_chr_banks(&mut self, chr_bank: ChrBank) {
method read_ex_ram (line 536) | pub fn read_ex_ram(&self, addr: u16) -> u8 {
method write_ex_ram (line 540) | pub fn write_ex_ram(&mut self, addr: u16, val: u8) {
method inc_fetch_count (line 544) | pub const fn inc_fetch_count(&mut self) {
method fetch_count (line 548) | pub const fn fetch_count(&self) -> u32 {
method sprite8x16 (line 552) | pub const fn sprite8x16(&self) -> bool {
method spr_fetch (line 556) | pub fn spr_fetch(&self) -> bool {
method nametable_select (line 560) | pub const fn nametable_select(&self, addr: u16) -> Nametable {
method fmt (line 1129) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method chr_read (line 614) | fn chr_read(&mut self, addr: u16, ciram: &CIRam) -> u8 {
method chr_peek (line 676) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_read (line 734) | fn prg_read(&mut self, addr: u16) -> u8 {
method prg_peek (line 758) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 819) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 835) | fn prg_write(&mut self, addr: u16, val: u8) {
method ppu_write (line 1021) | fn ppu_write(&mut self, addr: u16, val: u8) {
method irq_pending (line 1036) | fn irq_pending(&self) -> bool {
method dma_pending (line 1041) | fn dma_pending(&self) -> bool {
method clear_dma_pending (line 1046) | fn clear_dma_pending(&mut self) {
method mirroring (line 1052) | fn mirroring(&self) -> Mirroring {
method reset (line 1058) | fn reset(&mut self, _kind: ResetKind) {
method clock (line 1065) | fn clock(&mut self) {
method region (line 1097) | fn region(&self) -> NesRegion {
method set_region (line 1101) | fn set_region(&mut self, region: NesRegion) {
method save (line 1108) | fn save(&self, path: impl AsRef<Path>) -> fs::Result<()> {
method load (line 1113) | fn load(&mut self, path: impl AsRef<Path>) -> fs::Result<()> {
method output (line 1119) | fn output(&self) -> f32 {
FILE: tetanes-core/src/mapper/m007_axrom.rs
type Axrom (line 17) | pub struct Axrom {
constant PRG_ROM_WINDOW (line 26) | const PRG_ROM_WINDOW: usize = 32 * 1024;
constant CHR_RAM_SIZE (line 27) | const CHR_RAM_SIZE: usize = 8 * 1024;
constant SINGLE_SCREEN_B (line 28) | const SINGLE_SCREEN_B: u8 = 0b10000;
method load (line 31) | pub fn load(
method chr_peek (line 55) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 65) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 74) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 84) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 97) | fn mirroring(&self) -> Mirroring {
FILE: tetanes-core/src/mapper/m009_pxrom.rs
type Pxrom (line 17) | pub struct Pxrom {
constant PRG_WINDOW (line 39) | const PRG_WINDOW: usize = 8 * 1024;
constant CHR_ROM_WINDOW (line 40) | const CHR_ROM_WINDOW: usize = 4 * 1024;
constant PRG_RAM_SIZE (line 41) | const PRG_RAM_SIZE: usize = 8 * 1024;
constant MIRRORING_MASK (line 43) | const MIRRORING_MASK: u8 = 0x01;
method load (line 46) | pub fn load(
method update_banks (line 71) | pub fn update_banks(&mut self) {
method chr_read (line 88) | fn chr_read(&mut self, addr: u16, ciram: &CIRam) -> u8 {
method chr_peek (line 104) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 114) | fn prg_peek(&self, addr: u16) -> u8 {
method prg_write (line 124) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 144) | fn mirroring(&self) -> Mirroring {
method reset (line 150) | fn reset(&mut self, _kind: ResetKind) {
FILE: tetanes-core/src/mapper/m010_fxrom.rs
type Fxrom (line 19) | pub struct Fxrom {
constant PRG_WINDOW (line 41) | const PRG_WINDOW: usize = 16 * 1024;
constant CHR_ROM_WINDOW (line 42) | const CHR_ROM_WINDOW: usize = 4 * 1024;
constant PRG_RAM_SIZE (line 43) | const PRG_RAM_SIZE: usize = 8 * 1024;
constant MIRRORING_MASK (line 45) | const MIRRORING_MASK: u8 = 0x01;
method load (line 48) | pub fn load(
method update_banks (line 70) | pub fn update_banks(&mut self) {
method chr_read (line 87) | fn chr_read(&mut self, addr: u16, ciram: &CIRam) -> u8 {
method chr_peek (line 103) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 113) | fn prg_peek(&self, addr: u16) -> u8 {
method prg_write (line 123) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 145) | fn mirroring(&self) -> Mirroring {
method reset (line 151) | fn reset(&mut self, _kind: ResetKind) {
method save (line 160) | fn save(&self, path: impl AsRef<Path>) -> fs::Result<()> {
method load (line 165) | fn load(&mut self, path: impl AsRef<Path>) -> fs::Result<()> {
FILE: tetanes-core/src/mapper/m011_color_dreams.rs
type ColorDreams (line 17) | pub struct ColorDreams {
constant PRG_WINDOW (line 27) | const PRG_WINDOW: usize = 32 * 1024;
constant CHR_ROM_WINDOW (line 28) | const CHR_ROM_WINDOW: usize = 8 * 1024;
constant CHR_BANK_MASK (line 30) | const CHR_BANK_MASK: u8 = 0b1111_0000;
constant PRG_BANK_MASK (line 31) | const PRG_BANK_MASK: u8 = 0b0000_0011;
method load (line 34) | pub fn load(
method chr_peek (line 59) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 69) | fn prg_peek(&self, addr: u16) -> u8 {
method prg_write (line 78) | fn prg_write(&mut self, addr: u16, mut val: u8) {
method mirroring (line 95) | fn mirroring(&self) -> Mirroring {
FILE: tetanes-core/src/mapper/m018_jalecoss88006.rs
type PageBit (line 17) | enum PageBit {
method page (line 23) | const fn page(&self, page: usize, val: u8) -> usize {
method from (line 33) | fn from(addr: u16) -> Self {
type Regs (line 45) | pub struct Regs {
type JalecoSs88006 (line 55) | pub struct JalecoSs88006 {
constant PRG_WINDOW (line 68) | const PRG_WINDOW: usize = 8 * 1024;
constant PRG_RAM_SIZE (line 69) | const PRG_RAM_SIZE: usize = 8 * 1024;
constant CHR_WINDOW (line 70) | const CHR_WINDOW: usize = 1024;
constant IRQ_MASKS (line 72) | const IRQ_MASKS: [u16; 4] = [0xFFFF, 0x0FFF, 0x00FF, 0x000F];
method load (line 75) | pub fn load(
method update_prg_bank (line 101) | fn update_prg_bank(&mut self, bank: usize, val: u8, bits: PageBit) {
method update_chr_bank (line 106) | fn update_chr_bank(&mut self, bank: usize, val: u8, bits: PageBit) {
method chr_peek (line 130) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 140) | fn prg_peek(&self, addr: u16) -> u8 {
method prg_write (line 151) | fn prg_write(&mut self, addr: u16, val: u8) {
method irq_pending (line 220) | fn irq_pending(&self) -> bool {
method mirroring (line 226) | fn mirroring(&self) -> Mirroring {
method reset (line 232) | fn reset(&mut self, kind: ResetKind) {
method clock (line 241) | fn clock(&mut self) {
FILE: tetanes-core/src/mapper/m019_namco163.rs
type Board (line 18) | pub enum Board {
type Regs (line 29) | pub struct Regs {
type Namco163 (line 40) | pub struct Namco163 {
constant PRG_WINDOW (line 59) | const PRG_WINDOW: usize = 8 * 1024;
constant PRG_RAM_SIZE (line 60) | const PRG_RAM_SIZE: usize = 8 * 1024;
constant CHR_WINDOW (line 61) | const CHR_WINDOW: usize = 1024;
method load (line 64) | pub fn load(
method update_prg_ram_access (line 115) | fn update_prg_ram_access(&mut self) {
method maybe_set_board (line 142) | fn maybe_set_board(&mut self, board: Board) {
method chr_peek (line 187) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_read (line 204) | fn prg_read(&mut self, addr: u16) -> u8 {
method prg_peek (line 213) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 234) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 245) | fn prg_write(&mut self, addr: u16, val: u8) {
method irq_pending (line 338) | fn irq_pending(&self) -> bool {
method mirroring (line 344) | fn mirroring(&self) -> Mirroring {
method reset (line 350) | fn reset(&mut self, kind: ResetKind) {
method clock (line 366) | fn clock(&mut self) {
method save (line 382) | fn save(&self, path: impl AsRef<std::path::Path>) -> fs::Result<()> {
method load (line 386) | fn load(&mut self, path: impl AsRef<std::path::Path>) -> fs::Result<()> {
method output (line 397) | fn output(&self) -> f32 {
type Audio (line 404) | pub struct Audio {
constant CHANNEL_COUNT (line 424) | const CHANNEL_COUNT: usize = 8;
constant REG_FREQ_LOW (line 426) | const REG_FREQ_LOW: usize = 0x00;
constant REG_FREQ_MID (line 427) | const REG_FREQ_MID: usize = 0x02;
constant REG_FREQ_HIGH (line 428) | const REG_FREQ_HIGH: usize = 0x04;
constant REG_WAVE_LEN (line 429) | const REG_WAVE_LEN: usize = 0x04;
constant REG_WAVE_ADDR (line 430) | const REG_WAVE_ADDR: usize = 0x06;
constant REG_VOLUME (line 431) | const REG_VOLUME: usize = 0x07;
method new (line 433) | pub fn new() -> Self {
method read_register (line 448) | pub fn read_register(&mut self, addr: u16) -> u8 {
method peek_register (line 458) | pub fn peek_register(&self, addr: u16) -> u8 {
method write_register (line 466) | pub fn write_register(&mut self, addr: u16, val: u8) {
method output (line 485) | pub const fn output(&self) -> f32 {
method update_output (line 493) | fn update_output(&mut self) {
method base_addr (line 505) | const fn base_addr(&self) -> usize {
method phase (line 511) | const fn phase(&self) -> u32 {
method wave_length (line 517) | fn wave_length(&self) -> u32 {
method wave_address (line 524) | fn wave_address(&self) -> u32 {
method volume (line 532) | fn volume(&self) -> u8 {
method set_phase (line 538) | const fn set_phase(&mut self, phase: u32) {
method frequency (line 544) | fn frequency(&self) -> u32 {
method update_channel (line 553) | fn update_channel(&mut self) {
method channel_count (line 576) | fn channel_count(&self) -> u8 {
method default (line 418) | fn default() -> Self {
method clock (line 582) | fn clock(&mut self) {
FILE: tetanes-core/src/mapper/m024_m026_vrc6.rs
type Revision (line 18) | pub enum Revision {
type Regs (line 29) | pub struct Regs {
type Vrc6 (line 38) | pub struct Vrc6 {
constant PRG_RAM_SIZE (line 54) | const PRG_RAM_SIZE: usize = 8 * 1024;
constant PRG_WINDOW (line 55) | const PRG_WINDOW: usize = 8 * 1024;
constant CHR_WINDOW (line 56) | const CHR_WINDOW: usize = 1024;
method load (line 59) | pub fn load(
method prg_ram_enabled (line 89) | pub const fn prg_ram_enabled(&self) -> bool {
method set_nametables (line 93) | pub fn set_nametables(&mut self, nametables: &[usize]) {
method set_mirroring (line 99) | pub fn set_mirroring(&mut self, mirroring: Mirroring) {
method set_nametable_page (line 110) | pub const fn set_nametable_page(&mut self, bank: usize, page: usize) {
method update_chr_banks (line 114) | pub fn update_chr_banks(&mut self) {
method chr_peek (line 256) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 275) | fn prg_peek(&self, addr: u16) -> u8 {
method prg_write (line 286) | fn prg_write(&mut self, mut addr: u16, val: u8) {
method irq_pending (line 346) | fn irq_pending(&self) -> bool {
method mirroring (line 352) | fn mirroring(&self) -> Mirroring {
method reset (line 358) | fn reset(&mut self, kind: ResetKind) {
method clock (line 365) | fn clock(&mut self) {
method output (line 375) | fn output(&self) -> f32 {
type Audio (line 382) | pub struct Audio {
method new (line 397) | const fn new() -> Self {
method output (line 408) | fn output(&self) -> f32 {
method write_register (line 413) | fn write_register(&mut self, addr: u16, val: u8) {
method default (line 391) | fn default() -> Self {
method clock (line 438) | fn clock(&mut self) {
method reset (line 450) | fn reset(&mut self, _kind: ResetKind) {
type Pulse (line 457) | pub struct Pulse {
method new (line 475) | const fn new() -> Self {
method write_register (line 488) | fn write_register(&mut self, addr: u16, val: u8) {
method set_freq_shift (line 507) | const fn set_freq_shift(&mut self, val: u8) {
method volume (line 511) | fn volume(&self) -> f32 {
method default (line 469) | fn default() -> Self {
method clock (line 521) | fn clock(&mut self) {
type Saw (line 534) | pub struct Saw {
method new (line 551) | const fn new() -> Self {
method write_register (line 563) | fn write_register(&mut self, addr: u16, val: u8) {
method set_freq_shift (line 581) | const fn set_freq_shift(&mut self, val: u8) {
method volume (line 585) | fn volume(&self) -> f32 {
method default (line 545) | fn default() -> Self {
method clock (line 595) | fn clock(&mut self) {
FILE: tetanes-core/src/mapper/m034_bnrom.rs
type Bnrom (line 17) | pub struct Bnrom {
constant PRG_ROM_WINDOW (line 26) | const PRG_ROM_WINDOW: usize = 32 * 1024;
constant CHR_RAM_SIZE (line 27) | const CHR_RAM_SIZE: usize = 8 * 1024;
method load (line 30) | pub fn load(
method chr_peek (line 54) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 64) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 73) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 83) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 91) | fn mirroring(&self) -> Mirroring {
FILE: tetanes-core/src/mapper/m034_nina001.rs
type Nina001 (line 17) | pub struct Nina001 {
constant PRG_ROM_WINDOW (line 27) | const PRG_ROM_WINDOW: usize = 32 * 1024;
constant PRG_RAM_SIZE (line 28) | const PRG_RAM_SIZE: usize = 8 * 1024;
constant CHR_ROM_WINDOW (line 29) | const CHR_ROM_WINDOW: usize = 4 * 1024;
method load (line 31) | pub fn load(
method chr_peek (line 59) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 69) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 79) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 89) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 103) | fn mirroring(&self) -> Mirroring {
FILE: tetanes-core/src/mapper/m066_gxrom.rs
type Gxrom (line 17) | pub struct Gxrom {
constant PRG_ROM_WINDOW (line 26) | const PRG_ROM_WINDOW: usize = 32 * 1024;
constant CHR_WINDOW (line 27) | const CHR_WINDOW: usize = 8 * 1024;
constant CHR_BANK_MASK (line 29) | const CHR_BANK_MASK: u8 = 0x0F;
constant PRG_BANK_MASK (line 30) | const PRG_BANK_MASK: u8 = 0x30;
method load (line 32) | pub fn load(
method chr_peek (line 56) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 66) | fn prg_peek(&self, addr: u16) -> u8 {
method prg_write (line 75) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 85) | fn mirroring(&self) -> Mirroring {
FILE: tetanes-core/src/mapper/m069_sunsoft_fme7.rs
type Regs (line 18) | pub struct Regs {
type SunsoftFme7 (line 31) | pub struct SunsoftFme7 {
constant PRG_WINDOW (line 44) | const PRG_WINDOW: usize = 8 * 1024;
constant PRG_RAM_SIZE (line 45) | const PRG_RAM_SIZE: usize = 32 * 1024;
constant CHR_WINDOW (line 46) | const CHR_WINDOW: usize = 1024;
method load (line 48) | pub fn load(
method chr_peek (line 93) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 103) | fn prg_peek(&self, addr: u16) -> u8 {
method prg_write (line 118) | fn prg_write(&mut self, addr: u16, val: u8) {
method irq_pending (line 160) | fn irq_pending(&self) -> bool {
method mirroring (line 166) | fn mirroring(&self) -> Mirroring {
method clock (line 174) | fn clock(&mut self) {
method output (line 189) | fn output(&self) -> f32 {
type Audio (line 196) | pub struct Audio {
method new (line 213) | pub fn new() -> Self {
method output (line 235) | pub fn output(&self) -> f32 {
method period (line 242) | pub fn period(&self, channel: usize) -> u16 {
method envelope_period (line 249) | pub fn envelope_period(&self) -> u16 {
method noise_period (line 255) | pub const fn noise_period(&self) -> u8 {
method volume (line 261) | pub const fn volume(&self, channel: usize) -> u8 {
method envelope_enabled (line 267) | pub const fn envelope_enabled(&self, channel: usize) -> bool {
method square_enabled (line 273) | pub const fn square_enabled(&self, channel: usize) -> bool {
method noise_enabled (line 279) | pub const fn noise_enabled(&self, channel: usize) -> bool {
method write_register (line 283) | const fn write_register(&mut self, addr: u16, val: u8) {
method default (line 207) | fn default() -> Self {
method clock (line 295) | fn clock(&mut self) {
FILE: tetanes-core/src/mapper/m071_bf909x.rs
type Revision (line 17) | pub enum Revision {
type Bf909x (line 26) | pub struct Bf909x {
constant PRG_ROM_WINDOW (line 36) | const PRG_ROM_WINDOW: usize = 16 * 1024;
constant CHR_RAM_SIZE (line 37) | const CHR_RAM_SIZE: usize = 8 * 1024;
constant SINGLE_SCREEN_A (line 39) | const SINGLE_SCREEN_A: u8 = 0x10;
method load (line 41) | pub fn load(
method set_revision (line 64) | pub const fn set_revision(&mut self, rev: Revision) {
method chr_peek (line 76) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 86) | fn prg_peek(&self, addr: u16) -> u8 {
method chr_write (line 95) | fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
method prg_write (line 105) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 125) | fn mirroring(&self) -> Mirroring {
FILE: tetanes-core/src/mapper/m079_nina003_006.rs
type Nina003006 (line 17) | pub struct Nina003006 {
constant PRG_ROM_WINDOW (line 27) | const PRG_ROM_WINDOW: usize = 32 * 1024;
constant CHR_ROM_WINDOW (line 28) | const CHR_ROM_WINDOW: usize = 8 * 1024;
method load (line 30) | pub fn load(
method chr_peek (line 55) | fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
method prg_peek (line 65) | fn prg_peek(&self, addr: u16) -> u8 {
method prg_write (line 74) | fn prg_write(&mut self, addr: u16, val: u8) {
method mirroring (line 94) | fn mirroring(&self) -> Mirroring {
FILE: tetanes-core/src/mapper/vrc_irq.rs
type VrcIrq (line 10) | pub struct VrcIrq {
method write_reload (line 21) | pub const fn write_reload(&mut self, val: u8) {
method write_control (line 25) | pub const fn write_control(&mut self, val: u8) {
method acknowledge (line 38) | pub const fn acknowledge(&mut self) {
method clock (line 46) | fn clock(&mut self) {
method reset (line 63) | fn reset(&mut self, _kind: ResetKind) {
FILE: tetanes-core/src/mem.rs
type Memory (line 21) | pub struct Memory<D> {
function empty (line 27) | pub fn empty() -> Self {
function new (line 34) | pub fn new(mut size: usize) -> Self {
function with_ram_state (line 44) | pub fn with_ram_state(size: usize, state: RamState) -> Self {
function truncate (line 51) | pub fn truncate(&mut self, size: usize) {
function new_const (line 60) | pub fn new_const() -> Self
function with_ram_state_const (line 70) | pub fn with_ram_state_const(state: RamState) -> Self {
function fmt (line 78) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function fmt (line 86) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Target (line 94) | type Target = D;
method deref (line 95) | fn deref(&self) -> &Self::Target {
method deref_mut (line 101) | fn deref_mut(&mut self) -> &mut Self::Target {
function as_ref (line 107) | fn as_ref(&self) -> &[T] {
function as_mut (line 113) | fn as_mut(&mut self) -> &mut [T] {
type Output (line 119) | type Output = T;
function index (line 122) | fn index(&self, index: usize) -> &Self::Output {
function index_mut (line 129) | fn index_mut(&mut self, index: usize) -> &mut Self::Output {
type Output (line 135) | type Output = [T];
function index (line 138) | fn index(&self, range: Range<usize>) -> &Self::Output {
function index_mut (line 146) | fn index_mut(&mut self, range: Range<usize>) -> &mut Self::Output {
type Output (line 153) | type Output = [T];
function index (line 156) | fn index(&self, range: RangeInclusive<usize>) -> &Self::Output {
function index_mut (line 165) | fn index_mut(&mut self, range: RangeInclusive<usize>) -> &mut Self::Outp...
type ConstArray (line 174) | pub struct ConstArray<T, const N: usize> {
function new (line 180) | pub fn new() -> Self
function filled (line 188) | pub const fn filled(val: T) -> Self
function fmt (line 197) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method default (line 205) | fn default() -> Self {
function from (line 213) | fn from(data: [T; N]) -> Self {
type Target (line 219) | type Target = [T; N];
method deref (line 222) | fn deref(&self) -> &Self::Target {
method deref_mut (line 229) | fn deref_mut(&mut self) -> &mut Self::Target {
function as_ref (line 236) | fn as_ref(&self) -> &[T] {
function as_mut (line 243) | fn as_mut(&mut self) -> &mut [T] {
type Output (line 249) | type Output = T;
function index (line 252) | fn index(&self, index: usize) -> &Self::Output {
function index_mut (line 259) | fn index_mut(&mut self, index: usize) -> &mut Self::Output {
type Output (line 265) | type Output = [T];
function index (line 268) | fn index(&self, range: Range<usize>) -> &Self::Output {
function index_mut (line 275) | fn index_mut(&mut self, range: Range<usize>) -> &mut Self::Output {
type Output (line 281) | type Output = [T];
function index (line 284) | fn index(&self, range: RangeInclusive<usize>) -> &Self::Output {
function index_mut (line 292) | fn index_mut(&mut self, range: RangeInclusive<usize>) -> &mut Self::Outp...
method serialize (line 299) | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
function deserialize (line 315) | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
type Read (line 354) | pub trait Read {
method read (line 357) | fn read(&mut self, addr: u16) -> u8 {
method peek (line 362) | fn peek(&self, addr: u16) -> u8;
type Write (line 366) | pub trait Write {
method write (line 368) | fn write(&mut self, addr: u16, val: u8);
type RamState (line 374) | pub enum RamState {
method as_slice (line 383) | pub const fn as_slice() -> &'static [Self] {
method as_str (line 389) | pub const fn as_str(&self) -> &'static str {
method fill (line 398) | pub fn fill(&self, data: &mut [u8]) {
method from (line 410) | fn from(value: usize) -> Self {
method as_ref (line 420) | fn as_ref(&self) -> &str {
method fmt (line 426) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Err (line 437) | type Err = &'static str;
method from_str (line 438) | fn from_str(s: &str) -> Result<Self, Self::Err> {
type BankAccess (line 451) | pub enum BankAccess {
type Banks (line 460) | pub struct Banks {
method new (line 483) | pub fn new(
method set (line 521) | pub fn set(&mut self, bank: usize, page: usize) {
method set_range (line 533) | pub fn set_range(&mut self, start: usize, end: usize, page: usize) {
method set_access (line 553) | pub fn set_access(&mut self, bank: usize, access: BankAccess) {
method set_access_range (line 558) | pub fn set_access_range(&mut self, start: usize, end: usize, access: B...
method readable (line 565) | pub const fn readable(&self, addr: u16) -> bool {
method writable (line 573) | pub const fn writable(&self, addr: u16) -> bool {
method last (line 582) | pub const fn last(&self) -> usize {
method banks_len (line 588) | pub const fn banks_len(&self) -> usize {
method get (line 594) | pub const fn get(&self, addr: u16) -> usize {
method translate (line 600) | pub const fn translate(&self, addr: u16) -> usize {
method page (line 606) | pub const fn page(&self, bank: usize) -> usize {
method page_offset (line 612) | pub const fn page_offset(&self, bank: usize) -> usize {
method page_count (line 618) | pub const fn page_count(&self) -> usize {
method fmt (line 624) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<...
type Error (line 473) | pub enum Error {
function get_bank (line 641) | fn get_bank() {
function bank_translate (line 660) | fn bank_translate() {
FILE: tetanes-core/src/ppu.rs
type Mirroring (line 34) | pub enum Mirroring {
type PaletteRam (line 47) | pub struct PaletteRam(ConstArray<u8, 32>);
method mirror (line 52) | const fn mirror(addr: u16) -> usize {
method peek (line 63) | fn peek(&self, addr: u16) -> u8 {
method write (line 70) | fn write(&mut self, addr: u16, val: u8) {
type CIRam (line 79) | pub struct CIRam(Box<ConstArray<u8, { size::VRAM }>>);
method mirror (line 100) | pub const fn mirror(addr: u16, mirroring: Mirroring) -> usize {
method read (line 106) | pub fn read(&mut self, addr: u16, mirroring: Mirroring) -> u8 {
method peek (line 111) | pub fn peek(&self, addr: u16, mirroring: Mirroring) -> u8 {
method write (line 116) | pub fn write(&mut self, addr: u16, val: u8, mirroring: Mirroring) {
type Output (line 122) | type Output = u8;
method index (line 125) | fn index(&self, index: usize) -> &Self::Output {
method index_mut (line 132) | fn index_mut(&mut self, index: usize) -> &mut Self::Output {
type PpuAddr (line 137) | pub trait PpuAddr {
method is_attr (line 139) | fn is_attr(&self) -> bool;
method is_palette (line 141) | fn is_palette(&self) -> bool;
method is_attr (line 146) | fn is_attr(&self) -> bool {
method is_palette (line 151) | fn is_palette(&self) -> bool {
type Ppu (line 162) | pub struct Ppu {
constant NTSC_PALETTE (line 385) | pub const NTSC_PALETTE: &'static [u8] = include_bytes!("../ntscpalette...
constant SYSTEM_PALETTE (line 390) | pub const SYSTEM_PALETTE: [(u8,u8,u8); 64] = [
method new (line 414) | pub fn new(region: NesRegion) -> Self {
method chr_read (line 487) | fn chr_read(&mut self, addr: u16) -> u8 {
method chr_peek (line 493) | fn chr_peek(&self, addr: u16) -> u8 {
method chr_write (line 499) | fn chr_write(&mut self, addr: u16, val: u8) {
method bus_read (line 505) | fn bus_read(&mut self, addr: u16) -> u8 {
method bus_peek (line 518) | fn bus_peek(&self, addr: u16) -> u8 {
method bus_write (line 531) | fn bus_write(&mut self, addr: u16, val: u8) {
method frame_buffer (line 543) | pub fn frame_buffer(&self) -> &[u16] {
method frame_number (line 550) | pub const fn frame_number(&self) -> u32 {
method pixel_brightness (line 557) | pub fn pixel_brightness(&self, x: u16, y: u16) -> u32 {
method load_mapper (line 563) | pub fn load_mapper(&mut self, mapper: Mapper) {
method mirroring (line 569) | pub fn mirroring(&self) -> Mirroring {
method snapshot (line 574) | pub fn snapshot(&self) -> Self {
method load_nametables (line 612) | pub fn load_nametables(&self, nametables: &mut [u8]) {
method load_pattern_tables (line 664) | pub fn load_pattern_tables(&self, pattern_tables: &mut [u8]) {
method load_oam (line 688) | pub fn load_oam(
method load_palettes (line 781) | pub fn load_palettes(&self, palettes: &mut [u8], colors: &mut [u8]) {
method set_pixel (line 792) | fn set_pixel(color: u16, x: u16, y: u16, width: u16, pixels: &mut [u8]) {
method increment_vram_addr (line 805) | const fn increment_vram_addr(&mut self) {
method start_vblank (line 816) | fn start_vblank(&mut self) {
method stop_vblank (line 828) | fn stop_vblank(&mut self) {
method fetch_bg_nt_byte (line 845) | fn fetch_bg_nt_byte(&mut self) {
method fetch_bg_attr_byte (line 862) | fn fetch_bg_attr_byte(&mut self) {
method bg_fetch_cycle (line 873) | fn bg_fetch_cycle(&mut self) {
method oam_eval_cycle (line 894) | fn oam_eval_cycle(&mut self) {
method spr_eval_cycle (line 985) | fn spr_eval_cycle(&mut self) {
method load_sprites (line 1016) | fn load_sprites(&mut self) {
method spr_fetch_cycle (line 1082) | fn spr_fetch_cycle(&mut self) {
method pixel_palette (line 1099) | fn pixel_palette(&mut self) -> u8 {
method headless_sprite_zero_hit (line 1157) | fn headless_sprite_zero_hit(&mut self) {
method render_pixel (line 1203) | fn render_pixel(&mut self) {
method clock_to (line 1221) | pub fn clock_to(&mut self, clock: u32) {
method write_ctrl (line 1252) | pub fn write_ctrl(&mut self, val: u8) {
method write_mask (line 1288) | pub fn write_mask(&mut self, val: u8) {
method read_status (line 1304) | pub fn read_status(&mut self) -> u8 {
method peek_status (line 1338) | pub const fn peek_status(&self) -> u8 {
method write_oamaddr (line 1349) | pub const fn write_oamaddr(&mut self, val: u8) {
method read_oamdata (line 1360) | pub fn read_oamdata(&mut self) -> u8 {
method peek_oamdata (line 1372) | pub fn peek_oamdata(&self) -> u8 {
method write_oamdata (line 1389) | pub fn write_oamdata(&mut self, mut val: u8) {
method write_scroll (line 1429) | pub fn write_scroll(&mut self, val: u8) {
method write_addr (line 1440) | pub fn write_addr(&mut self, val: u8) {
method read_data (line 1450) | pub fn read_data(&mut self) -> u8 {
method peek_data (line 1484) | pub fn peek_data(&self) -> u8 {
method write_data (line 1496) | pub fn write_data(&mut self, val: u8) {
method default (line 291) | fn default() -> Self {
constant NAMETABLE_START (line 299) | pub const NAMETABLE_START: u16 = 0x2000;
constant ATTR_OFFSET (line 300) | pub const ATTR_OFFSET: u16 = 0x03C0;
constant PALETTE_START (line 302) | pub const PALETTE_START: u16 = 0x3F00;
constant PALETTE_END (line 303) | pub const PALETTE_END: u16 = 0x3F20;
constant WIDTH (line 309) | pub const WIDTH: u16 = 256;
constant HEIGHT (line 310) | pub const HEIGHT: u16 = 240;
constant FRAME (line 311) | pub const FRAME: usize = (WIDTH * HEIGHT) as usize;
constant NAMETABLE (line 313) | pub const NAMETABLE: u16 = 0x0400;
constant OAM (line 314) | pub const OAM: usize = 256;
constant SECONDARY_OAM (line 315) | pub const SECONDARY_OAM: usize = 32;
constant VRAM (line 317) | pub const VRAM: usize = 0x0800;
constant PALETTE (line 318) | pub const PALETTE: usize = 32;
constant START (line 327) | pub const START: u16 = 0;
constant ODD_SKIP (line 328) | pub const ODD_SKIP: u16 = 339;
constant END (line 329) | pub const END: u16 = 340;
constant VISIBLE_START (line 331) | pub const VISIBLE_START: u16 = 1;
constant VISIBLE_END (line 332) | pub const VISIBLE_END: u16 = 256;
constant VBLANK (line 334) | pub const VBLANK: u16 = VISIBLE_START;
constant OAM_CLEAR_START (line 336) | pub const OAM_CLEAR_START: u16 = 1;
constant OAM_CLEAR_END (line 337) | pub const OAM_CLEAR_END: u16 = 64;
constant SPR_EVAL_START (line 339) | pub const SPR_EVAL_START: u16 = 65;
constant SPR_EVAL_START1 (line 340) | pub const SPR_EVAL_START1: u16 = 66;
constant SPR_EVAL_END0 (line 341) | pub const SPR_EVAL_END0: u16 = 255;
constant SPR_EVAL_END (line 342) | pub const SPR_EVAL_END: u16 = 256;
constant SPR_FETCH_START (line 343) | pub const SPR_FETCH_START: u16 = 257;
constant SPR_FETCH_END (line 344) | pub const SPR_FETCH_END: u16 = 320;
constant SPR_FETCH_RANGE (line 345) | pub const SPR_FETCH_RANGE: RangeInclusive<u16> = SPR_FETCH_START..=SPR_F...
constant BG_PREFETCH_START (line 347) | pub const BG_PREFETCH_START: u16 = 321;
constant BG_PREFETCH_END (line 348) | pub const BG_PREFETCH_END: u16 = 336;
constant BG_PREFETCH_RANGE (line 349) | pub const BG_PREFETCH_RANGE: RangeInclusive<u16> = BG_PREFETCH_START..=B...
constant BG_DUMMY_START (line 351) | pub const BG_DUMMY_START: u16 = 337;
constant BG_DUMMY_END (line 352) | pub const BG_DUMMY_END: u16 = END;
constant INC_Y (line 354) | pub const INC_Y: u16 = 256;
constant COPY_Y_START (line 355) | pub const COPY_Y_START: u16 = 280;
constant COPY_Y_END (line 356) | pub const COPY_Y_END: u16 = 304;
constant COPY_Y_RANGE (line 357) | pub const COPY_Y_RANGE: RangeInclusive<u16> = COPY_Y_START..=COPY_Y_END;
constant DIVIDER_NTSC (line 360) | pub const DIVIDER_NTSC: u8 = 4;
constant DIVIDER_PAL (line 361) | pub const DIVIDER_PAL: u8 = 5;
constant DIVIDER_DENDY (line 362) | pub const DIVIDER_DENDY: u8 = DIVIDER_PAL;
constant START (line 369) | pub const START: u16 = 0;
constant VISIBLE_START (line 371) | pub const VISIBLE_START: u16 = START;
constant VISIBLE_END (line 372) | pub const VISIBLE_END: u16 = 239;
constant POSTRENDER (line 374) | pub const POSTRENDER: u16 = 240;
constant PRERENDER_NTSC (line 375) | pub const PRERENDER_NTSC: u16 = 261;
constant PRERENDER_PAL (line 376) | pub const PRERENDER_PAL: u16 = 311;
constant PRERENDER_DENDY (line 377) | pub const PRERENDER_DENDY: u16 = PRERENDER_PAL;
constant VBLANK_NTSC (line 379) | pub const VBLANK_NTSC: u16 = 241;
constant VBLANK_PAL (line 380) | pub const VBLANK_PAL: u16 = VBLANK_NTSC;
constant VBLANK_DENDY (line 381) | pub const VBLANK_DENDY: u16 = 291;
method clock (line 1510) | fn clock(&mut self) {
method region (line 1651) | fn region(&self) -> NesRegion {
method set_region (line 1655) | fn set_region(&mut self, region: NesRegion) {
method reset (line 1683) | fn reset(&mut self, kind: ResetKind) {
function ciram_mirror_horizontal (line 1735) | fn ciram_mirror_horizontal() {
function ciram_mirror_vertical (line 1751) | fn ciram_mirror_vertical() {
function ciram_mirror_single_screen_a (line 1767) | fn ciram_mirror_single_screen_a() {
function ciram_mirror_single_screen_b (line 1783) | fn ciram_mirror_single_screen_b() {
function vram_writes (line 1799) | fn vram_writes() {
function vram_reads (line 1812) | fn vram_reads() {
function vram_read_pagecross (line 1829) | fn vram_read_pagecross() {
function vram_read_vertical_increment (line 1846) | fn vram_read_vertical_increment() {
function vram_horizontal_mirror (line 1868) | fn vram_horizontal_mirror() {
function vram_vertical_mirror (line 1905) | fn vram_vertical_mirror() {
function read_status_resets_latch (line 1957) | fn read_status_resets_latch() {
function vram_mirroring (line 1982) | fn vram_mirroring() {
function read_status_resets_vblank (line 1999) | fn read_status_resets_vblank() {
function sprite_zero_hit_headless_visible_cycle (line 2009) | fn sprite_zero_hit_headless_visible_cycle() {
function oam_read_write (line 2035) | fn oam_read_write() {
FILE: tetanes-core/src/ppu/ctrl.rs
type Ctrl (line 14) | pub struct Ctrl {
method new (line 54) | pub fn new() -> Self {
method write (line 60) | pub const fn write(&mut self, val: u8) {
method reset (line 78) | fn reset(&mut self, _kind: ResetKind) {
FILE: tetanes-core/src/ppu/frame.rs
type Buffer (line 16) | pub struct Buffer(Box<ConstArray<u16, { ppu::size::FRAME }>>);
method fmt (line 19) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method default (line 25) | fn default() -> Self {
type Target (line 31) | type Target = [u16; ppu::size::FRAME];
method deref (line 32) | fn deref(&self) -> &Self::Target {
method deref_mut (line 38) | fn deref_mut(&mut self) -> &mut Self::Target {
type Frame (line 47) | pub struct Frame {
method new (line 60) | pub fn new() -> Self {
method increment (line 68) | pub const fn increment(&mut self) {
method pixel (line 74) | pub fn pixel(&self, x: u16, y: u16) -> u16 {
method set_pixel (line 79) | pub fn set_pixel(&mut self, x: u16, y: u16, color: u16) {
method pixel_brightness (line 84) | pub fn pixel_brightness(&self, x: u16, y: u16) -> u32 {
method number (line 95) | pub const fn number(&self) -> u32 {
method is_odd (line 100) | pub const fn is_odd(&self) -> bool {
method buffer (line 106) | pub fn buffer(&self) -> &[u16; ppu::size::FRAME] {
method default (line 54) | fn default() -> Self {
method reset (line 112) | fn reset(&mut self, _kind: ResetKind) {
FILE: tetanes-core/src/ppu/mask.rs
type Mask (line 14) | pub struct Mask {
method new (line 56) | pub fn new(region: NesRegion) -> Self {
method write (line 66) | pub fn write(&mut self, val: u8) {
method update_emphasis (line 81) | pub fn update_emphasis(&mut self) {
method set_region (line 106) | pub fn set_region(&mut self, region: NesRegion) {
method reset (line 114) | fn reset(&mut self, _kind: ResetKind) {
method clock (line 120) | fn clock(&mut self) {
FILE: tetanes-core/src/ppu/scroll.rs
type Scroll (line 13) | pub struct Scroll {
constant COARSE_X_MASK (line 32) | pub const COARSE_X_MASK: u16 = 0x001F;
constant COARSE_Y_MASK (line 33) | pub const COARSE_Y_MASK: u16 = 0x03E0;
constant NT_X_MASK (line 34) | pub const NT_X_MASK: u16 = 0x0400;
constant NT_Y_MASK (line 35) | pub const NT_Y_MASK: u16 = 0x0800;
constant FINE_Y_MASK (line 36) | pub const FINE_Y_MASK: u16 = 0x7000;
constant X_MAX_COL (line 37) | const X_MAX_COL: u16 = 31;
constant Y_MAX_COL (line 38) | const Y_MAX_COL: u16 = 29;
constant Y_OVER_COL (line 39) | const Y_OVER_COL: u16 = 31;
constant Y_INCREMENT (line 40) | const Y_INCREMENT: u16 = 0x1000;
constant ATTR_START (line 42) | const ATTR_START: u16 = 0x23C0;
constant ADDR_MIRROR (line 43) | const ADDR_MIRROR: u16 = 0x3FFF;
method new (line 45) | pub const fn new() -> Self {
method attr_addr (line 65) | pub const fn attr_addr(&self) -> u16 {
method attr_shift (line 74) | pub const fn attr_shift(&self) -> u16 {
method addr (line 80) | pub const fn addr(&self) -> u16 {
method write (line 88) | pub fn write(&mut self, val: u8) {
method write_addr (line 121) | pub fn write_addr(&mut self, val: u8) {
method set_v (line 146) | pub const fn set_v(&mut self, val: u16) {
method delayed_update (line 155) | pub const fn delayed_update(&mut self) -> bool {
method increment (line 169) | pub const fn increment(&mut self, val: u16) {
method copy_x (line 175) | pub const fn copy_x(&mut self) {
method copy_y (line 185) | pub const fn copy_y(&mut self) {
method increment_x (line 198) | pub const fn increment_x(&mut self) {
method increment_y (line 213) | pub const fn increment_y(&mut self) {
method reset_latch (line 236) | pub const fn reset_latch(&mut self) {
method write_nametable_select (line 241) | pub fn write_nametable_select(&mut self, val: u8) {
method reset (line 251) | fn reset(&mut self, kind: ResetKind) {
FILE: tetanes-core/src/ppu/sprite.rs
type Sprite (line 13) | pub struct Sprite {
method new (line 25) | pub const fn new() -> Self {
method fmt (line 46) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method default (line 40) | fn default() -> Self {
FILE: tetanes-core/src/ppu/status.rs
type Status (line 14) | pub struct Status {
method new (line 56) | pub fn new() -> Self {
method write (line 63) | pub const fn write(&mut self, val: u8) {
method read (line 72) | pub const fn read(&self) -> u8 {
method set_spr_overflow (line 77) | pub fn set_spr_overflow(&mut self, val: bool) {
method set_spr_zero_hit (line 83) | pub fn set_spr_zero_hit(&mut self, val: bool) {
method set_in_vblank (line 89) | pub fn set_in_vblank(&mut self, val: bool) {
method reset_in_vblank (line 95) | pub fn reset_in_vblank(&mut self) {
method reset (line 103) | fn reset(&mut self, kind: ResetKind) {
FILE: tetanes-core/src/sys/fs/os.rs
function writer_impl (line 10) | pub fn writer_impl(path: impl AsRef<Path>) -> Result<impl Write> {
function reader_impl (line 23) | pub fn reader_impl(path: impl AsRef<Path>) -> Result<impl Read> {
function clear_dir_impl (line 28) | pub fn clear_dir_impl(path: impl AsRef<Path>) -> Result<()> {
function exists_impl (line 37) | pub fn exists_impl(path: impl AsRef<Path>) -> bool {
FILE: tetanes-core/src/sys/fs/wasm.rs
type StoreWriter (line 13) | pub struct StoreWriter {
type StoreReader (line 18) | pub struct StoreReader {
function local_storage (line 22) | pub fn local_storage() -> Result<web_sys::Storage> {
method write (line 34) | fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
method flush (line 39) | fn flush(&mut self) -> io::Result<()> {
method read (line 64) | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
function writer_impl (line 69) | pub fn writer_impl(path: impl AsRef<Path>) -> Result<impl Write> {
function reader_impl (line 77) | pub fn reader_impl(path: impl AsRef<Path>) -> Result<impl Read> {
function clear_dir_impl (line 98) | pub fn clear_dir_impl(path: impl AsRef<Path>) -> Result<()> {
function exists_impl (line 113) | pub fn exists_impl(path: impl AsRef<Path>) -> bool {
FILE: tetanes-core/src/video.rs
type ParseVideoFilterError (line 15) | pub struct ParseVideoFilterError;
type VideoFilter (line 19) | pub enum VideoFilter {
method as_slice (line 26) | pub const fn as_slice() -> &'static [Self] {
method as_ref (line 32) | fn as_ref(&self) -> &str {
type Error (line 41) | type Error = ParseVideoFilterError;
method try_from (line 43) | fn try_from(value: usize) -> Result<Self, Self::Error> {
type Frame (line 54) | pub struct Frame(Vec<u8>);
constant SIZE (line 57) | pub const SIZE: usize = ppu::size::FRAME * 4;
method new (line 60) | pub fn new() -> Self {
method default (line 71) | fn default() -> Self {
type Target (line 77) | type Target = Vec<u8>;
method deref (line 78) | fn deref(&self) -> &Self::Target {
method deref_mut (line 84) | fn deref_mut(&mut self) -> &mut Self::Target {
type Video (line 91) | pub struct Video {
method new (line 104) | pub fn new() -> Self {
method with_filter (line 109) | pub fn with_filter(filter: VideoFilter) -> Self {
method apply_filter (line 117) | pub fn apply_filter(&mut self, buffer: &[u16], frame_number: u32) -> &...
method apply_filter_into (line 127) | pub fn apply_filter_into(&self, buffer: &[u16], frame_number: u32, out...
method decode_buffer (line 135) | pub fn decode_buffer(buffer: &[u16], output: &mut [u8]) {
method apply_ntsc_filter (line 152) | pub fn apply_ntsc_filter(buffer: &[u16], frame_number: u32, output: &m...
method fmt (line 177) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method default (line 97) | fn default() -> Self {
function generate_ntsc_palette (line 185) | fn generate_ntsc_palette() -> Vec<u32> {
FILE: tetanes-utils/src/bin/generate_db.rs
constant GAME_DB_TXT (line 18) | const GAME_DB_TXT: &str = "tetanes-core/game_database.txt";
constant GAME_DB (line 19) | const GAME_DB: &str = "tetanes-core/game_db.dat";
function main (line 21) | fn main() -> anyhow::Result<()> {
function apply_corrections (line 77) | fn apply_corrections(game: &mut Game) {
type Game (line 116) | pub struct Game {
method new (line 130) | fn new<P: AsRef<Path>>(path: P) -> anyhow::Result<Game> {
type Opt (line 172) | struct Opt {
FILE: tetanes-utils/src/bin/list_boards.rs
function main (line 9) | fn main() -> anyhow::Result<()> {
function get_mapper (line 42) | fn get_mapper<P: AsRef<Path>>(path: P) -> anyhow::Result<String> {
type Opt (line 49) | struct Opt {
FILE: tetanes/build.rs
function main (line 1) | fn main() {
FILE: tetanes/src/bin/build_artifacts.rs
type Args (line 17) | pub struct Args {
type Build (line 33) | struct Build {
method new (line 80) | fn new(args: Args) -> anyhow::Result<Self> {
method make (line 112) | fn make(
method create_build_dir (line 126) | fn create_build_dir(&self, dir: impl AsRef<Path>) -> anyhow::Result<Pa...
method write_sha256 (line 138) | fn write_sha256(&self, file: impl AsRef<Path>, output: impl AsRef<Path...
method tar_gz (line 167) | fn tar_gz(
method create_linux_artifacts (line 194) | fn create_linux_artifacts(&self) -> anyhow::Result<()> {
method create_macos_app (line 290) | fn create_macos_app(&self) -> anyhow::Result<()> {
method create_windows_installer (line 402) | fn create_windows_installer(&self) -> anyhow::Result<()> {
method compress_web_artifacts (line 446) | fn compress_web_artifacts(&self) -> anyhow::Result<()> {
function main (line 45) | fn main() -> anyhow::Result<()> {
function copy (line 464) | fn copy(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> anyhow::Result<...
function rename (line 474) | fn rename(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> anyhow::Resul...
function create_dir_all (line 484) | fn create_dir_all(dir: impl AsRef<Path>) -> anyhow::Result<()> {
function remove_dir_all (line 493) | fn remove_dir_all(dir: impl AsRef<Path>) -> anyhow::Result<()> {
function write (line 502) | fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> anyhow::...
function read_to_string (line 513) | fn read_to_string(path: impl AsRef<Path>) -> anyhow::Result<String> {
function symlink (line 523) | fn symlink(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> anyhow::Resu...
function cmd_spawn_wait (line 535) | fn cmd_spawn_wait(cmd: &mut Command) -> anyhow::Result<ExitStatus> {
function cmd_output (line 545) | fn cmd_output(cmd: &mut Command) -> anyhow::Result<Output> {
function cmd_status (line 553) | fn cmd_status(cmd: &mut Command) -> anyhow::Result<ExitStatus> {
FILE: tetanes/src/error.rs
type Error (line 1) | pub type Error = anyhow::Error;
type Result (line 2) | pub type Result<T, E = Error> = anyhow::Result<T, E>;
FILE: tetanes/src/logging.rs
function create_registry (line 10) | fn create_registry() -> Layered<Targets, Registry> {
function init (line 27) | pub fn init() -> anyhow::Result<logging::Log> {
FILE: tetanes/src/main.rs
function main (line 46) | fn main() -> anyhow::Result<()> {
FILE: tetanes/src/nes.rs
type Nes (line 42) | pub struct Nes {
method run (line 130) | pub fn run(cfg: Config) -> anyhow::Result<()> {
method should_terminate (line 147) | pub fn should_terminate(&self) -> bool {
method new (line 159) | pub fn new(cfg: Config, event_loop: &EventLoop<NesEvent>) -> Self {
method request_renderer_resources (line 182) | pub(crate) fn request_renderer_resources(
method init_running (line 211) | pub(crate) fn init_running(&mut self, event_loop: &ActiveEventLoop) ->...
type State (line 54) | pub(crate) enum State {
method is_exiting (line 77) | pub const fn is_exiting(&self) -> bool {
method default (line 69) | fn default() -> Self {
type RunState (line 84) | pub enum RunState {
method paused (line 91) | pub const fn paused(&self) -> bool {
method auto_paused (line 95) | pub const fn auto_paused(&self) -> bool {
method manually_paused (line 99) | pub const fn manually_paused(&self) -> bool {
type Running (line 106) | pub(crate) struct Running {
FILE: tetanes/src/nes/action.rs
type Action (line 18) | pub enum Action {
constant BINDABLE (line 40) | pub const BINDABLE: [Self; 112] = [
method is_joypad (line 167) | pub const fn is_joypad(&self) -> bool {
method joypad_player (line 171) | pub fn joypad_player(&self, player: Player) -> bool {
method fmt (line 177) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method as_ref (line 183) | fn as_ref(&self) -> &str {
type Error (line 311) | type Error = anyhow::Error;
method try_from (line 313) | fn try_from(s: &str) -> Result<Self, Self::Error> {
method from (line 430) | fn from(state: Ui) -> Self {
method from (line 436) | fn from(menu: Menu) -> Self {
method from (line 442) | fn from(feature: Feature) -> Self {
method from (line 448) | fn from(setting: Setting) -> Self {
method from (line 454) | fn from((player, btn): (Player, JoypadBtn)) -> Self {
method from (line 460) | fn from(deck: DeckAction) -> Self {
method from (line 466) | fn from(action: Debug) -> Self {
method partial_cmp (line 28) | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
method cmp (line 34) | fn cmp(&self, other: &Self) -> std::cmp::Ordering {
type Ui (line 472) | pub enum Ui {
type Feature (line 481) | pub enum Feature {
type Setting (line 490) | pub enum Setting {
type DebugKind (line 511) | pub enum DebugKind {
type DebugStep (line 519) | pub enum DebugStep {
type Debug (line 528) | pub enum Debug {
FILE: tetanes/src/nes/audio.rs
type SampleRb (line 13) | type SampleRb = Arc<HeapRb<f32>>;
type SampleProducer (line 14) | type SampleProducer = CachingProd<SampleRb>;
type SampleConsumer (line 15) | type SampleConsumer = CachingCons<SampleRb>;
type State (line 20) | pub enum State {
type CallbackMsg (line 33) | pub enum CallbackMsg {
type Audio (line 41) | pub struct Audio {
method fmt (line 51) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method new (line 68) | pub fn new(enabled: bool, mut sample_rate: f32, latency: Duration, buf...
method enabled (line 91) | pub fn enabled(&self) -> bool {
method device (line 101) | pub fn device(&self) -> Option<&cpal::Device> {
method set_enabled (line 107) | pub fn set_enabled(&mut self, enabled: bool) -> anyhow::Result<State> {
method process (line 117) | pub fn process(&mut self, samples: &[f32]) {
method channels (line 129) | pub fn channels(&self) -> u16 {
method queued_time (line 137) | pub fn queued_time(&self) -> Duration {
method pause (line 150) | pub fn pause(&mut self, paused: bool) {
method recreate_output (line 161) | fn recreate_output(&mut self) -> anyhow::Result<State> {
method set_sample_rate (line 169) | pub fn set_sample_rate(&mut self, sample_rate: f32) -> anyhow::Result<...
method set_buffer_size (line 176) | pub fn set_buffer_size(&mut self, buffer_size: usize) -> anyhow::Resul...
method set_latency (line 183) | pub fn set_latency(&mut self, latency: Duration) -> anyhow::Result<Sta...
method is_recording (line 189) | pub fn is_recording(&self) -> bool {
method start_recording (line 197) | pub fn start_recording(&mut self) -> anyhow::Result<()> {
method stop_recording (line 210) | pub fn stop_recording(&mut self) -> anyhow::Result<Option<PathBuf>> {
method start (line 222) | pub fn start(&mut self) -> anyhow::Result<State> {
method stop (line 236) | pub fn stop(&mut self) -> State {
method available_hosts (line 246) | pub fn available_hosts(&self) -> Vec<cpal::HostId> {
method available_devices (line 256) | pub fn available_devices(&self) -> anyhow::Result<cpal::Devices> {
method supported_configs (line 266) | pub fn supported_configs(&self) -> Option<anyhow::Result<cpal::Support...
type Output (line 277) | struct Output {
method fmt (line 286) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method create (line 296) | fn create(
method choose_config (line 331) | fn choose_config(
method start (line 388) | fn start(&mut self) -> anyhow::Result<()> {
method stop (line 404) | fn stop(&mut self) {
type Mixer (line 412) | pub(crate) struct Mixer {
method fmt (line 424) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method start (line 438) | fn start(
method pause (line 483) | fn pause(&mut self, paused: bool) {
method start_recording (line 501) | fn start_recording(&mut self) -> anyhow::Result<()> {
method stop_recording (line 532) | fn stop_recording(&mut self) -> anyhow::Result<Option<PathBuf>> {
method make_stream (line 543) | fn make_stream<T>(
method process (line 566) | fn process(&mut self, samples: &[f32]) {
FILE: tetanes/src/nes/config.rs
constant MAX_RECENT_ROMS (line 22) | const MAX_RECENT_ROMS: usize = 10;
type AudioConfig (line 27) | pub struct AudioConfig {
method default (line 34) | fn default() -> Self {
type EmulationConfig (line 55) | pub struct EmulationConfig {
method default (line 69) | fn default() -> Self {
type RecentRom (line 94) | pub enum RecentRom {
method name (line 103) | pub fn name(&self) -> &str {
type RendererConfig (line 114) | pub struct RendererConfig {
method default (line 133) | fn default() -> Self {
type InputConfig (line 156) | pub struct InputConfig {
method set_binding (line 188) | pub fn set_binding(&mut self, action: Action, input: Input, binding: u...
method clear_binding (line 220) | pub fn clear_binding(&mut self, input: Input) {
method update_gamepad_assignments (line 241) | pub fn update_gamepad_assignments(&mut self, gamepads: &Gamepads) {
method next_gamepad_unassigned (line 266) | pub fn next_gamepad_unassigned(&mut self) -> Option<Player> {
method gamepad_assigned_to (line 273) | pub const fn gamepad_assigned_to(&self, player: Player) -> Option<Uuid> {
method gamepad_assignment (line 277) | pub fn gamepad_assignment(&self, uuid: &Uuid) -> Option<Player> {
method assign_gamepad (line 284) | pub const fn assign_gamepad(&mut self, player: Player, uuid: Uuid) {
method unassign_gamepad (line 288) | pub fn unassign_gamepad(&mut self, player: Player) -> Option<Uuid> {
method unassign_gamepad_name (line 292) | pub fn unassign_gamepad_name(&mut self, uuid: &Uuid) -> Option<Player> {
method default (line 166) | fn default() -> Self {
type Config (line 315) | pub struct Config {
constant SAVE_DIR (line 324) | pub const SAVE_DIR: &'static str = "save";
constant SAVE_EXTENSION (line 325) | pub const SAVE_EXTENSION: &'static str = "sav";
constant WINDOW_TITLE (line 326) | pub const WINDOW_TITLE: &'static str = "TetaNES";
constant FILENAME (line 327) | pub const FILENAME: &'static str = "config.json";
method default_config_dir (line 330) | pub fn default_config_dir() -> PathBuf {
method default_data_dir (line 338) | pub fn default_data_dir() -> PathBuf {
method default_picture_dir (line 346) | pub fn default_picture_dir() -> PathBuf {
method default_audio_dir (line 354) | pub fn default_audio_dir() -> PathBuf {
method config_path (line 362) | pub fn config_path() -> PathBuf {
method save_path (line 367) | pub fn save_path(name: &str, slot: u8) -> PathBuf {
method reset (line 375) | pub fn reset(&mut self) {
method save (line 379) | pub fn save(&self) -> anyhow::Result<()> {
method load (line 388) | pub fn load(path: Option<PathBuf>) -> Self {
method increment_speed (line 427) | pub fn increment_speed(&mut self) -> f32 {
method next_increment_speed (line 432) | pub fn next_increment_speed(&self) -> f32 {
method decrement_speed (line 440) | pub fn decrement_speed(&mut self) -> f32 {
method next_decrement_speed (line 445) | pub fn next_decrement_speed(&self) -> f32 {
method increment_scale (line 453) | pub fn increment_scale(&mut self) -> f32 {
method next_increment_scale (line 458) | pub fn next_increment_scale(&self) -> f32 {
method decrement_scale (line 466) | pub fn decrement_scale(&mut self) -> f32 {
method next_decrement_scale (line 471) | pub fn next_decrement_scale(&self) -> f32 {
method window_size (line 480) | pub fn window_size(&self, aspect_ratio: f32) -> egui::Vec2 {
method window_size_for_scale (line 485) | pub fn window_size_for_scale(&self, aspect_ratio: f32, scale: f32) -> ...
method texture_size (line 494) | pub const fn texture_size(&self) -> egui::Vec2 {
method shortcut (line 504) | pub fn shortcut(&self, action: impl Into<Action>) -> String {
method action_input (line 515) | pub fn action_input(&self, action: impl Into<Action>) -> Option<Input> {
method add_recent_rom (line 532) | pub fn add_recent_rom(&mut self, rom: RecentRom) {
type FrameRate (line 542) | pub enum FrameRate {
constant MIN (line 550) | pub const MIN: Self = Self::X50;
constant MAX (line 551) | pub const MAX: Self = Self::X60;
method duration (line 553) | pub fn duration(&self) -> Duration {
method from (line 587) | fn from(region: NesRegion) -> Self {
method from (line 597) | fn from(region: &NesRegion) -> Self {
method as_ref (line 603) | fn as_ref(&self) -> &str {
method fmt (line 613) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
function from (line 559) | fn from(frame_rate: FrameRate) -> Self {
function from (line 569) | fn from(frame_rate: &FrameRate) -> Self {
function from (line 575) | fn from(frame_rate: FrameRate) -> Self {
function from (line 581) | fn from(frame_rate: &FrameRate) -> Self {
FILE: tetanes/src/nes/emulation.rs
type FrameStats (line 42) | pub struct FrameStats {
method new (line 65) | pub fn new() -> Self {
method default (line 52) | fn default() -> Self {
type FrameTimeDiag (line 72) | pub struct FrameTimeDiag {
constant MAX_HISTORY (line 81) | const MAX_HISTORY: usize = 120;
constant UPDATE_INTERVAL (line 82) | const UPDATE_INTERVAL: Duration = Duration::from_millis(300);
method new (line 84) | fn new() -> Self {
method push (line 94) | fn push(&mut self, frame_time: f32) {
method avg (line 109) | fn avg(&mut self) -> f32 {
method history (line 120) | fn history(&self) -> impl Iterator<Item = &f32> {
method reset (line 124) | fn reset(&mut self) {
function shutdown (line 133) | fn shutdown(tx: &NesEventProxy, err: impl std::fmt::Display) {
type Threads (line 140) | enum Threads {
type Single (line 147) | struct Single {
type Multi (line 153) | struct Multi {
method spawn (line 159) | fn spawn(
method main (line 176) | fn main(
type Emulation (line 196) | pub struct Emulation {
method new (line 202) | pub fn new(
method on_event (line 221) | pub fn on_event(&mut self, event: &NesEvent) {
method try_clock_frame (line 233) | pub fn try_clock_frame(&mut self) {
method terminate (line 241) | pub fn terminate(&mut self) {
type State (line 256) | pub struct State {
method new (line 290) | fn new(tx: NesEventProxy, frame_tx: BufSender<Frame, FrameRecycle>, cf...
method add_message (line 345) | pub(crate) fn add_message<S: ToString>(&mut self, ty: MessageType, msg...
method write_deck (line 349) | fn write_deck<T>(
method on_error (line 358) | fn on_error(&mut self, err: impl Into<anyhow::Error>) {
method on_event (line 375) | fn on_event(&mut self, event: &NesEvent) {
method on_emulation_event (line 388) | fn on_emulation_event(&mut self, event: &EmulationEvent) {
method on_config_event (line 524) | fn on_config_event(&mut self, event: &ConfigEvent) {
method update_frame_stats (line 606) | fn update_frame_stats(&mut self) {
method send_frame (line 637) | fn send_frame(&mut self) {
method set_run_state (line 645) | fn set_run_state(&mut self, mode: RunState) {
method save_state (line 663) | fn save_state(&mut self, slot: u8, auto: bool) {
method load_state (line 677) | fn load_state(&mut self, slot: u8) {
method unload_rom (line 692) | fn unload_rom(&mut self) {
method on_load_rom (line 715) | fn on_load_rom(&mut self, rom: LoadedRom) {
method load_rom_path (line 739) | fn load_rom_path(&mut self, path: impl AsRef<std::path::Path>) {
method load_rom (line 748) | fn load_rom(&mut self, name: &str, rom: &mut impl Read) {
method on_load_replay (line 756) | fn on_load_replay(&mut self, start: Cpu, name: impl AsRef<str>) {
method load_replay_path (line 769) | fn load_replay_path(&mut self, path: impl AsRef<Path>) {
method load_replay (line 777) | fn load_replay(&mut self, name: &str, replay: &mut impl Read) {
method update_region (line 784) | fn update_region(&mut self, region: NesRegion) {
method audio_record (line 791) | fn audio_record(&mut self, recording: bool) {
method replay_record (line 810) | fn replay_record(&mut self, recording: bool) {
method save_screenshot (line 829) | fn save_screenshot(&mut self) -> anyhow::Result<PathBuf> {
method park_duration (line 857) | fn park_duration(&self) -> Option<Duration> {
method try_clock_frame (line 885) | fn try_clock_frame(&mut self) {
method drop (line 284) | fn drop(&mut self) {
FILE: tetanes/src/nes/emulation/replay.rs
type State (line 18) | pub struct State((Cpu, Vec<ReplayFrame>));
type ReplayEvent (line 21) | pub enum ReplayEvent {
type Error (line 38) | type Error = anyhow::Error;
method try_from (line 40) | fn try_from(event: EmulationEvent) -> Result<Self, Self::Error> {
method from (line 28) | fn from(event: ReplayEvent) -> Self {
type ReplayFrame (line 52) | pub struct ReplayFrame {
type Record (line 59) | pub struct Record {
method new (line 65) | pub fn new() -> Self {
method start (line 69) | pub fn start(&mut self, cpu: Cpu) {
method stop (line 74) | pub fn stop(&mut self, name: &str) -> anyhow::Result<Option<PathBuf>> {
method push (line 78) | pub fn push(&mut self, frame: u32, event: EmulationEvent) {
method save (line 87) | pub fn save(&mut self, name: &str) -> anyhow::Result<Option<PathBuf>> {
type Replay (line 114) | pub struct Replay {
method new (line 119) | pub fn new() -> Self {
method load_path (line 124) | pub fn load_path(&mut self, path: impl AsRef<Path>) -> anyhow::Result<...
method load (line 133) | pub fn load(&mut self, mut replay: impl Read) -> anyhow::Result<Cpu> {
method next (line 142) | pub fn next(&mut self, frame: u32) -> Option<EmulationEvent> {
FILE: tetanes/src/nes/emulation/rewind.rs
type Frame (line 11) | pub struct Frame {
type Rewind (line 18) | pub struct Rewind {
constant TARGET_FPS (line 29) | const TARGET_FPS: usize = 60;
method new (line 31) | pub fn new(enabled: bool, seconds: u32, interval: u32) -> Self {
method frame_size (line 45) | const fn frame_size(seconds: usize, interval: usize) -> usize {
method set_enabled (line 49) | pub fn set_enabled(&mut self, enabled: bool) {
method set_seconds (line 56) | pub fn set_seconds(&mut self, seconds: u32) {
method set_interval (line 62) | pub fn set_interval(&mut self, interval: u32) {
method push (line 68) | pub fn push(&mut self, cpu: &Cpu) -> Result<()> {
method pop (line 93) | pub fn pop(&mut self) -> Option<Cpu> {
method clear (line 119) | pub fn clear(&mut self) {
method rewind_disabled (line 128) | pub fn rewind_disabled(&mut self) {
method instant_rewind (line 135) | pub fn instant_rewind(&mut self) {
FILE: tetanes/src/nes/event.rs
type Response (line 46) | pub struct Response {
type NesEventProxy (line 52) | pub struct NesEventProxy(EventLoopProxy<NesEvent>);
method new (line 55) | pub fn new(event_loop: &EventLoop<NesEvent>) -> Self {
method event (line 59) | pub fn event(&self, event: impl Into<NesEvent>) {
method inner (line 67) | pub const fn inner(&self) -> &EventLoopProxy<NesEvent> {
type NesEvent (line 74) | pub enum NesEvent {
method from (line 98) | fn from(event: accesskit_winit::Event) -> Self {
method from (line 163) | fn from(event: ConfigEvent) -> Self {
method from (line 175) | fn from(event: DebugEvent) -> Self {
method from (line 209) | fn from(event: EmulationEvent) -> Self {
method from (line 235) | fn from(event: RendererEvent) -> Self {
method from (line 253) | fn from(event: UiEvent) -> Self {
type AccessKitWindowEvent (line 90) | pub enum AccessKitWindowEvent {
type ConfigEvent (line 117) | pub enum ConfigEvent {
type DebugEvent (line 170) | pub enum DebugEvent {
type EmulationEvent (line 182) | pub enum EmulationEvent {
type RendererEvent (line 216) | pub enum RendererEvent {
type UiEvent (line 242) | pub enum UiEvent {
type ReplayData (line 259) | pub struct ReplayData(pub Vec<u8>);
method fmt (line 262) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method as_ref (line 268) | fn as_ref(&self) -> &[u8] {
method user_event (line 274) | fn user_event(&mut self, event_loop: &ActiveEventLoop, event: NesEvent) {
method resumed (line 323) | fn resumed(&mut self, event_loop: &ActiveEventLoop) {
method window_event (line 346) | fn window_event(
method device_event (line 363) | fn device_event(
method about_to_wait (line 380) | fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
method suspended (line 392) | fn suspended(&mut self, event_loop: &ActiveEventLoop) {
method exiting (line 404) | fn exiting(&mut self, event_loop: &ActiveEventLoop) {
method user_event (line 421) | fn user_event(&mut self, _event_loop: &ActiveEventLoop, mut event: NesEv...
method resumed (line 553) | fn resumed(&mut self, _event_loop: &ActiveEventLoop) {}
method window_event (line 555) | fn window_event(
method device_event (line 661) | fn device_event(
method about_to_wait (line 672) | fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
method suspended (line 700) | fn suspended(&mut self, event_loop: &ActiveEventLoop) {
method exiting (line 709) | fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
method memory_warning (line 721) | fn memory_warning(&mut self, _event_loop: &ActiveEventLoop) {
method run_state (line 732) | fn run_state(&self) -> RunState {
method set_run_state (line 736) | fn set_run_state(&mut self, state: RunState) {
method update_repaint_times (line 741) | pub fn update_repaint_times(&mut self, event_loop: &ActiveEventLoop) {
method on_ui_event (line 766) | pub fn on_ui_event(&mut self, event: &UiEvent) {
method event (line 816) | pub fn event(&mut self, event: impl Into<NesEvent>) {
method on_gamepad_event (line 838) | pub fn on_gamepad_event(&mut self, window_id: WindowId, event: gilrs::Ev...
method on_input (line 929) | pub fn on_input(
FILE: tetanes/src/nes/input.rs
type Input (line 87) | pub enum Input {
method fmt (line 95) | pub fn fmt(input: Input) -> String {
type AxisDirection (line 268) | pub enum AxisDirection {
type Bindings (line 273) | pub type Bindings = [Option<Input>; 3];
type ActionBindings (line 277) | pub struct ActionBindings {
method new (line 283) | pub const fn new(action: Action, bindings: Bindings) -> Self {
method empty (line 287) | pub fn empty(action: Action) -> Self {
method default_shortcuts (line 296) | pub fn default_shortcuts() -> BTreeMap<Action, ActionBindings> {
method default_player_bindings (line 365) | pub fn default_player_bindings(player: Player) -> BTreeMap<Action, Act...
type InputBindings (line 426) | pub struct InputBindings(HashMap<Input, Action>);
method from_input_config (line 429) | pub fn from_input_config(cfg: &InputConfig) -> Self {
type Target (line 445) | type Target = HashMap<Input, Action>;
method deref (line 446) | fn deref(&self) -> &Self::Target {
method deref_mut (line 452) | fn deref_mut(&mut self) -> &mut Self::Target {
type Gamepads (line 459) | pub struct Gamepads {
method new (line 467) | pub fn new() -> Self {
method update_events (line 493) | pub fn update_events(&mut self) {
method axis_state (line 501) | pub fn axis_state(value: f32) -> (Option<AxisDirection>, ElementState) {
method has_events (line 517) | pub fn has_events(&self) -> bool {
method input_from_event (line 521) | pub fn input_from_event(
method connected_gamepad (line 556) | pub fn connected_gamepad(&self, id: gilrs::GamepadId) -> Option<gilrs:...
method gamepad (line 562) | pub fn gamepad(&self, id: gilrs::GamepadId) -> Option<gilrs::Gamepad<'...
method gamepad_by_uuid (line 566) | pub fn gamepad_by_uuid(&self, uuid: &Uuid) -> Option<gilrs::Gamepad<'_...
method gamepad_name_by_uuid (line 575) | pub fn gamepad_name_by_uuid(&self, uuid: &Uuid) -> Option<String> {
method gamepad_uuid (line 579) | pub fn gamepad_uuid(&self, id: gilrs::GamepadId) -> Option<Uuid> {
method is_connected (line 583) | pub fn is_connected(&self, uuid: &Uuid) -> bool {
method list (line 587) | pub fn list(&self) -> Option<Peekable<gilrs::ConnectedGamepadsIterator...
method connected_uuids (line 591) | pub fn connected_uuids(&self) -> impl Iterator<Item = &Uuid> {
method events (line 595) | pub fn events(&self) -> impl Iterator<Item = &gilrs::Event> {
method next_event (line 599) | pub fn next_event(&mut self) -> Option<gilrs::Event> {
method clear_events (line 603) | pub fn clear_events(&mut self) {
method set_ui_consumes (line 607) | pub fn set_ui_consumes(&mut self, consumes: bool) {
method connect (line 614) | pub fn connect(&mut self, gamepad_id: gilrs::GamepadId) {
method disconnect (line 622) | pub fn disconnect(&mut self, gamepad_id: gilrs::GamepadId) {
method create_uuid (line 630) | pub fn create_uuid(gamepad: &gilrs::Gamepad<'_>) -> Uuid {
FILE: tetanes/src/nes/renderer.rs
constant OVERSCAN_TRIM (line 55) | pub const OVERSCAN_TRIM: usize = (4 * ppu::size::WIDTH * 8) as usize;
type FrameRecycle (line 59) | pub struct FrameRecycle;
method new_element (line 62) | fn new_element(&self) -> Frame {
method recycle (line 66) | fn recycle(&self, _frame: &mut Frame) {}
type State (line 70) | pub struct State {
method fmt (line 79) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Viewport (line 91) | pub struct Viewport {
method fmt (line 106) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method initialize_window (line 1277) | pub fn initialize_window(
method update_info (line 1318) | pub fn update_info(info: &mut ViewportInfo, ctx: &egui::Context, windo...
method set_cursor (line 1354) | fn set_cursor(&mut self, cursor_icon: egui::CursorIcon) {
type Renderer (line 126) | pub struct Renderer {
method fmt (line 143) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method new (line 177) | pub fn new(
method destroy (line 283) | pub fn destroy(&mut self) {
method root_window_id (line 296) | pub fn root_window_id(&self) -> Option<WindowId> {
method window_id_for_viewport (line 300) | pub fn window_id_for_viewport(&self, viewport_id: ViewportId) -> Optio...
method viewport_id_for_window (line 309) | pub fn viewport_id_for_window(&self, window_id: WindowId) -> Option<Vi...
method root_viewport (line 317) | pub fn root_viewport<R>(&self, reader: impl FnOnce(&Viewport) -> R) ->...
method root_window (line 322) | pub fn root_window(&self) -> Option<Arc<Window>> {
method all_window_ids (line 327) | pub fn all_window_ids(&self) -> Vec<WindowId> {
method window (line 336) | pub fn window(&self, window_id: WindowId) -> Option<Arc<Window>> {
method window_size (line 346) | pub fn window_size(&self, cfg: &Config) -> Vec2 {
method window_size_for_scale (line 350) | pub fn window_size_for_scale(&self, cfg: &Config, scale: f32) -> Vec2 {
method find_max_scale_for_width (line 358) | pub fn find_max_scale_for_width(&self, width: f32, cfg: &Config) -> f32 {
method all_viewports_occluded (line 368) | pub fn all_viewports_occluded(&self) -> bool {
method inner_size (line 373) | pub fn inner_size(&self) -> Option<PhysicalSize<u32>> {
method fullscreen (line 377) | pub fn fullscreen(&self) -> bool {
method set_fullscreen (line 383) | pub fn set_fullscreen(&mut self, fullscreen: bool, embed_viewports: bo...
method set_embed_viewports (line 393) | pub fn set_embed_viewports(&mut self, embed: bool) {
method set_always_on_top (line 397) | pub fn set_always_on_top(&mut self, always_on_top: bool) {
method initialize_all_windows (line 411) | fn initialize_all_windows(&mut self, event_loop: &ActiveEventLoop) {
method rom_loaded (line 432) | pub fn rom_loaded(&self) -> bool {
method add_message (line 436) | pub fn add_message<S>(&mut self, ty: MessageType, text: S)
method on_error (line 444) | pub fn on_error(&mut self, err: anyhow::Error) {
method load (line 451) | pub fn load(ctx: &egui::Context, cfg: &Config) -> anyhow::Result<()> {
method auto_save (line 469) | pub fn auto_save(&mut self, cfg: &Config) -> anyhow::Result<()> {
method save (line 477) | pub fn save(&mut self, cfg: &Config) -> anyhow::Result<()> {
method request_resources (line 498) | pub fn request_resources(
method create_window (line 546) | pub fn create_window(
method create_painter (line 656) | pub async fn create_painter(window: Arc<Window>) -> anyhow::Result<Pai...
method recreate_window (line 680) | pub fn recreate_window(&mut self, event_loop: &ActiveEventLoop) {
method drop_window (line 712) | pub fn drop_window(&mut self) -> anyhow::Result<()> {
method set_painter_window (line 727) | fn set_painter_window(
method create_or_update_viewport (line 744) | fn create_or_update_viewport<'a>(
method handle_platform_output (line 792) | pub fn handle_platform_output(viewport: &mut Viewport, platform_output...
method open_url_in_browser (line 814) | fn open_url_in_browser(url: &str) {
method handle_viewport_output (line 820) | fn handle_viewport_output(
method process_viewport_commands (line 841) | fn process_viewport_commands(
method redraw (line 1043) | pub fn redraw(
method handle_resize (line 1229) | fn handle_resize(&mut self, viewport_id: ViewportId, cfg: &Config) {
method resize_window (line 1247) | fn resize_window(&self, cfg: &Config) {
type Resources (line 161) | pub struct Resources {
method fmt (line 168) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
FILE: tetanes/src/nes/renderer/clipboard.rs
type Clipboard (line 2) | pub struct Clipboard {
method fmt (line 23) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method new (line 32) | pub fn new() -> Self {
method get (line 36) | pub fn get(&mut self) -> Option<String> {
method set (line 48) | pub fn set(&mut self, text: impl Into<String>) {
method default (line 11) | fn default() -> Self {
FILE: tetanes/src/nes/renderer/event.rs
method on_event (line 26) | pub fn on_event(&mut self, event: &mut NesEvent, cfg: &Config) {
method on_window_event (line 114) | pub fn on_window_event(&mut self, window_id: WindowId, event: &WindowEve...
method on_mouse_motion (line 406) | pub fn on_mouse_motion(&mut self, delta: (f64, f64)) {
method on_mouse_button_input (line 423) | fn on_mouse_button_input(
method on_cursor_moved (line 443) | fn on_cursor_moved(
method on_touch (line 460) | fn on_touch(
method on_mouse_wheel (line 533) | fn on_mouse_wheel(viewport: &mut Viewport, pixels_per_point: f32, delta:...
method on_keyboard_input (line 550) | fn on_keyboard_input(viewport: &mut Viewport, event: &KeyEvent) {
method on_gamepad_update (line 638) | pub fn on_gamepad_update(&self, gamepads: &Gamepads) -> Response {
type Error (line 651) | type Error = ();
method try_from (line 653) | fn try_from((key, modifiers): (egui::Key, egui::Modifiers)) -> Result<Se...
method from (line 661) | fn from(button: PointerButton) -> Self {
function is_cut_command (line 666) | pub fn is_cut_command(modifiers: egui::Modifiers, keycode: egui::Key) ->...
function is_copy_command (line 672) | pub fn is_copy_command(modifiers: egui::Modifiers, keycode: egui::Key) -...
function is_paste_command (line 678) | pub fn is_paste_command(modifiers: egui::Modifiers, keycode: egui::Key) ...
function is_printable_char (line 688) | pub const fn is_printable_char(chr: char) -> bool {
function key_from_winit_key (line 696) | pub fn key_from_winit_key(key: &winit::keyboard::Key) -> Option<egui::Ke...
function key_from_named_key (line 704) | pub fn key_from_named_key(named_key: winit::keyboard::NamedKey) -> Optio...
function key_from_keycode (line 771) | pub const fn key_from_keycode(keycode: KeyCode) -> Option<egui::Key> {
function keycode_from_key (line 887) | pub const fn keycode_from_key(key: egui::Key) -> Option<KeyCode> {
function modifiers_from_modifiers_state (line 1001) | pub fn modifiers_from_modifiers_state(modifier_state: ModifiersState) ->...
function modifiers_state_from_modifiers (line 1017) | pub fn modifiers_state_from_modifiers(modifiers: egui::Modifiers) -> Mod...
function pointer_button_from_mouse (line 1036) | pub const fn pointer_button_from_mouse(button: MouseButton) -> Option<Po...
function mouse_button_from_pointer (line 1047) | pub const fn mouse_button_from_pointer(button: PointerButton) -> MouseBu...
function translate_cursor (line 1057) | pub const fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option<w...
FILE: tetanes/src/nes/renderer/gui.rs
constant UI_SETTINGS_TITLE (line 59) | const UI_SETTINGS_TITLE: &str = "🔧 UI Settings";
constant UI_INSPECTION_TITLE (line 61) | const UI_INSPECTION_TITLE: &str = "🔍 UI Inspection";
constant UI_MEMORY_TITLE (line 63) | const UI_MEMORY_TITLE: &str = "📝 UI Memory";
type Menu (line 66) | pub enum Menu {
type MessageType (line 75) | pub enum MessageType {
type Gui (line 83) | pub struct Gui {
constant MSG_TIMEOUT (line 127) | const MSG_TIMEOUT: Duration = Duration::from_secs(3);
constant MAX_MESSAGES (line 128) | const MAX_MESSAGES: usize = 5;
constant NO_ROM_LOADED (line 129) | const NO_ROM_LOADED: &'static str = "No ROM is loaded.";
method new (line 132) | pub fn new(
method on_window_event (line 189) | pub fn on_window_event(&mut self, event: &WindowEvent) -> Response {
method on_event (line 203) | pub fn on_event(&mut self, queue: &wgpu::Queue, event: &mut NesEvent) {
method add_message (line 271) | pub fn add_message<S>(&mut self, ty: MessageType, text: S)
method loaded_region (line 285) | pub fn loaded_region(&self) -> Option<NesRegion> {
method aspect_ratio (line 289) | pub fn aspect_ratio(&self, cfg: &Config) -> f32 {
method ui (line 301) | pub fn ui(&mut self, ui: &mut Ui, cfg: &Config, gamepads: &Gamepads) {
method initialize (line 364) | fn initialize(&mut self, ui: &mut Ui, cfg: &Config) {
method show_about_window (line 418) | fn show_about_window(&mut self, ctx: &Context, enabled: bool) {
method show_about_homebrew_window (line 426) | fn show_about_homebrew_window(&mut self, ctx: &Context, enabled: bool) {
method show_viewport_info_window (line 455) | pub(super) fn show_viewport_info_window(
method show_performance_window (line 466) | fn show_performance_window(&mut self, ctx: &Context, enabled: bool, cf...
method close_viewport (line 476) | pub(super) fn close_viewport(&self, viewport_id: ViewportId) {
method show_viewport (line 502) | fn show_viewport(
method show_update_window (line 543) | fn show_update_window(&mut self, ctx: &Context, enabled: bool, cfg: &C...
method menubar (line 617) | fn menubar(&mut self, ui: &mut Ui, cfg: &Config) {
method toggle_dark_mode_button (line 653) | pub fn toggle_dark_mode_button(tx: &NesEventProxy, ui: &mut Ui) {
method file_menu (line 671) | fn file_menu(&mut self, ui: &mut Ui, cfg: &Config) {
method homebrew_rom_menu (line 798) | fn homebrew_rom_menu(&mut self, ui: &mut Ui) {
method controls_menu (line 818) | fn controls_menu(&mut self, ui: &mut Ui, cfg: &Config) {
method config_menu (line 933) | fn config_menu(&mut self, ui: &mut Ui, cfg: &Config) {
method window_menu (line 1023) | fn window_menu(&mut self, ui: &mut Ui, cfg: &Config) {
method debug_menu (line 1072) | fn debug_menu(&mut self, ui: &mut Ui, cfg: &Config) {
method nes_frame (line 1206) | fn nes_frame(&mut self, ui: &mut Ui, enabled: bool, cfg: &Config, game...
method performance_stats (line 1362) | fn performance_stats(&mut self, ui: &mut Ui, cfg: &Config) {
method help_menu (line 1518) | fn help_menu(&mut self, ui: &mut Ui) {
method about (line 1526) | fn about(&mut self, ui: &mut Ui, enabled: bool) {
method about_homebrew (line 1578) | fn about_homebrew(ui: &mut Ui, rom: RomAsset) {
method message_bar (line 1593) | fn message_bar(&mut self, ui: &mut Ui) {
method error_bar (line 1608) | fn error_bar(&mut self, ui: &mut Ui) {
method dark_theme (line 1622) | pub fn dark_theme() -> egui::Visuals {
method light_theme (line 1693) | pub fn light_theme() -> egui::Visuals {
FILE: tetanes/src/nes/renderer/gui/keybinds.rs
type Tab (line 21) | pub enum Tab {
type State (line 29) | pub struct State {
method ui (line 171) | fn ui(&mut self, ui: &mut Ui, enabled: bool, cfg: &Config, gamepad_sta...
method list (line 195) | fn list(
method player_gamepad_combo (line 258) | fn player_gamepad_combo(
method show_set_keybind_window (line 328) | pub fn show_set_keybind_window(
method set_keybind (line 355) | pub fn set_keybind(
method show_gamepad_unassign_window (line 482) | fn show_gamepad_unassign_window(&mut self, ctx: &Context) {
method gamepad_unassign_confirm (line 508) | fn gamepad_unassign_confirm(&mut self, ui: &mut Ui) {
type Keybinds (line 38) | pub struct Keybinds {
constant TITLE (line 68) | const TITLE: &'static str = "TetaNES - Keybinds";
method new (line 70) | pub fn new(tx: NesEventProxy) -> Self {
method wants_input (line 83) | pub fn wants_input(&self) -> bool {
method open (line 89) | pub fn open(&self) -> bool {
method set_open (line 93) | pub fn set_open(&self, open: bool, ctx: &Context) {
method toggle_open (line 100) | pub fn toggle_open(&self, ctx: &Context) {
method show (line 112) | pub fn show(&mut self, ui: &mut Ui, opts: ViewportOptions, cfg: Config...
type PendingInput (line 45) | pub struct PendingInput {
type GamepadState (line 54) | pub struct GamepadState {
type ConnectedGamepad (line 61) | pub struct ConnectedGamepad {
FILE: tetanes/src/nes/renderer/gui/lib.rs
type ViewportOptions (line 18) | pub struct ViewportOptions {
type ShowShortcut (line 24) | pub enum ShowShortcut {
method then (line 30) | pub fn then<T>(&self, f: impl FnOnce() -> T) -> Option<T> {
type ShortcutText (line 38) | pub trait ShortcutText<'a>
method shortcut_text (line 42) | fn shortcut_text(self, shortcut_text: impl Into<WidgetText>) -> Shortc...
function cursor_to_zapper (line 51) | pub fn cursor_to_zapper(x: f32, y: f32, rect: Rect) -> Option<Pos2> {
function input_down (line 60) | pub fn input_down(ui: &mut Ui, gamepads: &Gamepads, cfg: &Config, input:...
type ShortcutWidget (line 86) | pub struct ShortcutWidget<'a, T> {
type Target (line 93) | type Target = T;
method deref (line 94) | fn deref(&self) -> &Self::Target {
method deref_mut (line 100) | fn deref_mut(&mut self) -> &mut Self::Target {
method ui (line 109) | fn ui(self, ui: &mut Ui) -> Response {
type ToggleValue (line 146) | pub struct ToggleValue<'a> {
function new (line 152) | pub fn new(selected: &'a mut bool, text: impl Into<WidgetText>) -> Self {
method ui (line 161) | fn ui(self, ui: &mut Ui) -> Response {
type RadioValue (line 172) | pub struct RadioValue<'a, T> {
function new (line 179) | pub fn new(current_value: &'a mut T, alternative: T, text: impl Into<Wid...
method ui (line 189) | fn ui(self, ui: &mut Ui) -> Response {
type Error (line 204) | type Error = ();
method try_from (line 206) | fn try_from(val: Input) -> Result<Self, Self::Error> {
function screen_center (line 218) | pub fn screen_center(ctx: &Context) -> Option<Pos2> {
function screen_size_in_pixels (line 233) | pub fn screen_size_in_pixels(window: &Window) -> egui::Vec2 {
function pixels_per_point (line 238) | pub fn pixels_per_point(egui_ctx: &egui::Context, window: &Window) -> f32 {
function inner_rect_in_points (line 244) | pub fn inner_rect_in_points(window: &Window, pixels_per_point: f32) -> O...
function outer_rect_in_points (line 256) | pub fn outer_rect_in_points(window: &Window, pixels_per_point: f32) -> O...
function to_winit_icon (line 268) | pub fn to_winit_icon(icon: &egui::IconData) -> Option<winit::window::Ico...
function animated_dashed_rect (line 283) | pub fn animated_dashed_rect(
FILE: tetanes/src/nes/renderer/gui/ppu_viewer.rs
type State (line 26) | struct State {
method update_debugger (line 374) | fn update_debugger(&self, open: bool) {
method ui (line 388) | fn ui(&mut self, ui: &mut Ui, enabled: bool) {
method grid_settings (line 408) | fn grid_settings(&mut self, ui: &mut Ui) {
method general_settings (line 424) | fn general_settings(&mut self, ui: &mut Ui) {
method nametables_tab (line 452) | fn nametables_tab(&mut self, ui: &mut Ui) {
method nametable_hover (line 615) | fn nametable_hover(&mut self, ui: &mut Ui, res: &egui::Response, pos: ...
method nametable_tile_from_offset (line 638) | fn nametable_tile_from_offset(&self, offset: Vec2, texture_size: Vec2)...
method nametable_tile (line 695) | fn nametable_tile(&mut self, ui: &mut Ui, label: &str, offset: Option<...
method nametable_scroll_overlay (line 793) | fn nametable_scroll_overlay(&self, ui: &mut Ui, image_rect: Rect) {
method pattern_tables_tab (line 865) | fn pattern_tables_tab(&mut self, ui: &mut Ui) {
method pattern_tables_hover (line 929) | fn pattern_tables_hover(&mut self, ui: &mut Ui, res: &egui::Response, ...
method pattern_chr_tile_from_offset (line 952) | fn pattern_chr_tile_from_offset(&self, offset: Vec2, texture_size: Vec...
method pattern_tables_tile (line 976) | fn pattern_tables_tile(&mut self, ui: &mut Ui, label: &str, offset: Op...
method oam_tab (line 1012) | fn oam_tab(&mut self, ui: &mut Ui) {
method oam_hover (line 1116) | fn oam_hover(&mut self, ui: &mut Ui, res: &egui::Response, pos: Pos2) {
method sprites_hover (line 1143) | fn sprites_hover(&mut self, ui: &mut Ui, res: &egui::Response, pos: Po...
method oam_tile (line 1179) | fn oam_tile(&mut self, ui: &mut Ui, label: &str, offsets: Option<Vec2>) {
method oam_tile_from_offset (line 1217) | fn oam_tile_from_offset(&self, offset: Vec2, texture_size: Vec2) -> Ch...
method palette_tab (line 1236) | fn palette_tab(&mut self, ui: &mut Ui) {
method palette_hover (line 1269) | fn palette_hover(&mut self, ui: &mut Ui, res: &egui::Response, pos: Po...
method palette_color_from_offset (line 1291) | fn palette_color_from_offset(&self, offset: Vec2) -> PaletteColor {
method palette (line 1317) | fn palette(&mut self, ui: &mut Ui, label: &str, offset: Option<Vec2>) {
method palette_row (line 1367) | fn palette_row(&self, ui: &mut Ui, index: usize, pos: Pos2, size: Vec2...
method palette_grid (line 1382) | fn palette_grid(&self, ui: &mut Ui, size: Vec2) -> egui::Response {
type NametablesState (line 47) | struct NametablesState {
type PatternTablesState (line 56) | struct PatternTablesState {
type OamState (line 65) | struct OamState {
type PalettesState (line 77) | struct PalettesState {
type NametableTile (line 87) | struct NametableTile {
method default (line 103) | fn default() -> Self {
type ChrTile (line 123) | struct ChrTile {
method default (line 130) | fn default() -> Self {
type PaletteColor (line 141) | struct PaletteColor {
method default (line 149) | fn default() -> Self {
type PpuViewer (line 161) | pub struct PpuViewer {
constant TITLE (line 177) | const TITLE: &'static str = "TetaNES - PPU Viewer";
method new (line 179) | pub fn new(tx: NesEventProxy, render_state: &mut RenderState) -> Self {
method id (line 255) | pub const fn id(&self) -> ViewportId {
method open (line 259) | pub fn open(&self) -> bool {
method set_open (line 263) | pub fn set_open(&self, open: bool, ctx: &Context) {
method toggle_open (line 271) | pub fn toggle_open(&self, ctx: &Context) {
method update_ppu (line 284) | pub fn update_ppu(&mut self, queue: &wgpu::Queue, ppu: Ppu) {
method show (line 341) | pub fn show(&mut self, ui: &mut Ui, opts: ViewportOptions) {
type Tab (line 168) | pub enum Tab {
function zoom_slider (line 1410) | fn zoom_slider(ui: &mut Ui, zoom: &mut f32) {
function paint_grid (line 1424) | fn paint_grid(ui: &mut Ui, rect: Rect, y_spacing: f32, x_spacing: f32, c...
function translate_screen_pos_to_tile (line 1444) | fn translate_screen_pos_to_tile(pos: Pos2, image_rect: Rect, texture_siz...
function tile_selection (line 1451) | fn tile_selection(image_rect: Rect, texture_size: Vec2, tile_offset: Vec...
FILE: tetanes/src/nes/renderer/gui/preferences.rs
type State (line 33) | pub struct State {
method ui (line 516) | fn ui(&mut self, ui: &mut Ui, enabled: bool, cfg: &Config) {
method emulation_tab (line 564) | fn emulation_tab(&mut self, ui: &mut Ui, cfg: &Config) {
method audio_tab (line 760) | fn audio_tab(tx: &NesEventProxy, ui: &mut Ui, cfg: &Config) {
method video_tab (line 854) | fn video_tab(tx: &NesEventProxy, ui: &mut Ui, cfg: &Config) {
method input_tab (line 916) | fn input_tab(tx: &NesEventProxy, ui: &mut Ui, cfg: &Config) {
method genie_codes_entry (line 937) | pub fn genie_codes_entry(&mut self, ui: &mut Ui, cfg: &Config) {
method restore_defaults (line 999) | fn restore_defaults(tx: &NesEventProxy, ctx: &Context) {
method clear_save_states (line 1051) | pub(crate) fn clear_save_states(tx: &NesEventProxy) {
type Preferences (line 41) | pub struct Preferences {
constant TITLE (line 63) | const TITLE: &'static str = "TetaNES - Preferences";
method new (line 65) | pub fn new(tx: NesEventProxy) -> Self {
method open (line 77) | pub fn open(&self) -> bool {
method set_open (line 81) | pub fn set_open(&self, open: bool, ctx: &Context) {
method toggle_open (line 88) | pub fn toggle_open(&self, ctx: &Context) {
method show (line 100) | pub fn show(&mut self, ui: &mut Ui, opts: ViewportOptions, cfg: Config) {
method show_genie_codes_entry (line 131) | pub fn show_genie_codes_entry(&mut self, ui: &mut Ui, cfg: &Config) {
method genie_codes_list (line 135) | pub fn genie_codes_list(tx: &NesEventProxy, ui: &mut Ui, cfg: &Config,...
method save_slot_radio (line 173) | pub fn save_slot_radio(
method speed_slider (line 206) | pub fn speed_slider(tx: &NesEventProxy, ui: &mut Ui, mut speed: f32) {
method run_ahead_slider (line 218) | pub fn run_ahead_slider(tx: &NesEventProxy, ui: &mut Ui, mut run_ahead...
method rewind_checkbox (line 228) | pub fn rewind_checkbox(
method zapper_checkbox (line 246) | pub fn zapper_checkbox(
method overscan_checkbox (line 264) | pub fn overscan_checkbox(
method video_filter_radio (line 281) | pub fn video_filter_radio(
method shader_radio (line 315) | pub fn shader_radio(
method four_player_radio (line 346) | pub fn four_player_radio(tx: &NesEventProxy, ui: &mut Ui, mut four_pla...
method nes_region_radio (line 358) | pub fn nes_region_radio(tx: &NesEventProxy, ui: &mut Ui, mut region: N...
method ram_state_radio (line 373) | pub fn ram_state_radio(tx: &NesEventProxy, ui: &mut Ui, mut ram_state:...
method menubar_checkbox (line 386) | pub fn menubar_checkbox(
method messages_checkbox (line 402) | pub fn messages_checkbox(
method screen_reader_checkbox (line 421) | pub fn screen_reader_checkbox(ui: &mut Ui, shortcut: impl Into<Option<...
method window_scale_radio (line 436) | pub fn window_scale_radio(tx: &NesEventProxy, ui: &mut Ui, mut scale: ...
method fullscreen_checkbox (line 452) | pub fn fullscreen_checkbox(
method embed_viewports_checkbox (line 468) | pub fn embed_viewports_checkbox(
method always_on_top_checkbox (line 494) | pub fn always_on_top_checkbox(
type Tab (line 48) | pub enum Tab {
type GenieEntry (line 57) | pub struct GenieEntry {
FILE: tetanes/src/nes/renderer/painter.rs
type Surface (line 21) | pub struct Surface {
method new (line 29) | pub fn new(
method create_texture_view (line 42) | fn create_texture_view(
method set_shader (line 66) | fn set_shader(
type Target (line 84) | type Target = wgpu::Surface<'static>;
method deref (line 85) | fn deref(&self) -> &Self::Target {
type Painter (line 92) | pub struct Painter {
method new (line 119) | pub fn new() -> Self {
method set_shader (line 123) | pub fn set_shader(&mut self, shader: Shader) {
method set_window (line 137) | pub async fn set_window(
method paint (line 170) | pub fn paint(
method render_state (line 296) | pub const fn render_state(&self) -> Option<&RenderState> {
method render_state_mut (line 300) | pub const fn render_state_mut(&mut self) -> Option<&mut RenderState> {
method on_window_resized (line 304) | pub fn on_window_resized(&mut self, viewport_id: ViewportId, width: u3...
method retain_surfaces (line 313) | pub fn retain_surfaces(&mut self, viewport_ids: &ViewportIdSet) {
method destroy (line 317) | pub fn destroy(&mut self) {
method default (line 99) | fn default() -> Self {
type SlicedBuffer (line 325) | struct SlicedBuffer {
type RenderState (line 333) | pub struct RenderState {
method create (line 359) | async fn create(
method max_texture_side (line 585) | pub fn max_texture_side(&self) -> u32 {
method register_texture (line 589) | pub fn register_texture(
method create_vertex_buffer (line 618) | fn create_vertex_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buf...
method create_index_buffer (line 627) | fn create_index_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buff...
method resize_surface (line 636) | fn resize_surface(&self, surface: &mut Surface, width: NonZeroU32, hei...
method update_texture (line 661) | pub fn update_texture(&mut self, id: epaint::TextureId, image_delta: &...
method update_buffers (line 759) | pub fn update_buffers(
method render (line 873) | pub fn render<'rp>(
method create_sampler (line 950) | fn create_sampler(
type UniformBuffer (line 983) | struct UniformBuffer {
method eq (line 991) | fn eq(&self, other: &Self) -> bool {
type ScreenDescriptor (line 997) | pub struct ScreenDescriptor {
method screen_size_in_points (line 1007) | fn screen_size_in_points(&self) -> [f32; 2] {
type ScissorRect (line 1016) | struct ScissorRect {
method new (line 1024) | fn new(clip_rect: &epaint::Rect, pixels_per_point: f32, target_size: [...
FILE: tetanes/src/nes/renderer/shader.rs
type ParseShaderError (line 7) | pub struct ParseShaderError;
type Shader (line 11) | pub enum Shader {
method as_slice (line 18) | pub const fn as_slice() -> &'static [Self] {
method as_ref (line 24) | fn as_ref(&self) -> &str {
type Error (line 33) | type Error = ParseShaderError;
method try_from (line 35) | fn try_from(value: usize) -> Result<Self, Self::Error> {
type Resources (line 46) | pub struct Resources {
method new (line 53) | pub fn new(
FILE: tetanes/src/nes/renderer/texture.rs
type Texture (line 6) | pub struct Texture {
method new (line 17) | pub fn new(
method resize (line 72) | pub fn resize(&mut self, render_state: &mut RenderState, size: Vec2, a...
method sized (line 76) | pub fn sized(&self) -> SizedTexture {
method update (line 80) | pub fn update(&self, queue: &wgpu::Queue, bytes: &[u8]) {
method update_partial (line 84) | pub fn update_partial(&self, queue: &wgpu::Queue, bytes: &[u8], origin...
FILE: tetanes/src/nes/rom.rs
type RomData (line 2) | pub struct RomData(pub Vec<u8>);
method fmt (line 5) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method as_ref (line 11) | fn as_ref(&self) -> &[u8] {
type RomAsset (line 18) | pub struct RomAsset {
method fmt (line 27) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method new (line 38) | pub const fn new(
method data (line 54) | pub fn data(&self) -> RomData {
constant HOMEBREW_ROMS (line 80) | pub const HOMEBREW_ROMS: [RomAsset; 18] = rom_assets!(
FILE: tetanes/src/nes/version.rs
type Fetcher (line 11) | pub struct Fetcher {
method create_client (line 28) | fn create_client() -> Option<Client> {
method update_available (line 42) | pub fn update_available(&self, version: &'static str) -> anyhow::Resul...
method is_newer (line 109) | fn is_newer(new: &str, old: &str) -> bool {
method default (line 18) | fn default() -> Self {
type Version (line 120) | pub struct Version {
method new (line 132) | pub fn new() -> Self {
method current (line 139) | pub const fn current(&self) -> &str {
method latest (line 143) | pub fn latest(&self) -> String {
method set_latest (line 147) | pub fn set_latest(&mut self, version: String) {
method requires_updates (line 151) | pub const fn requires_updates(&self) -> bool {
method check_for_updates (line 156) | pub const fn check_for_updates(
method check_for_updates (line 164) | pub fn check_for_updates(
method install_update_and_restart (line 209) | pub fn install_update_and_restart(&mut self) -> anyhow::Result<()> {
method default (line 126) | fn default() -> Self {
FILE: tetanes/src/opts.rs
type FourPlayer (line 7) | pub(crate) struct FourPlayer(tetanes_core::input::FourPlayer);
method value_variants (line 10) | fn value_variants<'a>() -> &'a [Self] {
method to_possible_value (line 15) | fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
type RamState (line 21) | pub(crate) struct RamState(tetanes_core::mem::RamState);
method value_variants (line 24) | fn value_variants<'a>() -> &'a [Self] {
method to_possible_value (line 29) | fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
type NesRegion (line 35) | pub(crate) struct NesRegion(tetanes_core::common::NesRegion);
method value_variants (line 38) | fn value_variants<'a>() -> &'a [Self] {
method to_possible_value (line 43) | fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
type Opts (line 52) | pub struct Opts {
method load (line 111) | pub fn load(self) -> anyhow::Result<Config> {
FILE: tetanes/src/platform.rs
type Initialize (line 7) | pub trait Initialize {
method initialize (line 9) | fn initialize(&mut self) -> anyhow::Result<()>;
type BuilderExt (line 13) | pub trait BuilderExt {
method with_platform (line 15) | fn with_platform(self, title: &str) -> Self;
function open_file_dialog (line 19) | pub fn open_file_dialog(
function speak_text (line 30) | pub fn speak_text(text: &str) {
function constrain_window_to_viewport (line 38) | pub fn constrain_window_to_viewport(
type Feature (line 50) | pub enum Feature {
FILE: tetanes/src/sys.rs
type DiskUsage (line 7) | pub struct DiskUsage {
type SystemStats (line 15) | pub struct SystemStats {
type SystemInfo (line 21) | pub trait SystemInfo {
method update (line 22) | fn update(&mut self);
method stats (line 23) | fn stats(&self) -> Option<SystemStats>;
FILE: tetanes/src/sys/info/os.rs
type System (line 6) | pub struct System {
method default (line 12) | fn default() -> Self {
method update (line 43) | fn update(&mut self) {
method stats (line 63) | fn stats(&self) -> Option<SystemStats> {
FILE: tetanes/src/sys/info/wasm.rs
type System (line 4) | pub struct System {}
method update (line 7) | fn update(&mut self) {}
method stats (line 9) | fn stats(&self) -> Option<SystemStats> {
FILE: tetanes/src/sys/logging/os.rs
type Log (line 12) | pub struct Log {
function init_impl (line 16) | pub fn init_impl<S>(registry: S) -> anyhow::Result<(impl SubscriberInitE...
FILE: tetanes/src/sys/logging/wasm.rs
type Log (line 10) | pub struct Log;
function init_impl (line 12) | pub fn init_impl<S>(registry: S) -> anyhow::Result<(impl SubscriberInitE...
FILE: tetanes/src/sys/platform/os.rs
function open_file_dialog_impl (line 10) | pub fn open_file_dialog_impl(
function speak_text_impl (line 26) | pub const fn speak_text_impl(_text: &str) {}
method initialize (line 30) | fn initialize(&mut self) -> anyhow::Result<()> {
method initialize (line 47) | fn initialize(&mut self) -> anyhow::Result<()> {
method with_platform (line 54) | fn with_platform(self, _title: &str) -> Self {
function constrain_window_to_viewport_impl (line 102) | pub fn constrain_window_to_viewport_impl(
FILE: tetanes/src/sys/platform/wasm.rs
constant BIN_NAME (line 25) | const BIN_NAME: &str = env!("CARGO_PKG_NAME");
constant VERSION (line 26) | const VERSION: &str = env!("CARGO_PKG_VERSION");
constant OS_OPTIONS (line 27) | const OS_OPTIONS: [(Os, Arch, &str); 5] = [
type System (line 36) | pub struct System;
function open_file_dialog_impl (line 39) | pub fn open_file_dialog_impl(
function speak_text_impl (line 70) | pub fn speak_text_impl(text: &str) {
function on_error (line 92) | fn on_error(tx: &NesEventProxy, err: JsValue) {
function set_resize_handler (line 101) | fn set_resize_handler(window: &web_sys::Window, tx: &NesEventProxy) {
function set_file_onload_handler (line 130) | fn set_file_onload_handler(
function set_file_onchange_handlers (line 165) | fn set_file_onchange_handlers(
function constrain_window_to_viewport_impl (line 232) | pub fn constrain_window_to_viewport_impl(
function set_clipboard_text (line 277) | pub fn set_clipboard_text(state: &Rc<RefCell<State>>, text: String) -> R...
function process_input (line 306) | pub fn process_input(
type Os (line 406) | enum Os {
method fmt (line 416) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Arch (line 430) | enum Arch {
method fmt (line 436) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
function platform_to_string (line 446) | const fn platform_to_string(os: Os, arch: Arch) -> &'static str {
function user_agent_data (line 474) | fn user_agent_data(this: &NavigatorExt) -> Option<NavigatorUAData>;
function get_high_entropy_values (line 478) | async fn get_high_entropy_values(this: &NavigatorUAData, hints: Vec<Stri...
function mobile (line 482) | fn mobile(this: &HighEntropyValues) -> bool;
function platform (line 486) | fn platform(this: &HighEntropyValues) -> String;
function architecture (line 490) | fn architecture(this: &HighEntropyValues) -> String;
function detect_user_platform (line 494) | async fn detect_user_platform() -> anyhow::Result<(Os, Arch)> {
function download_url_by_os (line 552) | fn download_url_by_os(os: Os, arch: Arch) -> String {
function set_download_versions (line 564) | fn set_download_versions(document: &web_sys::Document) {
function finish_loading (line 634) | fn finish_loading(document: &web_sys::Document, tx: &NesEventProxy) -> a...
method initialize (line 646) | fn initialize(&mut self) -> anyhow::Result<()> {
method initialize (line 664) | fn initialize(&mut self) -> anyhow::Result<()> {
function download_save_states (line 797) | pub fn download_save_states() -> anyhow::Result<()> {
method with_platform (line 857) | fn with_platform(self, _title: &str) -> Self {
constant CANVAS (line 866) | pub(super) const CANVAS: &str = "frame";
constant LOADING_STATUS (line 867) | pub(super) const LOADING_STATUS: &str = "loading-status";
constant ROM_INPUT (line 868) | pub(super) const ROM_INPUT: &str = "load-rom";
constant REPLAY_INPUT (line 869) | pub(super) const REPLAY_INPUT: &str = "load-replay";
constant VERSION (line 870) | pub(super) const VERSION: &str = "version";
constant VERSION_DOWNLOAD (line 871) | pub(super) const VERSION_DOWNLOAD: &str = "version-download";
constant VERSION_OPTIONS (line 872) | pub(super) const VERSION_OPTIONS: &str = "version-options";
constant SELECTED_VERSION (line 873) | pub(super) const SELECTED_VERSION: &str = "selected-version";
constant WINDOWS_X86_LINK (line 874) | pub(super) const WINDOWS_X86_LINK: &str = "x86_64-pc-windows-msvc";
constant MACOS_X86_LINK (line 875) | pub(super) const MACOS_X86_LINK: &str = "x86_64-apple-darwin";
constant MACOS_AARCH64_LINK (line 876) | pub(super) const MACOS_AARCH64_LINK: &str = "aarch64-apple-darwin";
constant LINUX_X86_LINK (line 877) | pub(super) const LINUX_X86_LINK: &str = "x86_64-unknown-linux-gnu";
function get_canvas (line 881) | pub fn get_canvas() -> Option<web_sys::HtmlCanvasElement> {
function focus_canvas (line 889) | pub fn focus_canvas() {
FILE: tetanes/src/sys/thread/os.rs
function spawn_impl (line 5) | pub fn spawn_impl<F>(future: F)
function park_timeout_impl (line 14) | pub fn park_timeout_impl(dur: Duration) {
function sleep_impl (line 28) | pub async fn sleep_impl(dur: Duration) {
FILE: tetanes/src/sys/thread/wasm.rs
function spawn_impl (line 7) | pub fn spawn_impl<F>(future: F)
function park_timeout_impl (line 16) | pub fn park_timeout_impl(_dur: Duration) {}
function sleep_impl (line 19) | pub async fn sleep_impl(dur: Duration) {
FILE: tetanes/src/thread.rs
function spawn (line 6) | pub fn spawn<F>(future: F)
function park_timeout (line 15) | pub fn park_timeout(dur: Duration) {
function sleep (line 20) | pub async fn sleep(dur: Duration) {
Condensed preview — 534 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,613K chars).
[
{
"path": ".cargo/config.toml",
"chars": 210,
"preview": "[build]\nrustflags = [\"-Z\", \"threads=8\"]\n\n[target.'cfg(target_arch = \"wasm32\")']\nrustflags = [\n \"-Zthreads=8\",\n \"-Zwasm"
},
{
"path": ".config/nextest.toml",
"chars": 103,
"preview": "[profile.ci]\nfail-fast = false\nslow-timeout = { period = \"30s\", terminate-after = 4 }\ntest-threads = 1\n"
},
{
"path": ".git-blame-ignore-revs",
"chars": 0,
"preview": ""
},
{
"path": ".gitattributes",
"chars": 94,
"preview": "*.rs linguist-detectable=true\n*.js linguist-detectable=false\n*.html linguist-detectable=false\n"
},
{
"path": ".github/ISSUE_TEMPLATE/defect-report.md",
"chars": 1176,
"preview": "---\nname: Defect Report\nabout: Report issues to improve TetaNES\ntitle: ''\nlabels: needs-triage bug\nassignees: lukexor\n\n-"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 590,
"preview": "---\nname: Feature Request\nabout: Suggest an idea\ntitle: ''\nlabels: needs-triage enhancement\nassignees: lukexor\n\n---\n\n## "
},
{
"path": ".github/dependabot.yml",
"chars": 256,
"preview": "---\nversion: 2\nupdates:\n - package-ecosystem: \"github-actions\"\n directory: \"/\"\n schedule:\n interval: \"weekly"
},
{
"path": ".github/workflows/cd.yml",
"chars": 8337,
"preview": "---\nname: CD\n\n# yamllint disable-line rule:truthy\non:\n release:\n types: [published]\n workflow_dispatch:\n inputs:"
},
{
"path": ".github/workflows/ci.yml",
"chars": 5113,
"preview": "---\nname: CI\n\n# yamllint disable-line rule:truthy\non:\n push:\n branches: [main]\n paths-ignore:\n - \"**.md\"\n p"
},
{
"path": ".github/workflows/outdated.yml",
"chars": 1029,
"preview": "---\nname: Check Outdated\n\n# yamllint disable-line rule:truthy\non:\n schedule:\n # At 06:00 on day-of-month 2 and 16\n "
},
{
"path": ".github/workflows/release-pr.yml",
"chars": 1357,
"preview": "---\nname: Release PR\n\n# yamllint disable-line rule:truthy\non:\n push:\n branches: [main]\n\npermissions:\n pull-requests"
},
{
"path": ".github/workflows/security.yml",
"chars": 410,
"preview": "---\nname: Security Audit\n\n# yamllint disable-line rule:truthy\non:\n schedule:\n # At 06:00 once a week on Sunday\n -"
},
{
"path": ".github/workflows/triage.yml",
"chars": 374,
"preview": "---\nname: Triage Issues\n\n# yamllint disable-line rule:truthy\non:\n issues:\n types: [opened, reopened]\n\npermissions:\n "
},
{
"path": ".gitignore",
"chars": 88,
"preview": "tmp/\ntest_results*\ntarget/\n.DS_Store\n!assets/macos/.DS_Store\nlogs/\ndist/\n.direnv\nperf.*\n"
},
{
"path": ".gitmodules",
"chars": 0,
"preview": ""
},
{
"path": ".prettierignore",
"chars": 6,
"preview": "*.wxs\n"
},
{
"path": ".rgignore",
"chars": 20,
"preview": "roms\ntest_roms\ndocs\n"
},
{
"path": "Cargo.toml",
"chars": 2495,
"preview": "# Disabled for now since it was ICEing\n# cargo-features = [\"codegen-backend\"]\n\n[workspace]\nresolver = \"2\"\nmembers = [\"te"
},
{
"path": "Cross.toml",
"chars": 220,
"preview": "[build]\npre-build = [\n \"dpkg --add-architecture $CROSS_DEB_ARCH\",\n \"\"\"apt update && apt install -y \\\n libudev-dev"
},
{
"path": "LICENSE-APACHE",
"chars": 10849,
"preview": " Apache License\n Version 2.0, January 2004\n http"
},
{
"path": "LICENSE-MIT",
"chars": 1094,
"preview": "MIT License\n\nCopyright (c) 2021 Luke Petherbridge <me@lukeworks.tech>\n\nPermission is hereby granted, free of charge, to "
},
{
"path": "Makefile.toml",
"chars": 4389,
"preview": "[env]\nCARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true\n\n[config]\nreduce_output = false\nskip_core_tasks = true\ndefault_to_work"
},
{
"path": "README.md",
"chars": 25143,
"preview": "<!-- markdownlint-disable no-inline-html no-duplicate-heading -->\n\n# TetaNES\n\n[![Build Status]][build] [![Doc Status]][d"
},
{
"path": "ROADMAP.md",
"chars": 3360,
"preview": "# Roadmap\n\n- NES Formats & Run Modes\n - [x] NTSC\n - [x] PAL\n - [x] Dendy\n - [x] Headless mode\n- Central Processing U"
},
{
"path": "assets/linux/tetanes.desktop",
"chars": 86,
"preview": "[Desktop Entry]\nName=TetaNES\nExec=tetanes\nIcon=icon\nType=Application\nCategories=Game;\n"
},
{
"path": "assets/macos/Info.plist",
"chars": 613,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"https://www.apple.com/DTDs/"
},
{
"path": "cliff.toml",
"chars": 2859,
"preview": "[changelog]\nheader = \"\"\"\n<!-- markdownlint-disable-file no-duplicate-heading -->\n\n# Changelog\n\nAll notable changes to th"
},
{
"path": "deny.toml",
"chars": 2309,
"preview": "[graph]\nall-features = true\nno-default-features = false\n\n[output]\nfeature-depth = 1\n\n[advisories]\nversion = 2\ndb-path = "
},
{
"path": "docs/apu/apu_ref.txt",
"chars": 26381,
"preview": "NES APU Sound Hardware Reference\n--------------------------------\n\nThis reference covers Nintendo Entertainment System ("
},
{
"path": "docs/apu/audio_psuedo_code.txt",
"chars": 20035,
"preview": "LENGTH_TABLE // used by $4003 WRITE Pulse1/2\n\nAPU\n sequencer_mode // $4017 WRITE D7, clock_frame_sequenc"
},
{
"path": "docs/apu/blargg_tests_readme.txt",
"chars": 4788,
"preview": "NES APU Frame Counter Update\n----------------------------\n\nI have run more tests on the NES APU and come up with new inf"
},
{
"path": "docs/apu/mixer_readme.txt",
"chars": 3940,
"preview": "NES APU Mixer Tests\r\n-------------------\r\nThese tests verify proper operation of the NES APU's sound channel\r\nmixer, inc"
},
{
"path": "docs/apu/test_readme.txt",
"chars": 5693,
"preview": "NES APU Tests\n-------------\nThese ROMs test many aspects of the APU that are visible to the CPU.\nReally obsucre things a"
},
{
"path": "docs/apu/volume_readme.txt",
"chars": 3831,
"preview": "Volume tests for NES\r\n\r\n_____________________________________________________________________\r\nBackground\r\n\r\nThe NES has"
},
{
"path": "docs/cartridge_board_list.txt",
"chars": 55392,
"preview": "10 Yard Fight Nintendo E NROM\n110 in 1 (Canada?) Supervision\n"
},
{
"path": "docs/cpu/branch_timing_readme.txt",
"chars": 2320,
"preview": "NES 6502 Branch Timing Test ROMs\n--------------------------------\nThese ROMs test timing of the branch instruction, incl"
},
{
"path": "docs/cpu/dummy_writes_readme.txt",
"chars": 6535,
"preview": "NES Double-Write Behavior Tests\n----------------------------------\nThese tests verify that the CPU is doing double-write"
},
{
"path": "docs/cpu/exec_space_readme.txt",
"chars": 6890,
"preview": "NES Memory Execution Tests\n----------------------------------\nThese tests verify that the CPU can execute code from any "
},
{
"path": "docs/cpu/instr_misc_readme.txt",
"chars": 3962,
"preview": "NES CPU Instruction Behavior Misc Tests\r\n----------------------------------------\r\nThese tests verify miscellaneous inst"
},
{
"path": "docs/cpu/instr_test_readme.txt",
"chars": 9115,
"preview": "NES CPU Instruction Behavior Tests\r\n----------------------------------\r\nThese tests verify most instruction behavior fai"
},
{
"path": "docs/cpu/instr_timing_readme.txt",
"chars": 4005,
"preview": "NES CPU Instruction Timing Test\r\n-------------------------------\r\nThese tests verify timing of all NES CPU instructions,"
},
{
"path": "docs/cpu/interrupts_readme.txt",
"chars": 7111,
"preview": "NES CPU Interrupt Tests\n-----------------------\nTests behavior and timing of CPU in the presence of interrupts, both IRQ"
},
{
"path": "docs/cpu/opcode_list.txt",
"chars": 19200,
"preview": " 0 #00 BRK : mode: 6 Implied size: 2 cycles: 7 page_cycles: 0\n 1 #01 ORA : mode: 7 IndexedIndirect size:"
},
{
"path": "docs/cpu/reset_readme.txt",
"chars": 3396,
"preview": "CPU Power/Reset Tests\r\n---------------------\r\nVerifies CPU register values at power, and changes that occur during\r\nrese"
},
{
"path": "docs/genie_codes",
"chars": 440,
"preview": "Castlevania:\nSZSVLYSA : Infinite Health\nAVEEZZSA : Infinite Energy\nOXNGLZVK : Infinite Lives\nASOGOPIA : Start w/ 80 Hear"
},
{
"path": "docs/mapper/000.txt",
"chars": 354,
"preview": "\r\n========================\r\n= Mapper 000 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nNROM\r"
},
{
"path": "docs/mapper/001.txt",
"chars": 9856,
"preview": "\r\n========================\r\n= Mapper 001 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nMMC1\r"
},
{
"path": "docs/mapper/002.txt",
"chars": 1117,
"preview": "\r\n========================\r\n= Mapper 002 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nUxROM"
},
{
"path": "docs/mapper/003.txt",
"chars": 1063,
"preview": "\r\n========================\r\n= Mapper 003 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nCNROM"
},
{
"path": "docs/mapper/004.txt",
"chars": 12642,
"preview": "\r\n========================\r\n= Mapper 004 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nMMC3\r"
},
{
"path": "docs/mapper/005.txt",
"chars": 20681,
"preview": "\r\n========================\r\n= Mapper 005 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nMMC5\r"
},
{
"path": "docs/mapper/007.txt",
"chars": 866,
"preview": "\r\n========================\r\n= Mapper 007 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nAxROM"
},
{
"path": "docs/mapper/009.txt",
"chars": 2039,
"preview": "\r\n========================\r\n= Mapper 009 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nMMC2\r"
},
{
"path": "docs/mapper/010.txt",
"chars": 542,
"preview": "\r\n========================\r\n= Mapper 010 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nMMC4\r"
},
{
"path": "docs/mapper/011.txt",
"chars": 613,
"preview": "\r\n========================\r\n= Mapper 011 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/013.txt",
"chars": 782,
"preview": "\r\n========================\r\n= Mapper 013 =\r\n========================\r\n\r\n\r\naka:\r\n--------------------------\r\nCP"
},
{
"path": "docs/mapper/015.txt",
"chars": 1255,
"preview": "\r\n========================\r\n= Mapper 015 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/016.txt",
"chars": 10240,
"preview": "\r\n========================\r\n= Mapper 016 =\r\n= + 159 =\r\n========================\r\n\r\naka\r\n-------"
},
{
"path": "docs/mapper/018.txt",
"chars": 2284,
"preview": "\r\n========================\r\n= Mapper 018 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/019.txt",
"chars": 9137,
"preview": "\r\n========================\r\n= Mapper 019 =\r\n= + 210 =\r\n========================\r\n\r\naka\r\n-------"
},
{
"path": "docs/mapper/021.txt",
"chars": 6806,
"preview": "\r\n========================\r\n= Mapper 021 =\r\n= + 023 =\r\n= + 025 = \r\n============"
},
{
"path": "docs/mapper/022.txt",
"chars": 3305,
"preview": " ========================\r\n= Mapper 022 =\r\n= + 023 =\r\n= + 025 =\r\n==============="
},
{
"path": "docs/mapper/023.txt",
"chars": 268,
"preview": "\r\n========================\r\n= Mapper 023 =\r\n========================\r\n\r\n\r\nMapper 023 includes both a variant o"
},
{
"path": "docs/mapper/024.txt",
"chars": 5833,
"preview": "\r\n========================\r\n= Mapper 024 =\r\n= + 026 =\r\n========================\r\n\r\naka\r\n-------"
},
{
"path": "docs/mapper/025.txt",
"chars": 180,
"preview": "\r\n========================\r\n= Mapper 025 =\r\n========================\r\n\r\n\r\nMapper 025 is a variant of VRC4, whi"
},
{
"path": "docs/mapper/026.txt",
"chars": 180,
"preview": "\r\n========================\r\n= Mapper 026 =\r\n========================\r\n\r\n\r\nMapper 026 is a variant of VRC6, whi"
},
{
"path": "docs/mapper/032.txt",
"chars": 1536,
"preview": "\r\n ========================\r\n = Mapper 032 =\r\n ========================\r\n \r\n Example Games:\r\n ----------------"
},
{
"path": "docs/mapper/033.txt",
"chars": 1487,
"preview": "\r\n========================\r\n= Mapper 033 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/034.txt",
"chars": 2122,
"preview": "\r\n========================\r\n= Mapper 034 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nBxROM"
},
{
"path": "docs/mapper/044.txt",
"chars": 1566,
"preview": "\r\n========================\r\n= Mapper 044 =\r\n========================\r\n\r\n\r\nExample Game:\r\n---------------------"
},
{
"path": "docs/mapper/045.txt",
"chars": 2400,
"preview": "\r\n========================\r\n= Mapper 045 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/046.txt",
"chars": 591,
"preview": "\r\n========================\r\n= Mapper 046 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/047.txt",
"chars": 589,
"preview": "\r\n========================\r\n= Mapper 047 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/048.txt",
"chars": 2465,
"preview": "\r\n========================\r\n= Mapper 048 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/049.txt",
"chars": 981,
"preview": "\r\n========================\r\n= Mapper 049 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/050.txt",
"chars": 1193,
"preview": "\r\n========================\r\n= Mapper 050 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/052.txt",
"chars": 1663,
"preview": "\r\n========================\r\n= Mapper 052 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/057.txt",
"chars": 1039,
"preview": "\r\n========================\r\n= Mapper 057 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/058.txt",
"chars": 710,
"preview": "\r\n========================\r\n= Mapper 058 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/060.txt",
"chars": 520,
"preview": "\r\n========================\r\n= Mapper 060 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/061.txt",
"chars": 727,
"preview": "\r\n========================\r\n= Mapper 061 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/062.txt",
"chars": 851,
"preview": "\r\n========================\r\n= Mapper 062 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/064.txt",
"chars": 5610,
"preview": "\r\n========================\r\n= Mapper 064 =\r\n========================\r\n\r\n\r\naka\r\n--------------------------\r\nTen"
},
{
"path": "docs/mapper/065.txt",
"chars": 1981,
"preview": "\r\n========================\r\n= Mapper 065 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/066.txt",
"chars": 786,
"preview": "\r\n========================\r\n= Mapper 066 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nGxROM"
},
{
"path": "docs/mapper/067.txt",
"chars": 1788,
"preview": "\r\n========================\r\n= Mapper 067 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/068.txt",
"chars": 1881,
"preview": "\r\n========================\r\n= Mapper 068 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/069.txt",
"chars": 6675,
"preview": "\r\n========================\r\n= Mapper 069 =\r\n========================\r\n\r\n\r\naka\r\n--------------------------\r\nFME"
},
{
"path": "docs/mapper/070.txt",
"chars": 762,
"preview": "\r\n========================\r\n= Mapper 070 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/071.txt",
"chars": 1739,
"preview": "\r\n========================\r\n= Mapper 071 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/072.txt",
"chars": 2468,
"preview": "========================\r\n= Mapper 072 =\r\n========================\r\n\r\nExample Games:\r\n------------------------"
},
{
"path": "docs/mapper/073.txt",
"chars": 2504,
"preview": "\r\n========================\r\n= Mapper 073 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nVRC3\r"
},
{
"path": "docs/mapper/074.txt",
"chars": 598,
"preview": "\r\n========================\r\n= Mapper 074 =\r\n========================\r\n\r\n\r\naka:\r\n--------------------------\r\nPi"
},
{
"path": "docs/mapper/075.txt",
"chars": 1416,
"preview": "\r\n========================\r\n= Mapper 075 =\r\n========================\r\n\r\n\r\naka:\r\n--------------------------\r\nVR"
},
{
"path": "docs/mapper/076.txt",
"chars": 1318,
"preview": "\r\n ========================\r\n = Mapper 076 =\r\n ========================\r\n \r\n \r\n Example Games:\r\n -------------"
},
{
"path": "docs/mapper/077.txt",
"chars": 972,
"preview": "========================\r\n= Mapper 077 =\r\n========================\r\n\r\nExample Game:\r\n-------------------------"
},
{
"path": "docs/mapper/078.txt",
"chars": 1276,
"preview": "\r\n========================\r\n= Mapper 078 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/079.txt",
"chars": 491,
"preview": "\r\n========================\r\n= Mapper 079 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/080.txt",
"chars": 1281,
"preview": "========================\r\n= Mapper 080 =\r\n========================\r\n\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/082.txt",
"chars": 1455,
"preview": "\r\n========================\r\n= Mapper 082 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/085.txt",
"chars": 22141,
"preview": "\r\n========================\r\n= Mapper 085 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nVRC7\r"
},
{
"path": "docs/mapper/086.txt",
"chars": 1089,
"preview": "\r\n========================\r\n= Mapper 086 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/087.txt",
"chars": 545,
"preview": "\r\n========================\r\n= Mapper 087 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/088.txt",
"chars": 1381,
"preview": "\r\n========================\r\n= Mapper 088 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/089.txt",
"chars": 548,
"preview": "\r\n========================\r\n= Mapper 089 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/090.txt",
"chars": 12911,
"preview": "\r\n========================\r\n= Mapper 090 =\r\n= + 209 =\r\n========================\r\n\r\naka\r\n-------"
},
{
"path": "docs/mapper/091.txt",
"chars": 1494,
"preview": "\r\n========================\r\n= Mapper 091 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/092.txt",
"chars": 538,
"preview": "\r\n========================\r\n= Mapper 092 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/093.txt",
"chars": 524,
"preview": "\r\n========================\r\n= Mapper 093 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/094.txt",
"chars": 465,
"preview": "\r\n========================\r\n= Mapper 094 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/095.txt",
"chars": 1003,
"preview": "\r\n========================\r\n= Mapper 095 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nMMC3 "
},
{
"path": "docs/mapper/096.txt",
"chars": 3495,
"preview": "\r\n========================\r\n= Mapper 096 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/097.txt",
"chars": 635,
"preview": "\r\n========================\r\n= Mapper 097 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/105.txt",
"chars": 3577,
"preview": "\r\n========================\r\n= Mapper 105 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nNES-E"
},
{
"path": "docs/mapper/107.txt",
"chars": 461,
"preview": "\r\n========================\r\n= Mapper 107 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/112.txt",
"chars": 1166,
"preview": "\r\n========================\r\n= Mapper 112 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/113.txt",
"chars": 524,
"preview": "\r\n========================\r\n= Mapper 113 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/115.txt",
"chars": 1175,
"preview": "\r\n========================\r\n= Mapper 115 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/118.txt",
"chars": 1016,
"preview": "\r\n========================\r\n= Mapper 118 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nMMC3 "
},
{
"path": "docs/mapper/119.txt",
"chars": 532,
"preview": "\r\n========================\r\n= Mapper 119 =\r\n========================\r\n\r\naka\r\n--------------------------\r\nTQROM"
},
{
"path": "docs/mapper/140.txt",
"chars": 388,
"preview": "\r\n========================\r\n= Mapper 140 =\r\n========================\r\n\r\n\r\nExample Game:\r\n---------------------"
},
{
"path": "docs/mapper/152.txt",
"chars": 582,
"preview": "\r\n========================\r\n= Mapper 152 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/154.txt",
"chars": 1363,
"preview": "\r\n========================\r\n= Mapper 154 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/159.txt",
"chars": 162,
"preview": "\r\n========================\r\n= Mapper 159 =\r\n========================\r\n\r\nThis mapper is covered in full in mapp"
},
{
"path": "docs/mapper/164.txt",
"chars": 597,
"preview": "\r\n========================\r\n= Mapper 164 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/165.txt",
"chars": 1495,
"preview": "\r\n========================\r\n= Mapper 165 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/180.txt",
"chars": 793,
"preview": "\r\n========================\r\n= Mapper 180 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/182.txt",
"chars": 1268,
"preview": "\r\n========================\r\n= Mapper 182 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/184.txt",
"chars": 422,
"preview": "\r\n========================\r\n= Mapper 184 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/185.txt",
"chars": 1072,
"preview": "========================\r\n= Mapper 185 =\r\n========================\r\n\r\nExample Games:\r\n------------------------"
},
{
"path": "docs/mapper/189.txt",
"chars": 879,
"preview": "\r\n========================\r\n= Mapper 189 =\r\n========================\r\n\r\n\r\nExample Game:\r\n---------------------"
},
{
"path": "docs/mapper/191.txt",
"chars": 582,
"preview": "\r\n========================\r\n= Mapper 191 =\r\n========================\r\n\r\n\r\naka:\r\n--------------------------\r\nPi"
},
{
"path": "docs/mapper/192.txt",
"chars": 566,
"preview": "\r\n========================\r\n= Mapper 192 =\r\n========================\r\n\r\n\r\naka:\r\n--------------------------\r\nPi"
},
{
"path": "docs/mapper/193.txt",
"chars": 881,
"preview": "\r\n========================\r\n= Mapper 193 =\r\n========================\r\n\r\n\r\nExample Game:\r\n---------------------"
},
{
"path": "docs/mapper/194.txt",
"chars": 578,
"preview": "\r\n========================\r\n= Mapper 194 =\r\n========================\r\n\r\n\r\naka:\r\n--------------------------\r\nPi"
},
{
"path": "docs/mapper/200.txt",
"chars": 827,
"preview": "\r\n========================\r\n= Mapper 200 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/201.txt",
"chars": 804,
"preview": "\r\n========================\r\n= Mapper 201 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/203.txt",
"chars": 780,
"preview": "\r\n========================\r\n= Mapper 203 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/205.txt",
"chars": 1103,
"preview": "\r\n========================\r\n= Mapper 205 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/207.txt",
"chars": 1388,
"preview": "\r\n========================\r\n= Mapper 207 =\r\n========================\r\n\r\n\r\nExample Game:\r\n---------------------"
},
{
"path": "docs/mapper/209.txt",
"chars": 162,
"preview": "\r\n========================\r\n= Mapper 209 =\r\n========================\r\n\r\nThis mapper is covered in full in mapp"
},
{
"path": "docs/mapper/210.txt",
"chars": 162,
"preview": "\r\n========================\r\n= Mapper 210 =\r\n========================\r\n\r\nThis mapper is covered in full in mapp"
},
{
"path": "docs/mapper/225.txt",
"chars": 1244,
"preview": "\r\n========================\r\n= Mapper 225 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/226.txt",
"chars": 803,
"preview": "\r\n========================\r\n= Mapper 226 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/227.txt",
"chars": 1407,
"preview": "\r\n========================\r\n= Mapper 227 =\r\n========================\r\n\r\n\r\nExample Game:\r\n---------------------"
},
{
"path": "docs/mapper/228.txt",
"chars": 2056,
"preview": "\r\n========================\r\n= Mapper 228 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/230.txt",
"chars": 1454,
"preview": "\r\n========================\r\n= Mapper 230 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/231.txt",
"chars": 599,
"preview": "\r\n========================\r\n= Mapper 231 =\r\n========================\r\n\r\nExample Game:\r\n-----------------------"
},
{
"path": "docs/mapper/232.txt",
"chars": 909,
"preview": "\r\n========================\r\n= Mapper 232 =\r\n========================\r\n\r\nExample Games:\r\n----------------------"
},
{
"path": "docs/mapper/233.txt",
"chars": 4901,
"preview": "\r\n========================\r\n= Mapper 233 =\r\n========================\r\n\r\n\r\nExample Game:\r\n---------------------"
},
{
"path": "docs/mapper/234.txt",
"chars": 1973,
"preview": "========================\r\n= Mapper 234 =\r\n========================\r\n\r\nExample Game:\r\n-------------------------"
},
{
"path": "docs/mapper/240.txt",
"chars": 303,
"preview": "\r\n========================\r\n= Mapper 240 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/242.txt",
"chars": 388,
"preview": "\r\n========================\r\n= Mapper 242 =\r\n========================\r\n\r\n\r\nExample Game:\r\n---------------------"
},
{
"path": "docs/mapper/243.txt",
"chars": 882,
"preview": "\r\n========================\r\n= Mapper 243 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/245.txt",
"chars": 1906,
"preview": "\r\n========================\r\n= Mapper 245 =\r\n========================\r\n\r\n\r\nExample Games:\r\n--------------------"
},
{
"path": "docs/mapper/246.txt",
"chars": 1167,
"preview": "\r\n========================\r\n= Mapper 246 =\r\n========================\r\n\r\n\r\nExample Game:\r\n---------------------"
},
{
"path": "docs/mapper/__ READ THIS FIRST __.txt",
"chars": 12822,
"preview": "\r\n ******************************************\r\n * iNES Mappers by Mapper Number *\r\n * "
},
{
"path": "docs/mapper/changes.txt",
"chars": 2352,
"preview": "v0.7 (Bregalad)\r\n================================\r\n- Imported changes from NESDEV wiki:\r\n - mapper 005 : - Correct i"
},
{
"path": "docs/mapper/mmc3_irq_tests_readme.txt",
"chars": 4688,
"preview": "NTSC NES MMC3 IRQ Counter Test ROMs\n-----------------------------------\nThese ROMs test much of MMC3 IRQ counter behavio"
},
{
"path": "docs/mapper/mmc3_test_readme.txt",
"chars": 5046,
"preview": "NES MMC3 Tests\r\n--------------\r\nThese tests verify a small part of MMC3 (and some MMC6) behavior, mostly\r\nrelated to the"
},
{
"path": "docs/memory_mapping.txt",
"chars": 19505,
"preview": " **************************************\n ** **\n"
},
{
"path": "docs/nes_arch.txt",
"chars": 24530,
"preview": "\n Nintendo Entertainment System Architecture\n version 1.4 [09/09/1996]\n\n "
},
{
"path": "docs/nes_graphics.txt",
"chars": 4567,
"preview": "How NES Graphics work\nThe Basics\n---------------------\n\n All the graphical information is stored within the 16kb "
},
{
"path": "docs/nes_tech.txt",
"chars": 65920,
"preview": "+---------------------------------------------+\n| Nintendo Entertainment System Documentation |\n| "
},
{
"path": "docs/ppu/blargg_tests_readme.txt",
"chars": 2305,
"preview": "NTSC NES PPU Tests\n------------------\nThese ROMs test a few aspects of the NTSC NES PPU operation. They have been\ntested"
},
{
"path": "docs/ppu/nmi_sync_ntsc_readme.txt",
"chars": 9003,
"preview": "NES Precise NMI Synchronization\r\n-------------------------------\r\nThis library allows synchronizing exactly to the PPU f"
},
{
"path": "docs/ppu/oam_read_readme.txt",
"chars": 4224,
"preview": "NES OAM Read Test\r\n-----------------\r\nTests OAM reading ($2004), being sure it reads the byte from OAM at the\r\ncurrent a"
},
{
"path": "docs/ppu/oam_stress_readme.txt",
"chars": 3323,
"preview": "NES OAM Stress Test\r\n-------------------\r\nThoroughly tests OAM address ($2003) and read/write ($2004). On an NTSC\r\nNES, "
},
{
"path": "docs/ppu/open_bus_readme.txt",
"chars": 3686,
"preview": "NES PPU Open-Bus Test\r\n---------------------\r\nTests behavior when reading from open-bus PPU bits/registers, those bits\r\n"
},
{
"path": "docs/ppu/ppu_2c02_ref.txt",
"chars": 39720,
"preview": "*******************************\n*NTSC 2C02 technical reference*\n*******************************\nBrad Taylor (BTTDgroup@h"
},
{
"path": "docs/ppu/ppu_scrolling.txt",
"chars": 3807,
"preview": "Subject: [nesdev] the skinny on nes scrolling\r\nDate: Tue, 13 Apr 1999 16:42:00 -0600\r\nFrom: loopy <zxcvzxcv@netzero.net>"
},
{
"path": "docs/ppu/read_buffer_test_readme.txt",
"chars": 15138,
"preview": "NES PPU Read Buffer Tests\n----------------------------------\nThis mammoth test pack tests many aspects of the NES system"
},
{
"path": "docs/ppu/sprite_hit_readme.txt",
"chars": 7337,
"preview": "\n\n01-basics\n---------\nTests basic sprite 0 hit behavior (nothing timing related).\n\n2) Flag isn't working at all\n3) Shoul"
},
{
"path": "docs/ppu/sprite_overflow_readme.txt",
"chars": 5996,
"preview": "\n\n01-basics\n---------\nTests basic operation of sprite overflow flag (bit 5 of $2002).\n\n2) Should set flag when 9 sprites"
},
{
"path": "docs/ppu/tv_readme.txt",
"chars": 2246,
"preview": "TV pass or fail?\r\n\r\nThis program is designed for NES and tests various aspects of the\r\ndisplay it is connected to. Pres"
},
{
"path": "docs/ppu/vbl_nmi_readme.txt",
"chars": 6532,
"preview": "NES PPU Tests\n-------------\nThese tests verify the behavior and timing of the NTSC PPU's VBL flag,\nNMI enable, and NMI i"
},
{
"path": "docs/ppu/vbl_nmi_timing_readme.txt",
"chars": 4425,
"preview": "NTSC NES PPU VBL/NMI Timing Tests\n---------------------------------\nThese ROMs test the timing of the VBL flag and NMI t"
},
{
"path": "release-plz.toml",
"chars": 393,
"preview": "[workspace]\nallow_dirty = false\nchangelog_config = \"cliff.toml\"\nchangelog_update = true\ndependencies_update = true\ngit_r"
},
{
"path": "rust-toolchain.toml",
"chars": 146,
"preview": "[toolchain]\nchannel = \"nightly\"\ncomponents = [\"rustfmt\", \"clippy\", \"llvm-tools-preview\"]\ntargets = [\"wasm32-unknown-unkn"
},
{
"path": "tetanes/CHANGELOG.md",
"chars": 40266,
"preview": "<!-- markdownlint-disable-file no-duplicate-heading -->\n\n# Changelog\n\nAll notable changes to this project will be docume"
},
{
"path": "tetanes/Cargo.toml",
"chars": 4632,
"preview": "[package]\nname = \"tetanes\"\nversion.workspace = true\nedition.workspace = true\nlicense.workspace = true\ndescription = \"A c"
},
{
"path": "tetanes/assets/main.css",
"chars": 2678,
"preview": "@font-face {\n font-family: \"Pixeloid Sans\";\n src:\n local(\"Pixeloid Sans\"),\n url(\"./pixeloid-sans.ttf\") format(\"t"
},
{
"path": "tetanes/assets/pixeloid-license.txt",
"chars": 4396,
"preview": "Copyright © 2020-2021 GGBot (https://ggbot.net/fonts/), with Reserved Font Name \"Pixeloid\".\n\nThis Font Software is lice"
},
{
"path": "tetanes/assets/roms/alter_ego.txt",
"chars": 5614,
"preview": "Development notes\n\nAfter completing Lawn Master, I had plan to try use C compiler to make a simple\nNES game. From my pre"
},
{
"path": "tetanes/assets/roms/ao_demo.txt",
"chars": 1207,
"preview": "AO\nby Second Dimension\n\nAO is a homebrew puzzle game. Each of the 33 levels consists of a fixed, single\nscreen environme"
},
{
"path": "tetanes/assets/roms/assimilate.txt",
"chars": 659,
"preview": "Assimilate\nby Nessylum Games\n\nEver wanted to anal probe someone? Well the wait is over!\n\nAssimilate is the game you and "
},
{
"path": "tetanes/assets/roms/blade_buster.txt",
"chars": 403,
"preview": "Blade Buster\nby High Level Challenge\n\nIt is produced within the constraints of the NES in fact, it runs on emulator\nand "
},
{
"path": "tetanes/assets/roms/cheril_the_goddess.txt",
"chars": 1187,
"preview": "Cheril the Goddess\nBy The Mojon Twins\n\nYou control Cheril. There’s plenty of things to do, and you’ll need special\nobjec"
},
{
"path": "tetanes/assets/roms/dushlan.txt",
"chars": 2692,
"preview": "////////////////////////////////////////////////////////////////////////////\n// Dúshlán "
},
{
"path": "tetanes/assets/roms/from_below.txt",
"chars": 952,
"preview": "From Below\nby Matt Hughson\n\nFROM BELOW is a falling block puzzle game featuring:\n\nSoft Drops Hard Drops Wall Kicks T-Spi"
},
{
"path": "tetanes/assets/roms/lan_master.txt",
"chars": 2389,
"preview": "Lan Master\nby Shiru\n\nDevelopment notes\n\nThe project was started circa November 2010. I wanted to do a simple game that\nw"
},
{
"path": "tetanes/assets/roms/lawn_mower.txt",
"chars": 196,
"preview": "Lawn Mower\nby Shiru\n\nThe goal of this game is to mow all the grass before you run out of gas. Collect\ngas cans to keep y"
},
{
"path": "tetanes/assets/roms/mad_wizard.txt",
"chars": 770,
"preview": "Mad Wizard\nBy Sly Dog Studios\n\nTake an adventure in the world of Candelabra!\n\nThe evil summoner Amondus from The Order o"
},
{
"path": "tetanes/assets/roms/micro_knight.txt",
"chars": 69,
"preview": "Micro Knight\nBy SDM\n\nhttps://forums.nesdev.org/viewtopic.php?t=13450\n"
},
{
"path": "tetanes/assets/roms/nebs_n_debs.txt",
"chars": 392,
"preview": "Nebs 'n Debs\nBy Dullahan Software\n\nNebs 'n Debs is a new game for the original Nintendo Entertainment System. Run,\njump,"
},
{
"path": "tetanes/assets/roms/owlia.txt",
"chars": 1490,
"preview": "The Legends of Owlia\nBy Gradual Games\n\nThe Legends of Owlia is Gradual Games' second release for the NES. It is an\nactio"
},
{
"path": "tetanes/assets/roms/streemerz.txt",
"chars": 1599,
"preview": "\n\n\n __| __| __| _ \\ _ \\ __ `__ \\ _ \\ __|_ /\n \\__ \\ | | __/ __/ | | | __/ | /\n "
},
{
"path": "tetanes/assets/roms/super_painter.txt",
"chars": 472,
"preview": "Super Painter\nBy RetroSouls & Kulor\n\nTrapped in a colorless world, armed with a paintbrush - there’s only one thing\nto d"
},
{
"path": "tetanes/assets/roms/tiger_jenny.txt",
"chars": 356,
"preview": "Tiger Jenny\nBy Ludosity\n\nTiger Jenny by Ludosity is a NES game set in the same universe as “Ittle Dew” it\ntakes place a "
},
{
"path": "tetanes/assets/roms/yun.txt",
"chars": 720,
"preview": "Yun\nBy The Mojon Twins\n\nThe main goal is helping yun capturing every single being to fill the pantry of\nher restaurant. "
}
]
// ... and 334 more files (download for full content)
About this extraction
This page contains the full source code of the lukexor/tetanes GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 534 files (16.1 MB), approximately 890.8k tokens, and a symbol index with 2241 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.