├── gba ├── tilesets │ ├── bggfx.grit │ ├── hepsie.grit │ ├── kikimap.grit │ ├── kikitiles.grit │ ├── convergence.grit │ ├── helpbgtiles.grit │ ├── helpsprites.grit │ ├── Donna.grit │ ├── stopwatchdigits.grit │ ├── Gus_portrait.grit │ ├── monoscope.grit │ ├── pluge_shark_6color.grit │ ├── sharpness.grit │ ├── spritegfx.grit │ ├── stopwatchface.grit │ ├── stopwatchhand.grit │ ├── greenhillzone.grit │ ├── Donna.png │ ├── bggfx.png │ ├── hepsie.png │ ├── kikimap.png │ ├── kikitiles.png │ ├── monoscope.png │ ├── sharpness.png │ ├── spritegfx.png │ ├── Gus_portrait.png │ ├── convergence.png │ ├── helpbgtiles.png │ ├── helpsprites.png │ ├── greenhillzone.png │ ├── stopwatchface.png │ ├── stopwatchhand.png │ └── stopwatchdigits.png ├── Makefile ├── wfconfig.toml ├── tools │ ├── write_bios.c │ ├── get_bios.sh │ ├── chord_prototype.py │ └── vwfbuild.py ├── src │ ├── rand.c │ ├── 4bcanvas.h │ ├── ppuclear.c │ ├── pads.c │ ├── vwflabels.c │ ├── undte.c │ ├── backlight.c │ ├── audiosync.c │ └── posprintf.h └── CHANGES.txt ├── common ├── tools │ ├── index.txt │ ├── dtefe.py │ ├── cp144p.py │ ├── cp240p.py │ └── parsepages.py ├── tilesets │ ├── index.txt │ ├── vwf7_cp144p.png │ ├── vwf7_cp240p.png │ ├── pluge_shark_4color.png │ └── pluge_shark_6color.png └── docs │ ├── repocard.xcf │ ├── Gus_frontrear.png │ ├── Gus_rear_256.png │ ├── gplv2_2sided.odg │ ├── pluge_shark.xcf │ ├── Gillian_and_Gus.xcf │ ├── Gus_sketch_2006.png │ ├── Gus_sketch_by_darryl.revok_ink.png │ ├── Gus_sketch_by_darryl.revok_paint.png │ ├── Gus_measured.txt │ ├── assets.md │ ├── pluge_shark.md │ ├── size_comparison.txt │ └── release_checklist.md ├── fc-mdf ├── obj │ └── nes │ │ └── index.txt ├── tilesets │ └── fizztersmboldmono.png ├── docs │ ├── roadmap.md │ └── sequence.md ├── README.md ├── src │ ├── global.inc │ ├── nes.inc │ └── main.s ├── tools │ └── mkines.py ├── vrc7.x └── makefile ├── nes ├── obj │ └── nes │ │ └── index.txt ├── audio │ └── instsamp1.dmc ├── docs │ ├── nes_label.xcf │ ├── hoist_sketch.png │ ├── lj65gus32x64.xcf │ ├── 240p_nes_manual.odg │ ├── with_games_nes_label.xcf │ ├── Game_collection.txt │ ├── rombanks.md │ ├── Manual_noscreenshot_page5.txt │ └── Manual_screenshot_pages.txt ├── tilesets │ ├── crttest.png │ ├── gus_bg.png │ ├── hepsie.png │ ├── kikimap.png │ ├── kikitiles.png │ ├── mdf4k_chr.png │ ├── overscan.png │ ├── powerpad.png │ ├── sharpness.png │ ├── stdtiles.png │ ├── arkanoidchr.png │ ├── convergence.png │ ├── gus_portrait.png │ ├── gus_sprite.png │ ├── megatontiles.png │ ├── monoscope50.png │ ├── monoscope60.png │ ├── pads_buttons.png │ ├── fizzter_digits.png │ ├── greenhillzone.png │ ├── lag_clock_face.png │ ├── safeareas_lite.png │ ├── serialanalyzer.png │ ├── shadow_reticle.png │ ├── stopwatchhand.png │ ├── backlight_sprites.png │ └── controllerimages.png ├── tools │ ├── unused.py │ ├── sav2sb53.py │ ├── vwfbuild.py │ ├── nestobin.py │ ├── mktables.py │ ├── ld65ramuse.py │ ├── chnutils.py │ └── widesb53.py ├── nsf.cfg ├── src │ ├── nes.inc │ ├── muldiv.s │ ├── undte.s │ ├── mdfouriernsf.s │ ├── mmc3.s │ ├── unrom.s │ ├── paldetect.s │ ├── rectfill.inc │ ├── rand.s │ ├── bnrom.s │ ├── init.s │ ├── mmc1.s │ ├── ppuclear.s │ └── hanover.s ├── mdfourier4k.cfg ├── mdfourier4k-chrrom.cfg ├── bnrom512kbit.cfg ├── unrom512kbit.cfg ├── sgrom512kbit.cfg └── tgrom512kbit.cfg ├── gameboy ├── tilesets │ ├── bggfx.png │ ├── cps_grid.png │ ├── grayramp.png │ ├── hepsie.png │ ├── kikimap.png │ ├── helpblink.png │ ├── helptiles.png │ ├── kikitiles.png │ ├── sgbborder.png │ ├── sharpness.png │ ├── smptetiles.png │ ├── spritegfx.png │ ├── Gus_portrait.png │ ├── backlightzone.png │ ├── convergence.png │ ├── grayramp-sgb.png │ ├── greenhillzone.png │ ├── helptiles-gbc.png │ ├── megatontiles.png │ ├── stopwatchface.png │ ├── stopwatchhand.png │ ├── Donna_portrait.png │ ├── shadow_reticle.png │ ├── stopwatchdigits.png │ ├── Gus_portrait-GBC.png │ ├── linearity-quadrant.png │ └── Gus_portrait-GBC.pal.txt ├── docs │ ├── Gus-bwtocolor.png │ └── sample_gb_manual.odg ├── obj │ └── gb │ │ └── index.txt ├── TODO.txt ├── tools │ ├── rand.py │ ├── pitchtable.py │ ├── yuvgen.py │ ├── mapunused.py │ ├── bitbyte.py │ ├── uniq.py │ ├── attic │ │ └── vwfrectexperiment.py │ ├── romusage.py │ ├── vwfbuild.py │ └── incruniq.py └── src │ ├── linkthislast.z80 │ ├── header.z80 │ ├── bcd.z80 │ ├── linkthisfirst.z80 │ ├── mdfourierfe.z80 │ ├── gb.inc │ ├── rand.z80 │ └── undte.z80 ├── nes-palpar ├── tilesets │ └── palpar.png ├── mk.sh ├── nrom128.cfg └── src │ └── main.s ├── CHANGES.txt ├── .gitattributes ├── .github ├── actions │ ├── cache_cc65 │ │ └── action.yml │ └── cache_rgbds │ │ └── action.yml └── workflows │ └── build-artifact.yml ├── .gitignore ├── README.md └── makefile /gba/tilesets/bggfx.grit: -------------------------------------------------------------------------------- 1 | -gB2 -p! -------------------------------------------------------------------------------- /gba/tilesets/hepsie.grit: -------------------------------------------------------------------------------- 1 | -gB2 -p! -------------------------------------------------------------------------------- /gba/Makefile: -------------------------------------------------------------------------------- 1 | include wfMakefile 2 | -------------------------------------------------------------------------------- /gba/tilesets/kikimap.grit: -------------------------------------------------------------------------------- 1 | -p! -gB2 -tw16 -------------------------------------------------------------------------------- /gba/tilesets/kikitiles.grit: -------------------------------------------------------------------------------- 1 | -p! -gB2 -Mh2 -------------------------------------------------------------------------------- /gba/tilesets/convergence.grit: -------------------------------------------------------------------------------- 1 | -p! -gB2 -o 2 | -------------------------------------------------------------------------------- /gba/tilesets/helpbgtiles.grit: -------------------------------------------------------------------------------- 1 | -gB4 -gzl -pn16 -------------------------------------------------------------------------------- /gba/tilesets/helpsprites.grit: -------------------------------------------------------------------------------- 1 | -gB4 -gzl -pn16 -------------------------------------------------------------------------------- /gba/tilesets/Donna.grit: -------------------------------------------------------------------------------- 1 | -gB4 -gzl -mRtf -mLf -pn16 -------------------------------------------------------------------------------- /gba/tilesets/stopwatchdigits.grit: -------------------------------------------------------------------------------- 1 | -p! -gB2 -o 2 | -------------------------------------------------------------------------------- /gba/tilesets/Gus_portrait.grit: -------------------------------------------------------------------------------- 1 | -gB4 -gzl -mRtf -mLf -pn16 -------------------------------------------------------------------------------- /gba/tilesets/monoscope.grit: -------------------------------------------------------------------------------- 1 | -p! -gB2 -mRtpf -mLs -mzr 2 | -------------------------------------------------------------------------------- /gba/tilesets/pluge_shark_6color.grit: -------------------------------------------------------------------------------- 1 | -gB4 -gzl -pn8 2 | -------------------------------------------------------------------------------- /gba/tilesets/sharpness.grit: -------------------------------------------------------------------------------- 1 | -p! -gB2 -mRtpf -mLs -mzr 2 | -------------------------------------------------------------------------------- /gba/tilesets/spritegfx.grit: -------------------------------------------------------------------------------- 1 | -gB4 -gzl -Mw2 -Mh3 -pn16 2 | -------------------------------------------------------------------------------- /gba/tilesets/stopwatchface.grit: -------------------------------------------------------------------------------- 1 | -p! -gB1 -mRtf -mLf 2 | -------------------------------------------------------------------------------- /gba/tilesets/stopwatchhand.grit: -------------------------------------------------------------------------------- 1 | -p! -gB2 -Mw2 -Mh2 2 | -------------------------------------------------------------------------------- /gba/tilesets/greenhillzone.grit: -------------------------------------------------------------------------------- 1 | -pn8 -gB4 -gzl -mRtpf -mLs -mzl -------------------------------------------------------------------------------- /common/tools/index.txt: -------------------------------------------------------------------------------- 1 | Tools common to multiple ports will go here. 2 | -------------------------------------------------------------------------------- /fc-mdf/obj/nes/index.txt: -------------------------------------------------------------------------------- 1 | Farmers Alliance Mutual Insurance Company 2 | -------------------------------------------------------------------------------- /common/tilesets/index.txt: -------------------------------------------------------------------------------- 1 | Graphics common to multiple ports will go here. 2 | -------------------------------------------------------------------------------- /nes/obj/nes/index.txt: -------------------------------------------------------------------------------- 1 | Files produced by build tools go here, but caulk goes where? 2 | -------------------------------------------------------------------------------- /common/docs/repocard.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/docs/repocard.xcf -------------------------------------------------------------------------------- /gba/tilesets/Donna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/Donna.png -------------------------------------------------------------------------------- /gba/tilesets/bggfx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/bggfx.png -------------------------------------------------------------------------------- /gba/tilesets/hepsie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/hepsie.png -------------------------------------------------------------------------------- /gba/tilesets/kikimap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/kikimap.png -------------------------------------------------------------------------------- /nes/audio/instsamp1.dmc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/audio/instsamp1.dmc -------------------------------------------------------------------------------- /nes/docs/nes_label.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/docs/nes_label.xcf -------------------------------------------------------------------------------- /nes/tilesets/crttest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/crttest.png -------------------------------------------------------------------------------- /nes/tilesets/gus_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/gus_bg.png -------------------------------------------------------------------------------- /nes/tilesets/hepsie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/hepsie.png -------------------------------------------------------------------------------- /nes/tilesets/kikimap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/kikimap.png -------------------------------------------------------------------------------- /gameboy/tilesets/bggfx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/bggfx.png -------------------------------------------------------------------------------- /gba/tilesets/kikitiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/kikitiles.png -------------------------------------------------------------------------------- /gba/tilesets/monoscope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/monoscope.png -------------------------------------------------------------------------------- /gba/tilesets/sharpness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/sharpness.png -------------------------------------------------------------------------------- /gba/tilesets/spritegfx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/spritegfx.png -------------------------------------------------------------------------------- /nes/docs/hoist_sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/docs/hoist_sketch.png -------------------------------------------------------------------------------- /nes/docs/lj65gus32x64.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/docs/lj65gus32x64.xcf -------------------------------------------------------------------------------- /nes/tilesets/kikitiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/kikitiles.png -------------------------------------------------------------------------------- /nes/tilesets/mdf4k_chr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/mdf4k_chr.png -------------------------------------------------------------------------------- /nes/tilesets/overscan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/overscan.png -------------------------------------------------------------------------------- /nes/tilesets/powerpad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/powerpad.png -------------------------------------------------------------------------------- /nes/tilesets/sharpness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/sharpness.png -------------------------------------------------------------------------------- /nes/tilesets/stdtiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/stdtiles.png -------------------------------------------------------------------------------- /common/docs/Gus_frontrear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/docs/Gus_frontrear.png -------------------------------------------------------------------------------- /common/docs/Gus_rear_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/docs/Gus_rear_256.png -------------------------------------------------------------------------------- /common/docs/gplv2_2sided.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/docs/gplv2_2sided.odg -------------------------------------------------------------------------------- /common/docs/pluge_shark.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/docs/pluge_shark.xcf -------------------------------------------------------------------------------- /gameboy/tilesets/cps_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/cps_grid.png -------------------------------------------------------------------------------- /gameboy/tilesets/grayramp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/grayramp.png -------------------------------------------------------------------------------- /gameboy/tilesets/hepsie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/hepsie.png -------------------------------------------------------------------------------- /gameboy/tilesets/kikimap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/kikimap.png -------------------------------------------------------------------------------- /gba/tilesets/Gus_portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/Gus_portrait.png -------------------------------------------------------------------------------- /gba/tilesets/convergence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/convergence.png -------------------------------------------------------------------------------- /gba/tilesets/helpbgtiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/helpbgtiles.png -------------------------------------------------------------------------------- /gba/tilesets/helpsprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/helpsprites.png -------------------------------------------------------------------------------- /gba/wfconfig.toml: -------------------------------------------------------------------------------- 1 | [cartridge] 2 | logo = "official" 3 | title = "160P TEST" 4 | code = "H24A" 5 | maker = "P8" 6 | -------------------------------------------------------------------------------- /nes/docs/240p_nes_manual.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/docs/240p_nes_manual.odg -------------------------------------------------------------------------------- /nes/tilesets/arkanoidchr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/arkanoidchr.png -------------------------------------------------------------------------------- /nes/tilesets/convergence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/convergence.png -------------------------------------------------------------------------------- /nes/tilesets/gus_portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/gus_portrait.png -------------------------------------------------------------------------------- /nes/tilesets/gus_sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/gus_sprite.png -------------------------------------------------------------------------------- /nes/tilesets/megatontiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/megatontiles.png -------------------------------------------------------------------------------- /nes/tilesets/monoscope50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/monoscope50.png -------------------------------------------------------------------------------- /nes/tilesets/monoscope60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/monoscope60.png -------------------------------------------------------------------------------- /nes/tilesets/pads_buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/pads_buttons.png -------------------------------------------------------------------------------- /common/docs/Gillian_and_Gus.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/docs/Gillian_and_Gus.xcf -------------------------------------------------------------------------------- /common/docs/Gus_sketch_2006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/docs/Gus_sketch_2006.png -------------------------------------------------------------------------------- /common/tilesets/vwf7_cp144p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/tilesets/vwf7_cp144p.png -------------------------------------------------------------------------------- /common/tilesets/vwf7_cp240p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/tilesets/vwf7_cp240p.png -------------------------------------------------------------------------------- /gameboy/docs/Gus-bwtocolor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/docs/Gus-bwtocolor.png -------------------------------------------------------------------------------- /gameboy/tilesets/helpblink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/helpblink.png -------------------------------------------------------------------------------- /gameboy/tilesets/helptiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/helptiles.png -------------------------------------------------------------------------------- /gameboy/tilesets/kikitiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/kikitiles.png -------------------------------------------------------------------------------- /gameboy/tilesets/sgbborder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/sgbborder.png -------------------------------------------------------------------------------- /gameboy/tilesets/sharpness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/sharpness.png -------------------------------------------------------------------------------- /gameboy/tilesets/smptetiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/smptetiles.png -------------------------------------------------------------------------------- /gameboy/tilesets/spritegfx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/spritegfx.png -------------------------------------------------------------------------------- /gba/tilesets/greenhillzone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/greenhillzone.png -------------------------------------------------------------------------------- /gba/tilesets/stopwatchface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/stopwatchface.png -------------------------------------------------------------------------------- /gba/tilesets/stopwatchhand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/stopwatchhand.png -------------------------------------------------------------------------------- /nes-palpar/tilesets/palpar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes-palpar/tilesets/palpar.png -------------------------------------------------------------------------------- /nes/tilesets/fizzter_digits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/fizzter_digits.png -------------------------------------------------------------------------------- /nes/tilesets/greenhillzone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/greenhillzone.png -------------------------------------------------------------------------------- /nes/tilesets/lag_clock_face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/lag_clock_face.png -------------------------------------------------------------------------------- /nes/tilesets/safeareas_lite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/safeareas_lite.png -------------------------------------------------------------------------------- /nes/tilesets/serialanalyzer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/serialanalyzer.png -------------------------------------------------------------------------------- /nes/tilesets/shadow_reticle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/shadow_reticle.png -------------------------------------------------------------------------------- /nes/tilesets/stopwatchhand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/stopwatchhand.png -------------------------------------------------------------------------------- /gameboy/docs/sample_gb_manual.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/docs/sample_gb_manual.odg -------------------------------------------------------------------------------- /gameboy/tilesets/Gus_portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/Gus_portrait.png -------------------------------------------------------------------------------- /gameboy/tilesets/backlightzone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/backlightzone.png -------------------------------------------------------------------------------- /gameboy/tilesets/convergence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/convergence.png -------------------------------------------------------------------------------- /gameboy/tilesets/grayramp-sgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/grayramp-sgb.png -------------------------------------------------------------------------------- /gameboy/tilesets/greenhillzone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/greenhillzone.png -------------------------------------------------------------------------------- /gameboy/tilesets/helptiles-gbc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/helptiles-gbc.png -------------------------------------------------------------------------------- /gameboy/tilesets/megatontiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/megatontiles.png -------------------------------------------------------------------------------- /gameboy/tilesets/stopwatchface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/stopwatchface.png -------------------------------------------------------------------------------- /gameboy/tilesets/stopwatchhand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/stopwatchhand.png -------------------------------------------------------------------------------- /gba/tilesets/stopwatchdigits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gba/tilesets/stopwatchdigits.png -------------------------------------------------------------------------------- /nes/docs/with_games_nes_label.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/docs/with_games_nes_label.xcf -------------------------------------------------------------------------------- /nes/tilesets/backlight_sprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/backlight_sprites.png -------------------------------------------------------------------------------- /nes/tilesets/controllerimages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/nes/tilesets/controllerimages.png -------------------------------------------------------------------------------- /gameboy/tilesets/Donna_portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/Donna_portrait.png -------------------------------------------------------------------------------- /gameboy/tilesets/shadow_reticle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/shadow_reticle.png -------------------------------------------------------------------------------- /gameboy/tilesets/stopwatchdigits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/stopwatchdigits.png -------------------------------------------------------------------------------- /common/tilesets/pluge_shark_4color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/tilesets/pluge_shark_4color.png -------------------------------------------------------------------------------- /common/tilesets/pluge_shark_6color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/tilesets/pluge_shark_6color.png -------------------------------------------------------------------------------- /fc-mdf/tilesets/fizztersmboldmono.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/fc-mdf/tilesets/fizztersmboldmono.png -------------------------------------------------------------------------------- /gameboy/obj/gb/index.txt: -------------------------------------------------------------------------------- 1 | Files produced by build tools go here. (This file's existence forces the unzip tool to create this folder.) 2 | -------------------------------------------------------------------------------- /gameboy/tilesets/Gus_portrait-GBC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/Gus_portrait-GBC.png -------------------------------------------------------------------------------- /gameboy/tilesets/linearity-quadrant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/gameboy/tilesets/linearity-quadrant.png -------------------------------------------------------------------------------- /nes/docs/Game_collection.txt: -------------------------------------------------------------------------------- 1 | With Gus's Game Collection 2 | "To help you get a feel for how games will work on your TV once I'm done here." -------------------------------------------------------------------------------- /common/docs/Gus_sketch_by_darryl.revok_ink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/docs/Gus_sketch_by_darryl.revok_ink.png -------------------------------------------------------------------------------- /common/docs/Gus_sketch_by_darryl.revok_paint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinobatch/240p-test-mini/HEAD/common/docs/Gus_sketch_by_darryl.revok_paint.png -------------------------------------------------------------------------------- /gameboy/TODO.txt: -------------------------------------------------------------------------------- 1 | Pull requests are welcome for any of the following: 2 | 3 | * Get Gus blinking 4 | * Super Game Boy enhancement 5 | * Add DTE to NES/GBA suite's help pages 6 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | 0.23 (2021-06-xx) 2 | * List changes outside the full ports 3 | * Put documentation and packaging source in a separate zipfile 4 | * Add equipment test for testing CD players and other DAC-ADC pairs 5 | -------------------------------------------------------------------------------- /gameboy/tilesets/Gus_portrait-GBC.pal.txt: -------------------------------------------------------------------------------- 1 | ffffff b2b2b2 666666 000000 2 | ffffff ffd9b3 aa9177 000000 3 | ffffff 0b7bff 0752aa 000000 4 | ffffff fffe7b c67b0b 000000 5 | ffffff ffd9b3 c67b0b 000000 6 | ffffff ffd9b3 b2b2b2 666666 7 | ffd9b3 c67b0b aa9177 000000 -------------------------------------------------------------------------------- /gameboy/tools/rand.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Random number generator reference implementation 4 | 2020 Damian Yerrick 5 | No rights reserved 6 | """ 7 | s = 1 8 | for i in range(10): 9 | s = ((s + 0xB3) * 0x01010101) & 0xFFFFFFFF 10 | print("%04x" % (s >> 16)) 11 | -------------------------------------------------------------------------------- /fc-mdf/docs/roadmap.md: -------------------------------------------------------------------------------- 1 | Roadmap 2 | ======= 3 | 4 | 1. Build for all five mappers (and NROM as a control) 5 | 2. Bring up mapper detection 6 | 3. Display hello world 7 | 4. Implement VRC6 tone generator 8 | 5. Display what's going on 9 | 6. Consult with MDFourier server on what to do for other chips 10 | -------------------------------------------------------------------------------- /gameboy/tools/pitchtable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Pitch table generator 4 | 2020 Damian Yerrick 5 | No rights reserved 6 | """ 7 | 8 | print('section "pitch_table",ROM0,align[1]') 9 | print("pitch_table::") 10 | period = 131072.0 / 65.625 11 | for i in range(73): 12 | print(" dw %d" % round(2048 - period)) 13 | period = period / 1.059463094 14 | -------------------------------------------------------------------------------- /fc-mdf/README.md: -------------------------------------------------------------------------------- 1 | MDFourier tone generator for Family Computer 2 | ============================================ 3 | 4 | This will eventually be a set of six tone generators for [MDFourier] 5 | to test expansion audio in Konami VRC6, Konami VRC7, Namco 163, 6 | Sunsoft 5B, Nintendo MMC5, and the Famicom Disk System. 7 | 8 | 9 | [MDFourier]: http://junkerhq.net/MDFourier/ 10 | 11 | -------------------------------------------------------------------------------- /nes-palpar/mk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | mkdir -p obj/nes 4 | ../nes/tools/savtool.py --palette 0f2610200f2424240f2424240f242424 tilesets/palpar.png obj/nes/palpar.png.sav 5 | ../nes/tools/savtool.py obj/nes/palpar.png.sav obj/nes/palpar.chr 6 | ../nes/tools/savtool.py obj/nes/palpar.png.sav obj/nes/palpar.nam 7 | ca65 -o obj/nes/main.o src/main.s 8 | ld65 -o palpar.nes -C nrom128.cfg obj/nes/main.o 9 | -------------------------------------------------------------------------------- /gba/tools/write_bios.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "gba_bios.h" 5 | static_assert(sizeof(gba_bios_bin) == 16384); 6 | int main(void) { 7 | FILE *outfp = fopen("gba_bios.bin", "wb"); 8 | if (!outfp) return EXIT_FAILURE; 9 | size_t written = fwrite(gba_bios_bin, sizeof gba_bios_bin, 1, outfp); 10 | fclose(outfp); 11 | return !written; 12 | } 13 | -------------------------------------------------------------------------------- /gameboy/tools/yuvgen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | YUV table generator 4 | 2019 Damian Yerrick 5 | no rights reserved 6 | """ 7 | from math import sin, cos, pi 8 | 9 | for i in range(12): 10 | theta = i * pi / 6.0 11 | y = 127.5 12 | u = cos(theta) * 62.5 13 | v = sin(theta) * 62.5 14 | ## print("yuv is", round(y), round(u), round(v)) 15 | r = round(y + 1.13983*v) 16 | g = round(y - .39465*u - .58060*v) 17 | b = round(y + 2.03211*u) 18 | print(" drgb $%02x%02x%02x" % (r, g, b)) 19 | -------------------------------------------------------------------------------- /fc-mdf/src/global.inc: -------------------------------------------------------------------------------- 1 | .ifndef GLOBAL_INC 2 | GLOBAL_INC = 1 3 | 4 | .globalzp tvSystem, mapper_type, nmis, mdfourier_addrdata 5 | TV_NTSC = 0 6 | TV_PAL_NES = 1 7 | TV_DENDY = 2 8 | MAPPER_UNKNOWN = 0 9 | MAPPER_MMC5 = 1 10 | MAPPER_FME7 = 2 11 | MAPPER_VRC7 = 3 12 | MAPPER_VRC6 = 4 13 | MAPPER_VRC6ED2 = 5 14 | MAPPER_N163 = 6 15 | 16 | .global ppu_cls, ppu_clear_linebuf, ppu_wait_vblank, ppu_screen_on 17 | .global ppu_puts_ay, ppu_puts_0, ppu_putchar, ppu_newline 18 | .global OAM, ppu_clear_oam 19 | 20 | 21 | 22 | 23 | 24 | 25 | .endif 26 | -------------------------------------------------------------------------------- /gba/tools/get_bios.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Downloader for SkyEmu distribution of Cult of GBA BIOS 3 | # Copyright 2024 Damian Yerrick 4 | # SPDX-License-Identifier: FSFAP 5 | # 6 | # Script to download and produce the file gba_bios.bin based on 7 | # Cult of GBA BIOS 8 | # which is under Expat license 9 | set -e 10 | curl --remote-name --referer 'https://github.com/SourMesen/Mesen2/' 'https://raw.githubusercontent.com/skylersaleh/SkyEmu/555bd384f3346b7cd3103d74d5191c3b86312157/src/gba_bios.h' 11 | gcc -std=c11 -s -Os -o write_bios write_bios.c 12 | ./write_bios 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # There is no m68k or UNIX assembly code in this repository. 2 | # (For m68k see ) 3 | # 4 | # Unfortunately, GitHub includes only a hand-picked set of languages 5 | # in statistics. This means I have to classify 6502 and SM83 code 6 | # under the broader "assembly" category. 7 | # 8 | gameboy/src/*.z80 linguist-language=Assembly 9 | gameboy/src/*.inc linguist-language=Assembly 10 | nes/src/*.s linguist-language=Assembly 11 | nes/src/*.inc linguist-language=Assembly 12 | -------------------------------------------------------------------------------- /common/docs/Gus_measured.txt: -------------------------------------------------------------------------------- 1 | Vertical dimensions: ratio close to 1.5 2 | - top of cap to chin: in-game 41, 2006 sketch 61 3 | - chin to elbow: in-game 41, 2006 sketch about 56 4 | - chin to hem of shirt: in-game 57, 2006 sketch 80 5 | - chin to bottom: in-game 77, 2006 sketch 106 6 | 7 | Horizontal dimensions: ratio close to 1.8 8 | - crown of cap: in-game 34, 2006 sketch 59 9 | - head excluding ears: in-game 22, 2006 sketch 47 10 | - neck: in-game 14, 2006 sketch 26 11 | - vest at shoulder: in-game 40, 2006 sketch 54 12 | - vest between arm holes: in-game 34, 2006 sketch 49 13 | - hips: in-game 46, 2006 sketch 73 14 | 15 | Tilt values in 2006 sketch 16 | - head: 6 degrees 17 | - body: 0 degrees 18 | 19 | That's why I'll apply a 10:9 stretch (either 111% horizontally OR 90% 20 | vertically) to the pixel art of Gus on the front cover of the manual. 21 | -------------------------------------------------------------------------------- /nes/tools/unused.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | filenames = ["240pee.nes", "240pee-bnrom.nes"] 4 | for filename in filenames: 5 | with open(filename, "rb") as infp: 6 | infp.read(16) 7 | data = infp.read() 8 | 9 | runbyte, runlength, runthreshold = 0xC9, 0, 32 10 | runs = [] 11 | for addr, value in enumerate(data): 12 | if value != runbyte: 13 | if runlength >= runthreshold: 14 | runs.append((addr - runlength, addr)) 15 | runbyte, runlength = value, 0 16 | runlength += 1 17 | if runlength >= runthreshold: 18 | runs.append((len(data) - runlength, len(data))) 19 | 20 | totalsz = 0 21 | for startaddr, endaddr in runs: 22 | sz = endaddr - startaddr 23 | totalsz += sz 24 | print("%s: %04x-%04x: %d" 25 | % (filename, startaddr, endaddr - 1, sz)) 26 | print("%s: total: %d bytes (%.1f KiB)" 27 | % (filename, totalsz, totalsz / 1024.0)) 28 | -------------------------------------------------------------------------------- /nes/tools/sav2sb53.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import pb53 4 | 5 | def main(argv=None): 6 | argv = argv or sys.argv 7 | 8 | if len(argv) > 1 and argv[1] == '--help': 9 | print("usage: sav2sb53.py infile.sav outfile.pb53\n" 10 | "Packs a savtool background: tile count, PB53 tile data, PB53 nametable,\n" 11 | "16-entry palette") 12 | return 13 | if len(argv) != 3: 14 | print("sav2sb53.py: wrong number of arguments; try sav2sb53.py --help", 15 | file=sys.stderr) 16 | sys.exit(1) 17 | _, infilename, outfilename = argv 18 | with open(infilename, 'rb') as infp: 19 | data = infp.read(8192) 20 | namdata = data[6144:7168] 21 | num_tiles = 1 + max(namdata[:960]) 22 | chrdata = data[0:16 * num_tiles] 23 | paldata = data[-256:-240] 24 | 25 | outdata = [ 26 | bytearray([num_tiles & 0xFF]), 27 | pb53.pb53(chrdata)[0], pb53.pb53(namdata)[0], 28 | paldata 29 | ] 30 | with open(outfilename, 'wb') as outfp: 31 | outfp.writelines(outdata) 32 | 33 | if __name__=='__main__': 34 | main() 35 | -------------------------------------------------------------------------------- /nes/nsf.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # Linker script for NSF 3 | # Copyright 2011 Damian Yerrick 4 | # 5 | # Copying and distribution of this file, with or without 6 | # modification, are permitted in any medium without royalty 7 | # provided the copyright notice and this notice are preserved. 8 | # This file is offered as-is, without any warranty. 9 | # 10 | MEMORY { 11 | ZP: start = $10, size = $f0, type = rw; 12 | # use first $10 zeropage locations as locals 13 | RAM: start = $0300, size = $0500, type = rw; 14 | 15 | HEADER: start = $0000, size = $0080, type = ro, file = %O, fill=yes, fillval=$00; 16 | ROM7: start = $C000, size = $3FF0, type = ro, file = %O, fillval=$FF, define=yes; 17 | FOOTER: start = $0000, size = $4000, type = ro, file = %O, fillval=$00; 18 | } 19 | 20 | SEGMENTS { 21 | NSFHDR: load = HEADER, type = ro, align = $80; 22 | NSFEFOOTER: load = FOOTER, type = ro, optional = yes; 23 | ZEROPAGE: load = ZP, type = zp; 24 | BSS: load = RAM, type = bss, define = yes, align = $100; 25 | CODE: load = ROM7, type = ro, align = 64; 26 | RODATA: load = ROM7, type = ro, align = 64; 27 | DMC: load = ROM7, type = ro, align = 64, optional = yes; 28 | } 29 | 30 | FILES { 31 | %O: format = bin; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /gameboy/src/linkthislast.z80: -------------------------------------------------------------------------------- 1 | ; 2 | ; Variable clearing for 144p Test Suite 3 | ; 4 | ; Copyright 2021 Damian Yerrick 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | 23 | ; Used to determine the total size of SECTION FRAGMENT. 24 | ; See linkthisfirst.z80 for rationale. 25 | 26 | SECTION FRAGMENT "BSS", WRAM0 27 | __BSS_END__:: 28 | 29 | SECTION FRAGMENT "HBSS", HRAM 30 | __HBSS_END__:: 31 | 32 | -------------------------------------------------------------------------------- /nes-palpar/nrom128.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # Linker script for NROM-128 games 3 | # Copyright 2010-2014 Damian Yerrick 4 | # 5 | # Copying and distribution of this file, with or without 6 | # modification, are permitted in any medium without royalty 7 | # provided the copyright notice and this notice are preserved. 8 | # This file is offered as-is, without any warranty. 9 | # 10 | MEMORY { 11 | ZP: start = $10, size = $f0, type = rw; 12 | # use first $10 zeropage locations as locals 13 | HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00; 14 | RAM: start = $0300, size = $0500, type = rw; 15 | ROM7: start = $C000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF; 16 | CHRROM: start = $0000, size = $2000, type = ro, file = %O, fill=yes, fillval=$FF; 17 | } 18 | 19 | SEGMENTS { 20 | INESHDR: load = HEADER, type = ro, align = $10; 21 | ZEROPAGE: load = ZP, type = zp; 22 | BSS: load = RAM, type = bss, define = yes, align = $100; 23 | DMC: load = ROM7, type = ro, align = 64, optional = yes; 24 | CODE: load = ROM7, type = ro, align = $100; 25 | RODATA: load = ROM7, type = ro, align = $100; 26 | VECTORS: load = ROM7, type = ro, start = $FFFA; 27 | CHR: load = CHRROM, type = ro, align = 16, optional = yes; 28 | } 29 | 30 | FILES { 31 | %O: format = bin; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /fc-mdf/src/nes.inc: -------------------------------------------------------------------------------- 1 | ; 2 | ; NES I/O definitions 3 | ; Copyright 2010-2017 Damian Yerrick 4 | ; 5 | ; Copying and distribution of this file, with or without 6 | ; modification, are permitted in any medium without royalty provided 7 | ; the copyright notice and this notice are preserved in any source 8 | ; code copies. This file is offered as-is, without any warranty. 9 | ; 10 | 11 | .ifndef NES_INC 12 | .define NES_INC 1 13 | 14 | PPUCTRL = $2000 15 | NT_2000 = $00 16 | NT_2400 = $01 17 | NT_2800 = $02 18 | NT_2C00 = $03 19 | VRAM_DOWN = $04 20 | OBJ_0000 = $00 21 | OBJ_1000 = $08 22 | OBJ_8X16 = $20 23 | BG_0000 = $00 24 | BG_1000 = $10 25 | VBLANK_NMI = $80 26 | 27 | PPUMASK = $2001 28 | LIGHTGRAY = $01 29 | BG_OFF = $00 30 | BG_CLIP = $08 31 | BG_ON = $0A 32 | OBJ_OFF = $00 33 | OBJ_CLIP = $10 34 | OBJ_ON = $14 35 | TINT_R = $20 36 | TINT_G = $40 37 | TINT_B = $80 38 | 39 | PPUSTATUS = $2002 40 | OAMADDR = $2003 41 | PPUSCROLL = $2005 42 | 43 | PPUADDR = $2006 44 | .define NTXY(xt,yt) ($2000 | (xt) | ((yt) << 5)) 45 | 46 | PPUDATA = $2007 47 | 48 | OAM_DMA = $4014 49 | SNDCHN = $4015 50 | P1 = $4016 51 | P2 = $4017 52 | 53 | KEY_A = %10000000 54 | KEY_B = %01000000 55 | KEY_SELECT = %00100000 56 | KEY_START = %00010000 57 | KEY_UP = %00001000 58 | KEY_DOWN = %00000100 59 | KEY_LEFT = %00000010 60 | KEY_RIGHT = %00000001 61 | 62 | .endif 63 | -------------------------------------------------------------------------------- /nes/src/nes.inc: -------------------------------------------------------------------------------- 1 | ; 2 | ; NES I/O definitions 3 | ; Copyright 2010-2017 Damian Yerrick 4 | ; 5 | ; Copying and distribution of this file, with or without 6 | ; modification, are permitted in any medium without royalty provided 7 | ; the copyright notice and this notice are preserved in any source 8 | ; code copies. This file is offered as-is, without any warranty. 9 | ; 10 | 11 | .ifndef NES_INC 12 | .define NES_INC 1 13 | 14 | PPUCTRL = $2000 15 | NT_2000 = $00 16 | NT_2400 = $01 17 | NT_2800 = $02 18 | NT_2C00 = $03 19 | VRAM_DOWN = $04 20 | OBJ_0000 = $00 21 | OBJ_1000 = $08 22 | OBJ_8X16 = $20 23 | BG_0000 = $00 24 | BG_1000 = $10 25 | VBLANK_NMI = $80 26 | 27 | PPUMASK = $2001 28 | LIGHTGRAY = $01 29 | BG_OFF = $00 30 | BG_CLIP = $08 31 | BG_ON = $0A 32 | OBJ_OFF = $00 33 | OBJ_CLIP = $10 34 | OBJ_ON = $14 35 | TINT_R = $20 36 | TINT_G = $40 37 | TINT_B = $80 38 | 39 | PPUSTATUS = $2002 40 | OAMADDR = $2003 41 | PPUSCROLL = $2005 42 | 43 | PPUADDR = $2006 44 | .define NTXY(xt,yt) ($2000 | (xt) | ((yt) << 5)) 45 | 46 | PPUDATA = $2007 47 | 48 | OAM_DMA = $4014 49 | SNDCHN = $4015 50 | P1 = $4016 51 | P2 = $4017 52 | 53 | KEY_A = %10000000 54 | KEY_B = %01000000 55 | KEY_SELECT = %00100000 56 | KEY_START = %00010000 57 | KEY_UP = %00001000 58 | KEY_DOWN = %00000100 59 | KEY_LEFT = %00000010 60 | KEY_RIGHT = %00000001 61 | 62 | .endif 63 | -------------------------------------------------------------------------------- /nes-palpar/src/main.s: -------------------------------------------------------------------------------- 1 | .segment "ZEROPAGE" 2 | nmis: .res 1 3 | 4 | .segment "INESHDR" 5 | .byte "NES", $1A, 1, 1, 0, 0 6 | 7 | .segment "VECTORS" 8 | .addr nmi_handler, reset_handler, irq_handler 9 | 10 | .segment "RODATA" 11 | nam: .incbin "obj/nes/palpar.nam" 12 | palette: .byte $0F,$26,$10 13 | palette_end: 14 | 15 | .segment "CODE" 16 | nmi_handler: 17 | inc nmis 18 | rti 19 | irq_handler: 20 | bit $4015 21 | rti 22 | reset_handler: 23 | sei 24 | ldx #$FF 25 | txs 26 | inx 27 | stx $2000 28 | stx $2001 29 | bit $4015 30 | bit $2002 31 | @vwait1: 32 | bit $2002 33 | bpl @vwait1 34 | cld 35 | @vwait2: 36 | bit $2002 37 | bpl @vwait2 38 | lda #$3F 39 | ldx #$00 40 | sta $2006 41 | stx $2006 42 | @palloop: 43 | lda palette,x 44 | sta $2007 45 | inx 46 | cpx #palette_end-palette 47 | bcc @palloop 48 | lda #nam 51 | sta $01 52 | ldx #$20 53 | ldy #$00 54 | stx $2006 55 | sty $2006 56 | ldx #4 57 | @ntloop: 58 | lda ($00),y 59 | sta $2007 60 | iny 61 | bne @ntloop 62 | inc $01 63 | dex 64 | bne @ntloop 65 | lda #$80 66 | sta $2000 67 | lda nmis 68 | @vwait3: 69 | cmp nmis 70 | beq @vwait3 71 | lda #%00001010 72 | sta $2001 73 | forever: 74 | jmp forever 75 | 76 | .segment "CHR" 77 | .incbin "obj/nes/palpar.chr" 78 | -------------------------------------------------------------------------------- /.github/actions/cache_cc65/action.yml: -------------------------------------------------------------------------------- 1 | name: Cache CC65 2 | description: Caches CC65 and builds it from source if missing. (C) jroweboy (MIT) 3 | inputs: 4 | cc65Ref: 5 | description: "CC65 Git Ref (used to checkout a fixed version)" 6 | required: false 7 | default: "b8211a2921b11b045db0dd0d9ead6972c9b7bbf1" 8 | cc65Path: 9 | description: "CC65 path to cache" 10 | required: false 11 | default: ${{ github.workspace }}/opt/cc65 12 | runs: 13 | using: "composite" 14 | steps: 15 | # Cache CC65 16 | - uses: actions/cache@master 17 | id: cache_cc65 18 | with: 19 | path: ${{ inputs.cc65Path }} 20 | key: ${{ runner.os }}-cc65-${{ inputs.cc65Ref }}-${{ hashFiles('.github/actions/cache_cc65/*') }} 21 | # Checkout CC65 22 | - uses: actions/checkout@master 23 | if: steps.cache_cc65.outputs.cache-hit != 'true' 24 | with: 25 | repository: cc65/cc65 26 | ref: ${{ inputs.cc65Ref }} 27 | path: './cc65' 28 | # Build CC65 29 | - if: steps.cache_cc65.outputs.cache-hit != 'true' 30 | run: | 31 | cd cc65 32 | PREFIX=${{ inputs.cc65Path }} make 33 | PREFIX=${{ inputs.cc65Path }} make install 34 | shell: bash 35 | # Add CC65 binaries to path 36 | - run: echo "${{ inputs.cc65Path }}/bin" >> $GITHUB_PATH 37 | shell: bash -------------------------------------------------------------------------------- /fc-mdf/tools/mkines.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import argparse 4 | 5 | # map from names to (iNES mapper, use CHR ROM) pairs 6 | mapperdefs = { 7 | "mmc5": (5, True), 8 | "vrc6": (24, True), 9 | "vrc6ed2": (26, True), 10 | "vrc7": (85, False), 11 | "n163": (19, True), 12 | "fme7": (69, True), 13 | } 14 | 15 | 16 | def parse_argv(argv): 17 | p = argparse.ArgumentParser() 18 | p.add_argument("input", help="32768 byte program file") 19 | p.add_argument("mapper", type=lambda x: mapperdefs[x.lower()]) 20 | p.add_argument("output", help="write iNES format ROM") 21 | return p.parse_args(argv[1:]) 22 | 23 | def main(argv=None): 24 | args = parse_argv(argv or sys.argv) 25 | mapper, use_chr_rom = args.mapper 26 | header = bytearray(b"NES\x1A" + bytes(12)) 27 | header[4] = 2 # 2*16384 bytes 28 | header[5] = 4 if use_chr_rom else 0 29 | header[6] = (mapper << 4) & 0xF0 30 | header[7] = mapper & 0xF0 31 | with open(args.input, "rb") as infp: 32 | prg_rom = infp.read(32768) 33 | out = [header, prg_rom] 34 | if use_chr_rom: out.append(prg_rom) 35 | with open(args.output, "wb") as outfp: 36 | outfp.writelines(out) 37 | 38 | if __name__=='__main__': 39 | if "idlelib" in sys.modules: 40 | main(["./mkines.py", "../mdfourier.prg", "mmc5", "../mdfourier-mmc5.nes"]) 41 | else: 42 | main() 43 | -------------------------------------------------------------------------------- /nes/docs/rombanks.md: -------------------------------------------------------------------------------- 1 | There's a bit of "magic" involved in getting BNROM and UNROM to 2 | build from one source code. 3 | 4 | The BNROM version has two 32K banks: 0 and 1. Almost all code and 5 | data not related to compressed CHR and maps is in bank 1. These 6 | segments are in bank 0: 7 | 8 | * `PB53CODE` (BNROM: 0; UNROM: 3) 9 | CHR and map decompression code 10 | * `PB53TABLES` (BNROM: 0; UNROM: 3) 11 | List of PB53 (CHR) and SB53 (CHR + map + palette) addresses 12 | * `BANK00` and `BANK01` (BNROM: 0; UNROM: 0 and 1) 13 | Compressed CHR and map data 14 | * `GATEDATA` (BNROM: 0; UNROM: 1) 15 | Files decompressed not to VRAM but to main memory for a second pass 16 | of decompression, such as the tilemap in the vertical scroll test 17 | and the sprite in the shadow sprite test 18 | * `STUB0` (BNROM: 0; UNROM: does not exist) 19 | If the NES powers on in this bank, immediately switch to bank 1 20 | 21 | The tricky part is `GATEDATA`. Because the decompression code is not 22 | in the same bank as the test screen's code, calls need to go through 23 | `unpb53_gate` to switch banks. So the test program switches to bank 24 | 1 and calls the gate. In BNROM, switching to bank 1 is a no-op, and 25 | the gate switches to bank 0 to perform the decompression. In UNROM, 26 | switching to bank 1 brings `GATEDATA` contents into view, and 27 | the gate is a no-op because decompression is in the fixed bank. 28 | -------------------------------------------------------------------------------- /gba/src/rand.c: -------------------------------------------------------------------------------- 1 | /* 2 | Pseudorandom number generator 3 | Copyright 2018, 2020 Damian Yerrick 4 | 5 | This work is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any 7 | damages arising from the use of this work. 8 | 9 | Permission is granted to anyone to use this work for any purpose, 10 | including commercial applications, and to alter it and redistribute 11 | it freely, subject to the following restrictions: 12 | 13 | 1. The origin of this work must not be misrepresented; you must 14 | not claim that you wrote the original work. If you use 15 | this work in a product, an acknowledgment in the product 16 | documentation would be appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must 18 | not be misrepresented as being the original work. 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | 22 | "Source" is the preferred form of a work for making changes to it. 23 | 24 | */ 25 | #include "global.h" 26 | 27 | // This uses the same linear congruential generator as cc65's 28 | // random function, but with different tempering. 29 | 30 | static unsigned int seed = 1; 31 | 32 | void lcg_srand(unsigned int in_seed) { 33 | seed = in_seed; 34 | } 35 | 36 | int lcg_rand(void) { 37 | seed = (seed + 0xB3) * 0x01010101; 38 | return (seed ^ (seed >> 16)) & 0xFFFF; 39 | } 40 | -------------------------------------------------------------------------------- /nes/mdfourier4k.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # Linker script for MDFourier alone (a 4 KiB ROM) 3 | # Copyright 2010-2021 Damian Yerrick 4 | # 5 | # Copying and distribution of this file, with or without 6 | # modification, are permitted in any medium without royalty 7 | # provided the copyright notice and this notice are preserved. 8 | # This file is offered as-is, without any warranty. 9 | # 10 | MEMORY { 11 | ZP: start = $10, size = $f0, type = rw; 12 | # use first $10 zeropage locations as locals 13 | HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00; 14 | RAM: start = $0300, size = $0500, type = rw; 15 | ROMC0: start = $C000, size = $3000, type = ro, file = %O, fill=yes, fillval=$FF; 16 | ROMF0: start = $F000, size = $1000, type = ro, file = %O, fill=yes, fillval=$FF; 17 | } 18 | 19 | SEGMENTS { 20 | ZEROPAGE: load = ZP, type = zp; 21 | BSS: load = RAM, type = bss, define = yes, align = $100; 22 | 23 | INESHDR: load = HEADER, type = ro, align = $10; 24 | DMC: load = ROMF0, type = ro, align = 64, optional = yes; 25 | LIBCODE: load = ROMF0, type = ro, optional = yes; 26 | CODE: load = ROMF0, type = ro, align = $20; 27 | RODATA: load = ROMF0, type = ro; 28 | 29 | # Fixed space to put a mapper initialization patch if you need one 30 | # Famicom Box header can also go at $FFE0 31 | STUB15: load = ROMF0, type = ro, start = $FFA0; 32 | } 33 | 34 | FILES { 35 | %O: format = bin; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw[a-p] 2 | *.o 3 | *~ 4 | *.chr 5 | *.2bpp 6 | *.chr1 7 | *.pb16 8 | *.pb53 9 | *.sb53 10 | *.iu53 11 | *.pyc 12 | *.cdl 13 | *.nes.ram.nl 14 | *.n??.deb 15 | zip.in 16 | /*.zip.in 17 | __pycache__/ 18 | /240p-test-mini-*.zip 19 | /fc-mdf/obj/fc-mdf/*.s 20 | /fc-mdf/*.n??.deb 21 | /fc-mdf/*.dbg 22 | /fc-mdf/*.fdb 23 | /fc-mdf/*map.txt 24 | /fc-mdf/mdfourier-*.nes 25 | /fc-mdf/mdfourier.prg 26 | /gameboy/obj/gb/*.z80 27 | /gameboy/obj/gb/*.nam 28 | /gameboy/obj/gb/*.iu 29 | /gameboy/obj/gb/*.iuc 30 | /gameboy/obj/gb/*.pal 31 | /gameboy/obj/gb/*.huf 32 | /gameboy/obj/gb/*.border 33 | /gameboy/obj/gb/last-commit* 34 | /gameboy/gb240p.gb 35 | /gameboy/gb240p.map 36 | /gameboy/gb240p.sym 37 | # mgba-qt makes .sav files even for games with no battery 38 | # these are unrelated to NES savtool's image format 39 | /gameboy/gb240p.sav 40 | /gba/build 41 | /gba/240p*.elf 42 | /gba/240p*.gba 43 | /gba/*.sav 44 | /gba/.map 45 | /gba/compile_commands.json 46 | /gba/tools/write_bios 47 | /gba/tools/gba_bios.h 48 | /nes/obj/nes/*.s 49 | /nes/obj/nes/*.sav 50 | /nes/obj/nes/last-commit* 51 | /nes/*.dbg 52 | /nes/*.fdb 53 | /nes/*map.txt 54 | /nes/240pee-*rom.nes 55 | /nes/240pee.nes 56 | /nes/mdfourier.nsf 57 | /nes/mdfourier4k.nes 58 | /nes/mdfourier4k-*.nes 59 | /nes/240p-nes-others.7z 60 | /common/tools/dte 61 | /common/tools/dte.exe 62 | /nes-palpar/palpar.nes 63 | /nes-palpar/obj/nes/*.sav 64 | /nes-palpar/obj/nes/*.nam 65 | 66 | # this is NES and Game Boy homebrew, not DSiWare 67 | *.DS_Store 68 | -------------------------------------------------------------------------------- /.github/actions/cache_rgbds/action.yml: -------------------------------------------------------------------------------- 1 | name: Cache RGBDS 2 | description: Caches RGBDS and builds it from source if missing. Based on Cache CC65 by jroweboy (MIT) 3 | inputs: 4 | rgbdsRef: 5 | description: "RGBDS Git Ref (used to checkout a fixed version)" 6 | required: false 7 | default: "8b85875b6700244669fcba0d21d8b157b8ae7f57" 8 | rgbdsPath: 9 | description: "RGBDS path to cache" 10 | required: false 11 | default: ${{ github.workspace }}/opt/rgbds 12 | runs: 13 | using: "composite" 14 | steps: 15 | # Cache RGBDS 16 | - uses: actions/cache@master 17 | id: cache_rgbds 18 | with: 19 | path: ${{ inputs.rgbdsPath }} 20 | key: ${{ runner.os }}-rgbds-${{ inputs.rgbdsRef }}-${{ hashFiles('.github/actions/cache_rgbds/*') }} 21 | # Checkout RGBDS 22 | - uses: actions/checkout@master 23 | if: steps.cache_rgbds.outputs.cache-hit != 'true' 24 | with: 25 | repository: gbdev/rgbds 26 | ref: ${{ inputs.rgbdsRef }} 27 | path: './rgbds' 28 | # Build RGBDS 29 | - if: steps.cache_rgbds.outputs.cache-hit != 'true' 30 | run: | 31 | cd rgbds 32 | sudo PREFIX=${{ inputs.rgbdsPath }} make 33 | sudo PREFIX=${{ inputs.rgbdsPath }} make install 34 | shell: bash 35 | # Add RGBDS binaries to path 36 | - shell: bash 37 | run: | 38 | export PATH="${{ inputs.rgbdsPath }}/bin:$PATH" 39 | export MANPATH="${{ inputs.rgbdsPath }}/share/man:$MANPATH" 40 | -------------------------------------------------------------------------------- /nes/src/muldiv.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; Multiply and divide routines 3 | ; Copyright 2012-2015 Damian Yerrick 4 | ; 5 | ; Copying and distribution of this file, with or without 6 | ; modification, are permitted in any medium without royalty provided 7 | ; the copyright notice and this notice are preserved in all source 8 | ; code copies. This file is offered as-is, without any warranty. 9 | ; 10 | .exportzp prodlo, prodmlo, prodmhi, prodhi, fac1lo, fac1hi, fac2lo, fac2hi 11 | .export mul16x16, div32x16 12 | 13 | prodlo = $00 14 | prodmlo = $01 15 | prodmhi = $02 16 | prodhi = $03 17 | fac1lo = prodlo 18 | fac1hi = prodmlo 19 | fac2lo = $04 20 | fac2hi = $05 21 | 22 | .proc mul16x16 23 | lda #$00 24 | sta prodmhi 25 | sta prodhi 26 | ldy #16 27 | 28 | ; Prime the carry bit loop 29 | lsr prodmlo 30 | ror prodlo 31 | loop: 32 | bcc noadd 33 | clc 34 | lda fac2lo 35 | adc prodmhi 36 | sta prodmhi 37 | lda fac2hi 38 | adc prodhi 39 | sta prodhi 40 | noadd: 41 | ror prodhi 42 | ror prodmhi 43 | ror prodmlo 44 | ror prodlo 45 | dey 46 | bne loop 47 | rts 48 | .endproc 49 | 50 | 51 | .proc div32x16 52 | ldy #16 53 | ; prod is the dividend 54 | asl prodlo 55 | rol prodmlo 56 | loop: 57 | rol prodmhi 58 | rol prodhi 59 | bcs already_greater 60 | lda prodmhi 61 | cmp fac2lo 62 | lda prodhi 63 | sbc fac2hi 64 | bcc not_greater 65 | already_greater: 66 | lda prodmhi 67 | sbc fac2lo 68 | sta prodmhi 69 | lda prodhi 70 | sbc fac2hi 71 | sta prodhi 72 | sec 73 | not_greater: 74 | rol prodlo 75 | rol prodmlo 76 | dey 77 | bne loop 78 | rts 79 | .endproc 80 | -------------------------------------------------------------------------------- /nes/mdfourier4k-chrrom.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # Linker script for MDFourier alone (a 4 KiB ROM) 3 | # Copyright 2010-2021 Damian Yerrick 4 | # 5 | # Copying and distribution of this file, with or without 6 | # modification, are permitted in any medium without royalty 7 | # provided the copyright notice and this notice are preserved. 8 | # This file is offered as-is, without any warranty. 9 | # 10 | MEMORY { 11 | ZP: start = $10, size = $f0, type = rw; 12 | # use first $10 zeropage locations as locals 13 | HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00; 14 | RAM: start = $0300, size = $0500, type = rw; 15 | ROMC0: start = $C000, size = $3000, type = ro, file = %O, fill=yes, fillval=$FF; 16 | ROMF0: start = $F000, size = $1000, type = ro, file = %O, fill=yes, fillval=$FF; 17 | CHRROM: start = $0000, size = $2000, type = ro, file = %O, fill=yes, fillval=$FF; 18 | } 19 | 20 | SEGMENTS { 21 | ZEROPAGE: load = ZP, type = zp; 22 | BSS: load = RAM, type = bss, define = yes, align = $100; 23 | 24 | INESHDR: load = HEADER, type = ro, align = $10; 25 | DMC: load = ROMF0, type = ro, align = 64, optional = yes; 26 | LIBCODE: load = ROMF0, type = ro, optional = yes; 27 | CODE: load = ROMF0, type = ro, align = $20; 28 | RODATA: load = ROMF0, type = ro; 29 | 30 | # Fixed space to put a mapper initialization patch if you need one 31 | # Famicom Box header can also go at $FFE0 32 | STUB15: load = ROMF0, type = ro, start = $FFA0; 33 | CHR: load = CHRROM, type = ro, align = $20; 34 | } 35 | 36 | FILES { 37 | %O: format = bin; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /common/docs/assets.md: -------------------------------------------------------------------------------- 1 | These assets in upstream 240p Test Suite were replaced to keep the 2 | software free. 3 | 4 | - Gillian 5 | Was a drawing of Gillian Seed resembling the [opening cut scene] 6 | from Konami's _SD Snatcher_. Replaced with Gus, who first appeared 7 | in _LOCKJAW Tetromino Game_ and _LJ65_. 8 | - Motoko 9 | Was Maj. Motoko Kusanagi from Masamune Shirow's 10 | _Ghost in the Shell_. Replaced with another illustration of 11 | Gus by darryl.revok. (In fourth quarter 2021, upstream replaced 12 | it with Donna, and GB followed suit.) 13 | - Hepsie 14 | Was a badnik from Sega's _Sonic the Hedgehog_. Replaced with a 15 | fairy godmother character from a discarded Apple II game pitch. 16 | - Scroll test 17 | Was Green Hill Zone from _Sonic_. Replaced with a lookalike by 18 | Michael Moffitt and Damian Yerrick. 19 | - Vertical scroll test 20 | Tile set was from Taito's _KiKi KaiKai_. Replaced with original 21 | tiles with similar textures. 22 | - PLUGE Shark 23 | From Toaplan's _Fire Shark_. Replaced with a pastiche of 24 | the appearance of the Shark (U+1F988) emoji in Google Noto 25 | and Twitter Twemoji. 26 | 27 | [M. Culbuto] and [nevalyashka dolls] are almost a match for Gus's 28 | body shape. Aerialist Jen Bricker, skateboarder Kanya Sesser, and 29 | photographer Kevin Michael Connolly are even better matches. 30 | 31 | [opening cut scene]: https://lparchive.org/SD-Snatcher/Update%202/ 32 | [M. Culbuto]: https://www.youtube.com/watch?v=SR33c41SYj4 33 | [nevalyashka dolls]: https://travelleronamission.wordpress.com/2015/07/18/travel-theme-nevalyashka-dolls-the-toys-from-the-soviet-times/ 34 | -------------------------------------------------------------------------------- /common/docs/pluge_shark.md: -------------------------------------------------------------------------------- 1 | PLUGE Shark 2 | =========== 3 | 4 | The Pluge Contrast test in 240p Test Suite (for Sega Genesis) version 5 | 1.16 displays a [32x32-pixel shark head mascot][1] from Toaplan's 6 | game _Fire Shark_ as a repeating pattern. When I discovered that 7 | we probably don't have the rights to this icon, I drew my own 8 | replacement based on the pose used by emoji fonts' renderings 9 | of the character 🦈 [SHARK (U+1F988)][2], added in Unicode 11. 10 | I made it a bit more cartoonish, including a large eye like those 11 | of the icon, and colored it based on the closest color in the 12 | [Bisqwit palette on wiki.nesdev.com][3] to each color in the icon. 13 | 14 | * Outline: #000000 (NES $0F) 15 | * Background mark: #003C00 (NES $0A) 16 | * Background space: #077704 (NES $1A) 17 | * Shark body: #131F7F (NES $02) 18 | * Shark underbody: #00727D (NES $1C) 19 | * Shark eye and teeth: #AEAEAE (NES $10) 20 | * Tongue (not used): #BD3C30 (NES $16) 21 | 22 | Though I give NES equivalents for all seven colors, I ended up not 23 | drawing a visible tongue. And on 2bpp platforms (NES and GB), the 24 | background mark and space have to share colors with the shark. 25 | 26 | Copyright 2018 Damian Yerrick 27 | This shark icon and its description are licensed under 28 | [Creative Commons Attribution 4.0 International][4] (CC BY 4.0). 29 | 30 | 31 | [1]: https://github.com/ArtemioUrbina/240pTestSuite/blob/0a16fcb3bdd137767a892eb17c8f9aa565456e17/240psuite/SNES/240pSuite/fireshark.bmp 32 | [2]: https://emojipedia.org/shark/ 33 | [3]: https://wiki.nesdev.com/w/index.php/File:Savtool-swatches.png 34 | [4]: https://creativecommons.org/licenses/by/4.0/ 35 | -------------------------------------------------------------------------------- /nes/docs/Manual_noscreenshot_page5.txt: -------------------------------------------------------------------------------- 1 | This was all on page 5 before I decided to add screenshots to the manual. 2 | 3 | Available tests 4 | 5 | PLUGE (picture line-up generation equipment): Adjust the TV's "brightness" or black level 6 | Color bars: Adjust contrast and tint 7 | Color bleed: Diagnose incorrect color resampling 8 | Grid, Overscan, and Safe areas: show how much of the sides of the picture a TV cuts off 9 | Linearity: Measure whether shapes are consistent across the screen 10 | Convergence: Align red, green, and blue 11 | Gray ramp: Adjust color temperature and gamma 12 | Solid color screen: Diagnose 60 Hz audio buzz and signal loss from black screens 13 | IRE: Show a rectangle with 0% to 100% gray 14 | Sharpness: Show possibly unwanted edge enhancement 15 | Chroma crosstalk: Show a quirk of how the NTSC NES generates color 16 | CPU clock speed: Measure overclock modifications 17 | Shadow sprite: Show pseudo-transparency methods that trip up some upscalers, such as 30 Hz flicker and stripes 18 | Stopwatch: Measure lag difference between TVs, detect dropped frames, or time a workout 19 | Manual lag test: Line up reticles to get a feel for how much a scaler is delaying video and audio 20 | Scroll tests: Measure dropped frames, cadence detection, motion interpolation, and shape consistency 21 | Full screen stripes: Measure unwanted deinterlacing and consistent pixel width in a scaler 22 | Backlight zone test: Find dimming zones in an LED-lit LCD 23 | Sound test: Play several frequencies through each APU output pin 24 | Audio sync test: Play a simple animation to measure delay between audio and video 25 | Zapper test: Show whether the light gun is seeing the TV 26 | -------------------------------------------------------------------------------- /fc-mdf/vrc7.x: -------------------------------------------------------------------------------- 1 | # 2 | # Linker script for MDFourier Expansions 3 | # Copyright 2010, 2021 Damian Yerrick 4 | # 5 | # Copying and distribution of this file, with or without 6 | # modification, are permitted in any medium without royalty 7 | # provided the copyright notice and this notice are preserved. 8 | # This file is offered as-is, without any warranty. 9 | # 10 | MEMORY { 11 | ZP: start = $10, size = $f0, type = rw; 12 | # use first $10 zeropage locations as locals 13 | RAM: start = $0300, size = $0500, type = rw; 14 | 15 | # Skipping the header because it varies 16 | ROM80: start = $8000, size = $2000, type = ro, file = %O, fill=yes, fillval=$FF; 17 | ROMA0: start = $A000, size = $2000, type = ro, file = %O, fill=yes, fillval=$FF; 18 | ROMC0: start = $C000, size = $2000, type = ro, file = %O, fill=yes, fillval=$FF; 19 | ROME0: start = $E000, size = $2000, type = ro, file = %O, fill=yes, fillval=$FF; 20 | } 21 | 22 | SEGMENTS { 23 | ZEROPAGE: load = ZP, type = zp; 24 | BSS: load = RAM, type = bss, define = yes, align = $100; 25 | # To support both CHR ROM and CHR RAM boards, the test contains 26 | # the exact same data in both PRG ROM and CHR ROM. On CHR RAM 27 | # boards, init code copies the first 8K of PRG ROM to CHR RAM. 28 | CHR: load = ROM80, type = ro, align = $10; 29 | RODATA: load = ROMA0, type = ro, align = $10; 30 | CODE: load = ROMC0, type = ro, align = $10; 31 | INITDATA: load = ROME0, type = ro, align = $100, optional=yes; 32 | LOWCODE: load = ROME0, type = ro, align = $10; 33 | VECTORS: load = ROME0, type = ro, start = $FFFA; 34 | } 35 | 36 | FILES { 37 | %O: format = bin; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /common/docs/size_comparison.txt: -------------------------------------------------------------------------------- 1 | io: 536 2 | compression: 683 vs. 147 3 | vwf: 1420 vs. 1217 4 | layout: 968 vs. 440 5 | to be split up 6 | stills.o: 2036 (17.4%) 7 | stills.o: 786 (25.5%) 8 | -rw-r--r-- 1 pino pino 740 Jul 14 14:42 crosstalk.iu53 9 | -rw-r--r-- 1 pino pino 1284 Jul 14 14:42 sharpness.iu53 10 | -rw-r--r-- 1 pino pino 222 Jul 14 09:57 pluge_shark_4color.chr.pb53 11 | menu: 2348 vs. 2678 12 | helppages: 9644 vs. 7940 13 | linearity: 4415 vs. 1150 14 | overclock: 924 15 | overscan: 1512 vs. 510 16 | shadowsprite: 3296 vs. 4568 17 | stopwatch: 4174 vs. 2692 18 | megaton: 1995 vs. 1056 19 | gridscroll: 3838 vs. 2636 20 | backlight: 206 vs. 271 21 | zappertest: 452 22 | soundtest: 339 vs. 372 23 | crowd: 371 24 | audiosync: 289 vs. 296 25 | 26 | 27 | Why are some bigger on NES? 28 | * VWF: Probably code density for 16-bit operations 29 | * Layout: NES attribute tables are painful, among other things 30 | * Decompression: Nametables are packed tighter (for bigger screen), and special handling for 1bpp tiles. 31 | * Some tests don't exist on GB, such as Overclock, Crosstalk, Zapper, and Crowd. 32 | * Linearity: The circles cover nearly twice as much circumference, and there are two of them (NTSC and PAL). 33 | * Sharpness: Again, more diameter. 34 | * Help text: Help pages for tests that don't exist on GB don't exist on GB either 35 | * Overscan: NES lacks anything like GB "window" for drawing two of the edges. Instead a background update must be prepared. 36 | * Stopwatch: Antialiasing on clock face 37 | * Manual lag test: The DDR-style grades 38 | 39 | Some tests are bigger on GB. 40 | * Shadow sprite: Two Gus portraits with antialiasing vs. one (slightly larger) portrait without 41 | * Sound test: Wave RAM. 42 | * Lame boy doesn't exist 43 | -------------------------------------------------------------------------------- /nes/docs/Manual_screenshot_pages.txt: -------------------------------------------------------------------------------- 1 | Page 5 2 | 3 | Available tests 4 | 5 | [PLUGE] [PLUGE Contrast] [IRE] 6 | PLUGE (picture line-up generation equipment) and IRE 7 | Adjust brightness or black level 8 | 9 | [Gradient bars] [SMPTE bars] [Bars on gray] 10 | Color bars 11 | Adjust contrast, saturation, and tint 12 | 13 | [Gray ramp] [Color bleed] [Solid color] 14 | Gray ramp: Adjust color temperature and gamma 15 | Color bleed: Diagnose color resampling problems 16 | Solid color: Diagnose 60 Hz audio buzz and signal loss from 17 | black screen transition 18 | 19 | Page 6 20 | 21 | More tests 22 | 23 | [Grid] [Overscan] [Linearity] 24 | Grid and Overscan: Measure how much of each side a TV cuts off 25 | Linearity: Measure consistency of shapes across the screen 26 | 27 | [Convergence crosses] [Convergence large areas] [Sharpness A] 28 | Convergence: Align red, green, and blue components 29 | Sharpness: Adjust edge enhancement 30 | 31 | Chroma crosstalk: Show an artifact of NTSC NES composite out 32 | CPU clock speed: Adjust an overclock modification 33 | Stopwatch: Measure lag difference between TVs, detect 34 | dropped frames, or time a workout 35 | 36 | Page 7 37 | 38 | Shadow sprite: Test flicker and stripe transparency that may 39 | confuse scalers 40 | Full screen stripes: Measure consistent pixel width in a scaler 41 | Manual lag test: Line up reticles to get a feel for a scaler's delay 42 | 43 | Scroll test: Measure dropped frames, cadence detection, 44 | motion interpolation, and shape consistency 45 | Backlight zones: Find dimming zones in an LED-lit LCD 46 | 47 | Sound test: Play frequencies through each APU output pin 48 | Audio sync: Animation to measure delay between audio and video 49 | Zapper test: Measure whether the light gun sees the TV 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 240p-test-mini 2 | Size-optimized ports of Artemio's 240p Test Suite to 8-bit consoles 3 | 4 | I've remade Artemio Urbina's [240p Test Suite] for three more 5 | platforms: 6 | 7 | - Nintendo Entertainment System 8 | - Game Boy and Game Boy Color (as "144p Test Suite") 9 | - Game Boy Advance (as "160p Test Suite") 10 | 11 | The NES and GB ports are in assembly language for speed and size 12 | efficiency. The GBA port is in C, but still size-optimized to fit 13 | well within the 256 KiB multiboot limit. 14 | 15 | The GB and GBA ports have a different name because their LCD video 16 | timing doesn't match that of NTSC. They exist to test not only the 17 | TV but also the Super Game Boy or Game Boy Player accessory, which 18 | behaves as a scaler. 19 | 20 | Some functionality has been rearranged to fit the controller or 21 | to combine the function of similar tests. Some help pages have 22 | been rewritten for completeness, conciseness, and English usage 23 | improvements. Some tests' graphics have been replaced to keep 24 | the software free. 25 | 26 | To get set up to build the NES port, install GNU Make, Coreutils, 27 | Python 3, Pillow, and cc65 per [nrom-template] instructions. 28 | The port to Game Boy uses [RGBDS] instead of cc65. The GBA port 29 | uses devkitARM and libgba by [devkitPro], but Python 3 and Pillow 30 | are still required to convert the proportional font. 31 | 32 | Like Artemio's original versions, these ports of 240p Test Suite are free 33 | software under the GNU General Public License, version 2 or later. 34 | 35 | [240p Test Suite]: https://github.com/ArtemioUrbina/240pTestSuite 36 | [nrom-template]: https://github.com/pinobatch/nrom-template 37 | [RGBDS]: https://github.com/rednex/rgbds 38 | [devkitPro]: https://devkitpro.org/wiki/Getting_Started -------------------------------------------------------------------------------- /gba/tools/chord_prototype.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import wave 3 | from io import BytesIO 4 | from array import array 5 | import winsound 6 | 7 | ##ideal_freqs = [98.00, 123.47, 146.83, 196.00, 246.94, 392.00] 8 | ##ideal_periods = [185.27, 147.06, 123.66, 92.64, 73.53, 46.31] 9 | periods = [ 10 | [186, 1, 3], 11 | [148, 1, 3], 12 | [124, 3, 1], 13 | [93, 3, 1], 14 | [74, 3, 1], 15 | [47, 1, 3], 16 | ] 17 | delaylines = [array('h', [0]) * p[0] for p in periods] 18 | phases = array('h', [0]) * len(periods) 19 | 20 | for i, (p, delayline) in enumerate(zip(periods, delaylines)): 21 | exci = 10000 if i & 1 else -10000 22 | exci = array('h', [exci]) * (p[0] // 8) 23 | delayline[:len(exci)] = exci 24 | 25 | output = array('h', [0]) * 18157 * 2 26 | for i in range(len(periods)): 27 | period, fac1, fac2 = periods[i] 28 | phase = phases[i] 29 | delayline = delaylines[i] 30 | 31 | for t in range(i * 304, len(output)): 32 | output[t] += delayline[phase] 33 | nextphase = phase + 1 34 | if nextphase >= period: nextphase = 0 35 | newsample = (fac1 * delayline[phase] 36 | + fac2 * delayline[nextphase] 37 | + 2) * 255 // 1024 38 | delayline[phase] = newsample 39 | phase = nextphase 40 | phases[i] = phase 41 | output = bytearray((x + 0x8000) >> 8 for x in output) 42 | 43 | print(sum(len(x) for x in delaylines), "total samples in delay lines") 44 | with BytesIO() as outfp: 45 | wv = wave.open(outfp, "w") 46 | wv.setnchannels(1) 47 | wv.setsampwidth(1) 48 | wv.setframerate(18157) 49 | wv.writeframes(bytes(4000)) 50 | wv.writeframes(output) 51 | wv.close() 52 | winsound.PlaySound(outfp.getvalue(), winsound.SND_MEMORY) 53 | -------------------------------------------------------------------------------- /fc-mdf/makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | title := mdfourier 4 | version := wip 5 | defaultmapper := mmc5 6 | allmappers := mmc5 vrc6 vrc6ed2 vrc7 fme7 n163 7 | objlist := init main bg 8 | 9 | objdir := obj/nes 10 | srcdir := src 11 | imgdir := tilesets 12 | 13 | # Windows needs .exe suffixed to the names of executables; UNIX does 14 | # not. COMSPEC will be set to the name of the shell on Windows and 15 | # not defined on UNIX. Also the Windows Python installer puts 16 | # py.exe in the path, but not python3.exe, which confuses MSYS Make. 17 | ifdef COMSPEC 18 | DOTEXE:=.exe 19 | PY:=py 20 | EMU:=start "" 21 | DEBUGEMU := start "" 22 | else 23 | DOTEXE:= 24 | PY:=python3 25 | EMU:=fceux 26 | DEBUGEMU := ~/.wine/drive_c/Program\ Files\ \(x86\)/FCEUX/fceux.exe 27 | endif 28 | 29 | # Phony targets 30 | 31 | .PHONY: run debug all clean dist 32 | 33 | run: $(title)-$(defaultmapper).nes 34 | $(EMU) $< 35 | 36 | debug: $(title)-$(defaultmapper).nes 37 | $(DEBUGEMU) $< 38 | 39 | all: $(foreach o,$(allmappers),$(title)-$(o).nes) 40 | 41 | clean: 42 | -rm $(objdir)/*.o $(objdir)/*.chr $(title).prg 43 | 44 | dist: \ 45 | $(foreach o,$(allmappers),$(title)-$(o).nes) $(objdir)/index.txt \ 46 | README.md 47 | 48 | # Pictures 49 | 50 | $(objdir)/%16.chr: $(imgdir)/%.png 51 | $(PY) ../common/tools/pilbmp2nes.py $< --planes "0;1" -H16 -o $@ 52 | 53 | # Assembly 54 | 55 | $(objdir)/%.o: $(srcdir)/%.s $(srcdir)/nes.inc $(srcdir)/global.inc 56 | ca65 -o $@ $< 57 | 58 | $(objdir)/%.o: $(objdir)/%.s 59 | ca65 -o $@ $< 60 | 61 | # Includes 62 | 63 | $(objdir)/init.o: $(objdir)/fizztersmboldmono16.chr 64 | 65 | # Linking 66 | 67 | $(title).prg: vrc7.x $(foreach o,$(objlist),$(objdir)/$(o).o) 68 | ld65 -m map.txt -o $@ -C $^ 69 | 70 | $(title)-%.nes: tools/mkines.py $(title).prg 71 | $(PY) $^ $* $@ 72 | 73 | # Packaging 74 | 75 | $(objdir)/index.txt: makefile 76 | echo 'object files go here' > $@ 77 | -------------------------------------------------------------------------------- /gameboy/src/header.z80: -------------------------------------------------------------------------------- 1 | ; 2 | ; ROM header for Game Boy 3 | ; 4 | ; Copyright 2018 Damian Yerrick 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | include "src/gb.inc" 23 | 24 | ; Instead of vectors, 8080 family CPUs (including Z80 in Game Gear 25 | ; SM83 in Game Boy) have RSTs ("reset trampolines"?) spaced 8 bytes 26 | ; apart. There are eight for short encodings of CALL with specific 27 | ; addresses ($C7, $CF, ..., $FF for CALL $0000, CALL $0008, ..., 28 | ; CALL $0038) and five for interrupt service routines (ISR). 29 | ; Fill them with JPs to the actual handlers. 30 | SECTION "rst00", ROM0[$0000] 31 | ret 32 | ; $08, $10 used for RSTs 33 | 34 | ; $18-$38 and $50-$60 not used and omitted to save space 35 | ; Caution: If you set a bit in IE, you MUST have a handler for that 36 | ; interrupt. VisualBoyAdvance calls interrupt handlers on DI HALT. 37 | 38 | SECTION "header", ROM0[$0100] 39 | ; All licensed games begin with NOP JP. It may have had to do with 40 | ; the debugger used in the era. 41 | nop 42 | jp reset_handler 43 | ds 76, $00 44 | -------------------------------------------------------------------------------- /nes/bnrom512kbit.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # Linker script for BNROM (512 kbit) 3 | # Copyright 2010-2015 Damian Yerrick 4 | # 5 | # Copying and distribution of this file, with or without 6 | # modification, are permitted in any medium without royalty 7 | # provided the copyright notice and this notice are preserved. 8 | # This file is offered as-is, without any warranty. 9 | # 10 | MEMORY { 11 | ZP: start = $10, size = $f0, type = rw; 12 | # use first $10 zeropage locations as locals 13 | HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00; 14 | RAM: start = $0300, size = $0500, type = rw; 15 | ROM00: start = $8000, size = $8000, type = ro, file = %O, fill=yes, fillval=$FF, bank=0; 16 | ROM01: start = $8000, size = $8000, type = ro, file = %O, fill=yes, fillval=$FF, bank=1; 17 | } 18 | 19 | SEGMENTS { 20 | ZEROPAGE: load = ZP, type = zp; 21 | BSS: load = RAM, type = bss, define = yes, align = $100; 22 | 23 | INESHDR: load = HEADER, type = ro, align = $10; 24 | 25 | PB53CODE: load = ROM00, type = ro, optional = yes; 26 | PB53TABLES: load = ROM00, type = ro, optional = yes; 27 | BANK00: load = ROM00, type = ro, optional = yes; 28 | BANK01: load = ROM00, type = ro, optional = yes; 29 | GATEDATA: load = ROM00, type = ro, optional = yes; 30 | STUB0: load = ROM00, type = ro, start = $FF90; 31 | 32 | LIBDATA: load = ROM01, type = ro, align = $100, optional = yes; 33 | LIBCODE: load = ROM01, type = ro, align = $80, optional = yes; 34 | RODATA: load = ROM01, type = ro, align = $10; 35 | CODE: load = ROM01, type = ro, align = $40; 36 | CODE02: load = ROM01, type = ro, align = $40, optional = yes; 37 | HELPDATA: load = ROM01, type = ro, optional = yes; 38 | DMC: load = ROM01, type = ro, align = 64, optional = yes; 39 | STUB1: load = ROM01, type = ro, start = $FF90; 40 | } 41 | 42 | FILES { 43 | %O: format = bin; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /gameboy/src/bcd.z80: -------------------------------------------------------------------------------- 1 | ; 2 | ; Binary to decimal (8-bit) 3 | ; Copyright 2018 Damian Yerrick 4 | ; 5 | ; This software is provided 'as-is', without any express or implied 6 | ; warranty. In no event will the authors be held liable for any damages 7 | ; arising from the use of this software. 8 | ; 9 | ; Permission is granted to anyone to use this software for any purpose, 10 | ; including commercial applications, and to alter it and redistribute it 11 | ; freely, subject to the following restrictions: 12 | ; 13 | ; 1. The origin of this software must not be misrepresented; you must not 14 | ; claim that you wrote the original software. If you use this software 15 | ; in a product, an acknowledgment in the product documentation would be 16 | ; appreciated but is not required. 17 | ; 2. Altered source versions must be plainly marked as such, and must not be 18 | ; misrepresented as being the original software. 19 | ; 3. This notice may not be removed or altered from any source distribution. 20 | ; 21 | section "bcd",ROM0 22 | 23 | ;; 24 | ; Converts an 8-bit value to 3 binary-coded decimal digits. 25 | ; @param A the value 26 | ; @return A: tens and ones digits; B[1:0]: hundreds digit; 27 | ; B[7:2]: unspecified 28 | bcd8bit_baa:: 29 | 30 | swap a 31 | ld b,a 32 | and $0F ; bits 3-0 in A, range $00-$0F 33 | or a ; for some odd reason, AND sets half carry to 1 34 | daa ; A=$00-$15 35 | 36 | sla b 37 | adc a 38 | daa 39 | sla b 40 | adc a 41 | daa ; A=$00-$63 42 | rl b 43 | adc a 44 | daa 45 | rl b 46 | adc a 47 | daa 48 | rl b 49 | ret 50 | 51 | ;; 52 | ; Converts an 8-bit value to 2 separate decimal digits. 53 | ; @param A the value 54 | ; @return A: tens digit; C: ones digit; B: tens and ones digits; 55 | ; Z set if A == 0 (that is, if value was less than 10) 56 | bcd8bit_ac:: 57 | call bcd8bit_baa 58 | ld b, a 59 | and $0F ; A: 0000dcbz 60 | ld c, a ; C: 0000dcba 61 | xor b ; A: hgfe0000 62 | swap a ; A: 0000hgfe 63 | ret 64 | -------------------------------------------------------------------------------- /fc-mdf/src/main.s: -------------------------------------------------------------------------------- 1 | .include "nes.inc" 2 | .include "global.inc" 3 | .export main, irq_handler 4 | 5 | .segment "LOWCODE" 6 | 7 | .proc irq_handler 8 | rti 9 | .endproc 10 | 11 | .code 12 | 13 | .proc main 14 | lda #VBLANK_NMI 15 | sta PPUCTRL 16 | lda #$3F 17 | sta PPUADDR 18 | lda #$00 19 | sta PPUADDR 20 | lda mapper_type 21 | asl a 22 | beq :+ 23 | ora #$10 24 | : 25 | sta PPUDATA 26 | adc #$10 27 | sta PPUDATA 28 | lda #$20 29 | sta PPUDATA 30 | 31 | jsr ppu_cls 32 | lda #>hello_str 33 | ldy #hello_str 40 | ldy #someone_testing_str 43 | ldy # 5 | 6 | This work is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any 8 | damages arising from the use of this work. 9 | 10 | Permission is granted to anyone to use this work for any purpose, 11 | including commercial applications, and to alter it and redistribute 12 | it freely, subject to the following restrictions: 13 | 14 | 1. The origin of this work must not be misrepresented; you must 15 | not claim that you wrote the original work. If you use 16 | this work in a product, an acknowledgment in the product 17 | documentation would be appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must 19 | not be misrepresented as being the original work. 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | 23 | "Source" is the preferred form of a work for making changes to it. 24 | 25 | */ 26 | 27 | #ifndef P8_CANVAS_H 28 | #define P8_CANVAS_H 29 | 30 | #include 31 | #include 32 | 33 | typedef struct TileCanvas { 34 | uint8_t left; // in 8 pixel units on nametable 35 | uint8_t top; // in 8 pixel units on nametable 36 | uint8_t width; // in 8 pixel units on nametable 37 | uint8_t height; // in 8 pixel units on nametable 38 | u32 *chrBase; 39 | uint8_t map; // in 2 KiB units on VRAM 40 | uint8_t core; // 0: main; 1: sub 41 | uint16_t mapTileBase; 42 | } TileCanvas; 43 | 44 | extern const TileCanvas screen; 45 | 46 | void canvasClear(const TileCanvas *src, unsigned int color); 47 | void canvasInit(const TileCanvas *src, unsigned int color); 48 | void canvasRectfill(const TileCanvas *v, 49 | int l, int t, int r, int b, 50 | int c); 51 | void canvasHline(const TileCanvas *v, int l, int t, int r, int c); 52 | void canvasVline(const TileCanvas *v, int l, int t, int b, int c); 53 | void canvasRect(const TileCanvas *v, int l, int t, int r, int b, int c); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /nes/src/undte.s: -------------------------------------------------------------------------------- 1 | .include "global.inc" 2 | 3 | ; BPE (Byte Pair Encoding) or DTE (Digram Tree Encoding) 4 | ; Code units 0-127 map to literal ASCII characters. 5 | ; Code units 128-135 map to extra characters (arrows and the like). 6 | ; Code units 136-255 map to pairs of code units. The second 7 | ; is added to a stack, and the first is interpreted as above. 8 | 9 | DTE_MIN_CODEUNIT = 136 10 | FIRST_PRINTABLE_CU = 32 11 | 12 | .import dte_replacements 13 | 14 | .segment "LIBCODE" 15 | 16 | ;; 17 | ; Decompress a line of digram tree encoded text to help_line_buffer. 18 | ; @param AY pointer to start of compressed text, ending with code 19 | ; unit less than FIRST_PRINTABLE_CU 20 | ; @return $00: initial AY value; A: compressed bytes read; 21 | ; Y: decompressed bytes written; CF true 22 | .proc undte_line 23 | srcaddr = $00 24 | sty srcaddr 25 | sta srcaddr+1 26 | .endproc 27 | .proc undte_line0 28 | srcaddr = $00 29 | ysave = $02 30 | 31 | ; Copy the compressed data to the END of help_line_buffer. 32 | ; First calculate compressed data length 33 | ldy #0 34 | strlenloop: 35 | lda (srcaddr),y 36 | iny 37 | cpy #HELP_LINE_LEN 38 | bcs have_strlen 39 | cmp #FIRST_PRINTABLE_CU 40 | bcs strlenloop 41 | have_strlen: 42 | tya 43 | pha ; Save compressed byte count 44 | 45 | ; Now copy backward 46 | ldx #HELP_LINE_LEN 47 | poolypoc: 48 | dey 49 | dex 50 | lda (srcaddr),y 51 | sta help_line_buffer,x 52 | cpy #0 53 | bne poolypoc 54 | 55 | ; at this point, Y = 0, pointing to the decompressed data, 56 | ; and X points to the remaining compressed data 57 | decomploop: 58 | lda help_line_buffer,x 59 | decomp_code: 60 | cmp #DTE_MIN_CODEUNIT 61 | bcs handle_bytepair 62 | sta help_line_buffer,y 63 | iny 64 | inx 65 | cpx #HELP_LINE_LEN 66 | bcc decomploop 67 | 68 | pla 69 | rts 70 | 71 | handle_bytepair: 72 | ; For a bytepair, stack the second byte on the compressed data 73 | ; and reprocess the first byte 74 | sty ysave 75 | asl a 76 | tay 77 | lda dte_replacements-(DTE_MIN_CODEUNIT-128)*2+1,y 78 | sta help_line_buffer,x 79 | dex 80 | lda dte_replacements-(DTE_MIN_CODEUNIT-128)*2,y 81 | ldy ysave 82 | jmp decomp_code 83 | .endproc 84 | -------------------------------------------------------------------------------- /nes/unrom512kbit.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # Linker script for UNROM (512 kbit) 3 | # Copyright 2010-2015 Damian Yerrick 4 | # 5 | # Copying and distribution of this file, with or without 6 | # modification, are permitted in any medium without royalty 7 | # provided the copyright notice and this notice are preserved. 8 | # This file is offered as-is, without any warranty. 9 | # 10 | MEMORY { 11 | ZP: start = $10, size = $f0, type = rw; 12 | # use first $10 zeropage locations as locals 13 | HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00; 14 | RAM: start = $0300, size = $0500, type = rw; 15 | ROM00: start = $8000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=0; 16 | ROM01: start = $8000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=1; 17 | ROM02: start = $8000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=2; 18 | ROM15: start = $C000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=2; 19 | } 20 | 21 | SEGMENTS { 22 | ZEROPAGE: load = ZP, type = zp; 23 | BSS: load = RAM, type = bss, define = yes, align = $100; 24 | 25 | INESHDR: load = HEADER, type = ro, align = $10; 26 | BANK00: load = ROM00, type = ro, align = $100, optional = yes; 27 | BANK01: load = ROM01, type = ro, align = $100, optional = yes; 28 | GATEDATA: load = ROM01, type = ro, optional = yes; 29 | CODE02: load = ROM02, type = ro, align = $40; 30 | HELPDATA: load = ROM02, type = ro, optional = yes; 31 | DMC: load = ROM15, type = ro, align = 64, optional = yes; 32 | LIBDATA: load = ROM15, type = ro, align = $100, optional = yes; 33 | LIBCODE: load = ROM15, type = ro, align = $80, optional = yes; 34 | CODE: load = ROM15, type = ro, align = $40, optional = yes; 35 | RODATA: load = ROM15, type = ro, align = $10; 36 | PB53CODE: load = ROM15, type = ro, optional = yes; 37 | PB53TABLES: load = ROM15, type = ro, optional = yes; 38 | 39 | # Because UNROM/UOROM (mapper 2) has a fixed bank at $C000-$FFFF, 40 | # we need not include the reset stub in all banks. 41 | STUB15: load = ROM15, type = ro, start = $FF90; 42 | } 43 | 44 | FILES { 45 | %O: format = bin; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /gameboy/src/mdfourierfe.z80: -------------------------------------------------------------------------------- 1 | ; 2 | ; Front end for MDFourier tone generator in 144p Test Suite 3 | ; Copyright 2020, 2023 Damian Yerrick 4 | ; 5 | ; This program is free software; you can redistribute it and/or modify 6 | ; it under the terms of the GNU General Public License as published by 7 | ; the Free Software Foundation; either version 2 of the License, or 8 | ; (at your option) any later version. 9 | ; 10 | ; This program is distributed in the hope that it will be useful, 11 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ; GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with this program; if not, write to the Free Software Foundation, Inc., 17 | ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | ; 19 | include "src/gb.inc" 20 | include "src/global.inc" 21 | 22 | section "mdfourierfe",ROM0 23 | 24 | ;; 25 | ; make the background dark on GBC and light on DMG 26 | mdfourier_blank: 27 | ld a, $FF 28 | call set_bgp 29 | xor a 30 | ldh [rBGP], a 31 | fallthrough mdfourier_screen_on 32 | mdfourier_screen_on: 33 | ld a,LCDCF_ON|BG_NT0|BG_CHR21 34 | ldh [vblank_lcdc_value],a 35 | ldh [rLCDC],a 36 | ret 37 | 38 | activity_mdfourier:: 39 | call clear_gbc_attr 40 | call mdfourier_blank 41 | call mdfourier_ready_tone 42 | .restart: 43 | call clear_gbc_attr 44 | ld hl, CHRRAM2 45 | ld c, 16 46 | rst memset_tiny ; clear background tile 47 | call clear_scrn0_to_0 48 | ld de,CHRRAM1>>4 49 | ld hl,mdfourier_title_labels 50 | call vwfDrawLabels 51 | ld a, %01011100 52 | call set_bgp 53 | call mdfourier_screen_on 54 | ld b, 7 55 | call sgb_set_palette_b 56 | 57 | .loop: 58 | ld b,helpsect_mdfourier 59 | call read_pad_help_check 60 | jr nz, .restart 61 | rst wait_vblank_irq 62 | ldh a, [new_keys] 63 | bit PADB_B, a 64 | ret nz 65 | rra 66 | jr nc, .loop 67 | 68 | call mdfourier_blank 69 | call run_mdfourier 70 | jr .restart 71 | 72 | mdfourier_title_labels: 73 | db 24, 32,"MDFourier v1",10 74 | db 24, 40,"tone generator",10 75 | db 24, 64,"A: Start B: Stop",0 76 | -------------------------------------------------------------------------------- /nes/sgrom512kbit.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # Linker script for UNROM (512 kbit) 3 | # Copyright 2010-2015 Damian Yerrick 4 | # 5 | # Copying and distribution of this file, with or without 6 | # modification, are permitted in any medium without royalty 7 | # provided the copyright notice and this notice are preserved. 8 | # This file is offered as-is, without any warranty. 9 | # 10 | MEMORY { 11 | ZP: start = $10, size = $f0, type = rw; 12 | # use first $10 zeropage locations as locals 13 | HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00; 14 | RAM: start = $0300, size = $0500, type = rw; 15 | ROM00: start = $8000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=0; 16 | ROM01: start = $8000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=1; 17 | ROM02: start = $8000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=2; 18 | ROM15: start = $C000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=2; 19 | } 20 | 21 | SEGMENTS { 22 | ZEROPAGE: load = ZP, type = zp; 23 | BSS: load = RAM, type = bss, define = yes, align = $100; 24 | 25 | INESHDR: load = HEADER, type = ro, align = $10; 26 | BANK00: load = ROM00, type = ro, align = $100, optional = yes; 27 | BANK01: load = ROM01, type = ro, align = $100, optional = yes; 28 | GATEDATA: load = ROM01, type = ro, optional = yes; 29 | CODE02: load = ROM02, type = ro, align = $40; 30 | HELPDATA: load = ROM02, type = ro, optional = yes; 31 | DMC: load = ROM15, type = ro, align = 64, optional = yes; 32 | LIBDATA: load = ROM15, type = ro, align = $100, optional = yes; 33 | LIBCODE: load = ROM15, type = ro, align = $80, optional = yes; 34 | CODE: load = ROM15, type = ro, align = $40, optional = yes; 35 | RODATA: load = ROM15, type = ro, align = $10; 36 | PB53CODE: load = ROM15, type = ro, optional = yes; 37 | PB53TABLES: load = ROM15, type = ro, optional = yes; 38 | 39 | STUB00: load = ROM00, type = ro, start = $BFD0; 40 | STUB01: load = ROM01, type = ro, start = $BFD0; 41 | STUB02: load = ROM02, type = ro, start = $BFD0; 42 | STUB15: load = ROM15, type = ro, start = $FFD0; 43 | } 44 | 45 | FILES { 46 | %O: format = bin; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /nes/tools/vwfbuild.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from __future__ import with_statement 3 | from PIL import Image 4 | import array 5 | 6 | def ca65_bytearray(s): 7 | s = [' .byte ' + ','.join("%3d" % ch for ch in s[i:i + 16]) 8 | for i in range(0, len(s), 16)] 9 | return '\n'.join(s) 10 | 11 | def vwfcvt(filename, tileHt=8): 12 | im = Image.open(filename) 13 | pixels = im.load() 14 | (w, h) = im.size 15 | (xparentColor, sepColor) = im.getextrema() 16 | widths = bytearray() 17 | tiledata = bytearray() 18 | for yt in range(0, h, tileHt): 19 | for xt in range(0, w, 8): 20 | # step 1: find the glyph width 21 | tilew = 8 22 | for x in range(8): 23 | if pixels[x + xt, yt] == sepColor: 24 | tilew = x 25 | break 26 | # step 2: encode the pixels 27 | widths.append(tilew) 28 | for y in range(tileHt): 29 | rowdata = 0 30 | for x in range(8): 31 | pxhere = pixels[x + xt, y + yt] 32 | pxhere = 0 if pxhere in (xparentColor, sepColor) else 1 33 | rowdata = (rowdata << 1) | pxhere 34 | tiledata.append(rowdata) 35 | return (widths, tiledata) 36 | 37 | def main(argv=None): 38 | import sys 39 | if argv is None: 40 | argv = sys.argv 41 | if len(argv) > 1 and argv[1] == '--help': 42 | print("usage: %s font.png font.s" % argv[0]) 43 | return 44 | if len(argv) != 3: 45 | print("wrong number of options; try %s --help" % argv[0], file=sys.stderr) 46 | sys.exit(1) 47 | 48 | (widths, tiledata) = vwfcvt(argv[1]) 49 | out = ["; Generated by vwfbuild", 50 | ".export vwfChrWidths, vwfChrData", 51 | '.segment "LIBDATA"', 52 | '.align 256', 53 | 'vwfChrData:', 54 | ca65_bytearray(tiledata), 55 | "vwfChrWidths:", 56 | ca65_bytearray(widths), 57 | ''] 58 | with open(argv[2], 'w') as outfp: 59 | outfp.write('\n'.join(out)) 60 | 61 | if __name__ == '__main__': 62 | ## main(['vwfbuild', '../tilesets/vwf7.png', '../obj/vwf7.s']) 63 | main() 64 | -------------------------------------------------------------------------------- /common/tools/dtefe.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Python frontend for JRoatch's C language DTE compressor 4 | license: zlib 5 | """ 6 | import sys, os, subprocess 7 | 8 | def dte_compress(lines, compctrl=32, mincodeunit=128, 9 | inputlog=None): 10 | """Compress a set of byte strings with DTE. 11 | 12 | lines -- a list of byte strings to compress, where no code unit 13 | is greater than mincodeunit 14 | compctrl -- if an integer, exclude control characters less than this; 15 | if False, exclude control characters less than '\x20'; 16 | otherwise, exclude only '\x00' from compression 17 | inputlog -- log the input data to dte process to this file 18 | 19 | """ 20 | dte_path = os.path.join(os.path.dirname(__file__), "dte") 21 | delimiter = b'\0' 22 | if not isinstance(compctrl, int): 23 | compctrl = 1 if compctrl else 32 24 | if len(lines) > 1: 25 | unusedvalues = set(range(compctrl)) 26 | for line in lines: 27 | unusedvalues.difference_update(line) 28 | delimiter = min(unusedvalues) 29 | delimiter = bytes([delimiter]) 30 | excluderange = "0x00-0x%02x" % (compctrl - 1,) 31 | digramrange = "0x%02x-0xFF" % mincodeunit 32 | compress_cmd_line = [ 33 | dte_path, "-c", "-e", excluderange, "-r", digramrange 34 | ] 35 | 36 | inputdata = delimiter.join(lines) 37 | if inputlog: 38 | with open(inputlog, 'wb') as outfp: 39 | outfp.write(inputdata) 40 | print(" ".join(compress_cmd_line), file=sys.stderr) 41 | spresult = subprocess.run( 42 | compress_cmd_line, check=True, 43 | input=inputdata, stdout=subprocess.PIPE 44 | ) 45 | table_len = (256 - mincodeunit) * 2 46 | repls = [spresult.stdout[i:i + 2] for i in range(0, table_len, 2)] 47 | clines = spresult.stdout[table_len:].split(delimiter) 48 | return clines, repls, None 49 | 50 | def main(argv=None): 51 | argv = argv or sys.argv 52 | with open(argv[1], "rb") as infp: 53 | lines = [x.rstrip(b"\r\n") for x in infp] 54 | clines, repls = dte_compress(lines)[:2] 55 | print(clines) 56 | print(repls) 57 | 58 | if __name__=='__main__': 59 | if 'idlelib' in sys.modules: 60 | main(["dtefe.py", "../../README.md"]) 61 | else: 62 | main() 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /gameboy/src/gb.inc: -------------------------------------------------------------------------------- 1 | ; 2 | ; Hardware include addemda for Game Boy 3 | ; 4 | ; Copyright 2018, 2022 Damian Yerrick 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | 23 | IF !DEF(GB_INC) 24 | def GB_INC equ 1 25 | 26 | ; hardware.inc comes from 27 | ; https://github.com/gbdev/hardware.inc/blob/master/hardware.inc 28 | INCLUDE "src/hardware.inc" 29 | 30 | ; VRAM on the Game Boy consists of three 2K banks of CHR RAM and 31 | ; two 1K nametables. 32 | def CHRRAM0 EQU $8000 ; Sprite tiles 0-127; BG tiles 0-127 if BG_CHR01 on 33 | def CHRRAM1 EQU $8800 ; Sprite and BG tiles 128-255 34 | def CHRRAM2 EQU $9000 ; BG tiles 0-127 if BG_CHR01 off 35 | 36 | ; Common combinations of LCDC flags 37 | def OBJ_ON EQU LCDCF_OBJON 38 | def OBJ_8X16 EQU LCDCF_OBJON|LCDCF_OBJ16 39 | def BG_NT0 EQU LCDCF_BGON|LCDCF_BG9800 40 | def BG_NT1 EQU LCDCF_BGON|LCDCF_BG9C00 41 | def BG_CHR21 EQU LCDCF_BG8800 42 | def BG_CHR01 EQU LCDCF_BG8000 43 | def WINDOW_NT0 EQU LCDCF_WINON|LCDCF_WIN9800 44 | def WINDOW_NT1 EQU LCDCF_WINON|LCDCF_WIN9C00 45 | 46 | ; Palette ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 47 | 48 | ;; 49 | ; Syntax: drgb $FF9966 for color #FF9966 50 | ; Divides each hex tuplet by 8 and rounds down, forming an RGB555 51 | ; color word suitable for SNES/SGB or GBC/GBA/DS. 52 | macro drgb 53 | REPT _NARG 54 | dw (\1 & $F80000) >> 19 | (\1 & $00F800) >> 6 | (\1 & $0000F8) << 7 55 | shift 1 56 | ENDR 57 | endm 58 | 59 | ENDC 60 | -------------------------------------------------------------------------------- /gba/src/ppuclear.c: -------------------------------------------------------------------------------- 1 | /* 2 | Basic video functions for Game Boy Advance 3 | Copyright 2018 Damian Yerrick 4 | 5 | This work is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any 7 | damages arising from the use of this work. 8 | 9 | Permission is granted to anyone to use this work for any purpose, 10 | including commercial applications, and to alter it and redistribute 11 | it freely, subject to the following restrictions: 12 | 13 | 1. The origin of this work must not be misrepresented; you must 14 | not claim that you wrote the original work. If you use 15 | this work in a product, an acknowledgment in the product 16 | documentation would be appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must 18 | not be misrepresented as being the original work. 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | 22 | "Source" is the preferred form of a work for making changes to it. 23 | 24 | */ 25 | #include "global.h" 26 | #include 27 | 28 | 29 | EWRAM_BSS OBJ_ATTR SOAM[128]; 30 | unsigned char oam_used; 31 | 32 | /** 33 | * Clears all shadow OAM entries from start through 127. 34 | */ 35 | void ppu_clear_oam(size_t start) { 36 | for (; start < 128; ++start) { 37 | SOAM[start].attr0 = ATTR0_HIDE; 38 | } 39 | } 40 | 41 | void ppu_copy_oam() { 42 | tonccpy(oam_mem, SOAM, sizeof(SOAM)); 43 | } 44 | 45 | void dma_memset16(void *dst, unsigned int c16, size_t length_bytes) { 46 | dma_fill(dst, c16, length_bytes >> 1, 3, DMA_16|DMA_ENABLE); 47 | } 48 | 49 | void bitunpack2(void *restrict dst, const void *restrict src, size_t len) { 50 | // Load tiles 51 | BUP bgtilespec = { 52 | .src_len=len, .src_bpp=2, .dst_bpp=4, 53 | .dst_ofs=0 54 | }; 55 | BitUnPack(src, dst, &bgtilespec); 56 | } 57 | 58 | void bitunpack1(void *restrict dst, const void *restrict src, size_t len) { 59 | // Load tiles 60 | BUP bgtilespec = { 61 | .src_len=len, .src_bpp=1, .dst_bpp=4, 62 | .dst_ofs=0 63 | }; 64 | BitUnPack(src, dst, &bgtilespec); 65 | } 66 | 67 | void load_flat_map(unsigned short *dst, const unsigned short *src, unsigned int w, unsigned int h) { 68 | for (; h > 0; --h) { 69 | tonccpy(dst, src, w * 2); 70 | src += w; 71 | dst += 32; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /nes/src/mdfouriernsf.s: -------------------------------------------------------------------------------- 1 | .exportzp test_state, tvSystem, new_keys 2 | .export mdfourier_present, mdfourier_good_phase 3 | .import __ROM7_START__ 4 | .import mdfourier_init_apu, mdfourier_run, mdfourier_push_apu 5 | SIZEOF_TEST_STATE = 24 6 | 7 | .segment "NSFHDR" 8 | .byt "NESM", $1A ; signature 9 | .byt $01 ; version: NSF classic (no NSFe metadata) 10 | .byt 1 ; song count 11 | .byt 1 ; first song to play 12 | .addr __ROM7_START__ ; load address (should match link script) 13 | .addr mdfourier_nsf_init 14 | .addr mdfourier_nsf_play 15 | names_start: 16 | .byte "MDFourier v7" 17 | .res names_start+32-*, $00 18 | .byte "Damian Yerrick (@PinoBatch)" 19 | .res names_start+64-*, $00 20 | .byte "2020 Damian Yerrick" 21 | .res names_start+96-*, $00 22 | .word 16639 ; NTSC frame length 23 | .byt $00,$00,$00,$00,$00,$00,$00,$00 ; bankswitching disabled 24 | .word 19997 ; PAL frame length 25 | .byt $02 ; NTSC/PAL dual compatible; NTSC preferred 26 | .byt $00 ; Famicom mapper sound not used 27 | .dword 0 ; no NSFe metadata 28 | 29 | mdfourier_good_phase = mdfourier_present 30 | 31 | .zeropage 32 | 33 | song_id: .res 1 34 | new_keys = song_id 35 | tvSystem: .res 1 36 | sp_on_play_call: .res 1 37 | saved_stack_depth: .res 1 38 | saved_stack: .res 16 39 | test_state: .res SIZEOF_TEST_STATE 40 | 41 | .code 42 | .proc mdfourier_nsf_init 43 | sta song_id 44 | stx tvSystem 45 | tsx 46 | stx sp_on_play_call 47 | jsr mdfourier_init_apu 48 | jsr mdfourier_present 49 | jmp mdfourier_run 50 | .endproc 51 | 52 | ;; 53 | ; Saves coroutine state and returns to the NSF player 54 | .proc mdfourier_present 55 | tsx 56 | txa 57 | eor #$FF 58 | sec 59 | adc sp_on_play_call 60 | sta saved_stack_depth 61 | beq stack_is_empty 62 | ldy #0 63 | stack_loop: 64 | pla 65 | sta saved_stack,y 66 | iny 67 | cpy saved_stack_depth 68 | bcc stack_loop 69 | stack_is_empty: 70 | rts 71 | .endproc 72 | 73 | .proc mdfourier_nsf_play 74 | jsr mdfourier_push_apu 75 | 76 | ; Figure out how much stack we'll have to save next time 77 | tsx 78 | stx sp_on_play_call 79 | 80 | ; Restore what we saved last time 81 | ldy saved_stack_depth 82 | beq stack_is_empty 83 | unstack_loop: 84 | lda saved_stack-1,y 85 | pha 86 | dey 87 | bne unstack_loop 88 | stack_is_empty: 89 | rts 90 | .endproc 91 | -------------------------------------------------------------------------------- /gba/src/pads.c: -------------------------------------------------------------------------------- 1 | /* 2 | Controller reading for Game Boy Advance 3 | Copyright 2018 Damian Yerrick 4 | 5 | This work is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any 7 | damages arising from the use of this work. 8 | 9 | Permission is granted to anyone to use this work for any purpose, 10 | including commercial applications, and to alter it and redistribute 11 | it freely, subject to the following restrictions: 12 | 13 | 1. The origin of this work must not be misrepresented; you must 14 | not claim that you wrote the original work. If you use 15 | this work in a product, an acknowledgment in the product 16 | documentation would be appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must 18 | not be misrepresented as being the original work. 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | 22 | "Source" is the preferred form of a work for making changes to it. 23 | 24 | */ 25 | #include 26 | #include "global.h" 27 | 28 | #define DAS_DELAY 12 29 | #define DAS_SPEED 3 30 | 31 | // scanKeys in libgba isn't quite flexible enough for what I'm doing. 32 | // I want to make only some keys autorepeat and not others. 33 | // So I just translated my NES and GB autorepeat stuff line by line. 34 | unsigned short cur_keys, new_keys, das_keys, das_timer; 35 | 36 | unsigned int read_pad(void) { 37 | unsigned int keys = (~REG_KEYINPUT) & 0x03ff; 38 | new_keys = keys & ~cur_keys; 39 | cur_keys = keys; 40 | return keys; 41 | } 42 | 43 | unsigned int autorepeat(unsigned int allowed_keys) { 44 | // If no eligible keys are held, skip all autorepeat processing 45 | unsigned int allowed_held = allowed_keys & cur_keys; 46 | if (!allowed_held) return new_keys; 47 | 48 | // If any keys were newly pressed, set the eligible keys among them 49 | // as the autorepeating set. For example, changing from Up to 50 | // Up+Right sets Right as the new autorepeating set. 51 | if (new_keys) { 52 | das_keys = new_keys & allowed_keys; 53 | das_timer = DAS_DELAY; 54 | } else { 55 | 56 | // If time has expired, merge in the autorepeating set 57 | --das_timer; 58 | if (das_timer == 0) { 59 | new_keys |= das_keys; 60 | das_timer = DAS_SPEED; 61 | } 62 | } 63 | return new_keys; 64 | } 65 | -------------------------------------------------------------------------------- /nes/src/mmc3.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; MMC3 driver for NES 3 | ; Copyright 2011-2023 Damian Yerrick 4 | ; 5 | ; Copying and distribution of this file, with or without 6 | ; modification, are permitted in any medium without royalty provided 7 | ; the copyright notice and this notice are preserved in all source 8 | ; code copies. This file is offered as-is, without any warranty. 9 | ; 10 | 11 | .include "nes2header.inc" 12 | nes2prg 65536 13 | nes2chr 0 14 | nes2chrram 8192 15 | nes2mirror 'V' 16 | nes2mapper 4 17 | nes2tv 'N','P' 18 | nes2end 19 | 20 | .import reset_handler 21 | .importzp nmis 22 | .import unpb53_some, unpb53_file_cb, load_sb53_file_cb, load_iu53_file_cb 23 | .export unpb53_gate, unpb53_file, load_sb53_file, load_iu53_file 24 | .import rf_vwfClearPuts_cb, rf_load_layout_cb 25 | .export rf_vwfClearPuts, rf_load_layout 26 | .export rtl, mmc_bank_a 27 | 28 | ; Fixed code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 29 | 30 | MAIN_CODE_BANK = $04 31 | GATE_DATA_BANK = $02 32 | RESETSTUB_BASE = $FF70 33 | 34 | .segment "STUB15" 35 | resetstub_start: 36 | .assert resetstub_start = ::RESETSTUB_BASE, error, "RESETSTUB_BASE doesn't match linker configuration" 37 | 38 | ; Call gates 39 | unpb53_gate: 40 | lda #GATE_DATA_BANK 41 | jsr mmc_bank_a 42 | jsr unpb53_some 43 | rtl: 44 | lda #MAIN_CODE_BANK 45 | mmc_bank_a: 46 | ldx #6 47 | stx $8000 48 | sta $8001 49 | inx 50 | stx $8000 51 | ora #$01 52 | sta $8001 53 | rts 54 | 55 | load_sb53_file: 56 | jsr load_sb53_file_cb 57 | jmp rtl 58 | 59 | load_iu53_file: 60 | jsr load_iu53_file_cb 61 | jmp rtl 62 | 63 | unpb53_file: 64 | jsr unpb53_file_cb 65 | jmp rtl 66 | 67 | rf_load_layout: 68 | pha 69 | lda #GATE_DATA_BANK 70 | jsr mmc_bank_a 71 | pla 72 | jsr rf_load_layout_cb 73 | jmp rtl 74 | 75 | nmi_handler: 76 | inc nmis 77 | irq_handler: 78 | rti 79 | resetstub_entry: 80 | sei 81 | ldx #7 82 | loop: 83 | lda mmc3_initial_banks,x 84 | stx $8000 85 | sta $8001 86 | dex 87 | bpl loop 88 | sta $A000 ; set vertical mirroring 89 | txs 90 | jmp reset_handler 91 | 92 | mmc3_initial_banks: .byte 0, 2, 4, 5, 6, 7, 0, 1 93 | .assert * <= $FFE0, warn, "reset stub extends into Famicom Box header area" 94 | .res ::resetstub_start - ::RESETSTUB_BASE + $FFFA - * 95 | .addr nmi_handler, resetstub_entry, irq_handler 96 | 97 | rf_vwfClearPuts = rf_vwfClearPuts_cb 98 | -------------------------------------------------------------------------------- /gameboy/src/rand.z80: -------------------------------------------------------------------------------- 1 | ; 2 | ; Pseudorandom number generator 3 | ; 4 | ; Copyright 2018, 2020 Damian Yerrick 5 | ; 6 | ; This software is provided 'as-is', without any express or implied 7 | ; warranty. In no event will the authors be held liable for any damages 8 | ; arising from the use of this software. 9 | ; 10 | ; Permission is granted to anyone to use this software for any purpose, 11 | ; including commercial applications, and to alter it and redistribute it 12 | ; freely, subject to the following restrictions: 13 | ; 14 | ; 1. The origin of this software must not be misrepresented; you must not 15 | ; claim that you wrote the original software. If you use this software 16 | ; in a product, an acknowledgment in the product documentation would be 17 | ; appreciated but is not required. 18 | ; 2. Altered source versions must be plainly marked as such, and must not be 19 | ; misrepresented as being the original software. 20 | ; 3. This notice may not be removed or altered from any source distribution. 21 | ; 22 | section "rand_ram",WRAM0 23 | randstate: ds 4 24 | 25 | ; The formula is 26 | ; x[i + 1] = (x[i] + 0xB3) * 0x01010101 27 | ; or equivalently 28 | ; x[i + 1] = x[i] * 0x01010101 + 0xB3B3B3B3 29 | ; Prior to cc65 commit 3994fee595 it was 30 | ; x[i + 1] = x[i] * 0x01010101 + 0x31415927 31 | 32 | section "rand",ROM0 33 | 34 | ;; 35 | ; Generates a pseudorandom 16-bit integer in BC 36 | ; using the LCG formula from cc65 rand(): 37 | ; x[i + 1] = x[i] * 0x01010101 + 0xB3B3B3B3 38 | ; @return A=B=state bits 31-24 (which have the best entropy), 39 | ; C=state bits 23-16, HL trashed 40 | rand:: 41 | ; Add 0xB3 then multiply by 0x01010101 42 | ld hl, randstate+0 43 | ld a, [hl] 44 | add a, $B3 45 | ld [hl+], a 46 | adc a, [hl] 47 | ld [hl+], a 48 | adc a, [hl] 49 | ld [hl+], a 50 | ld c, a 51 | adc a, [hl] 52 | ld [hl], a 53 | ld b, a 54 | ret 55 | 56 | ;; 57 | ; Sets the random seed to BC. 58 | ; C expects startup code to behave as if srand(1) was called. 59 | ; AHL trashed 60 | srand:: 61 | ld hl,randstate+3 62 | xor a 63 | ld [hl-],a 64 | ld [hl-],a 65 | ld a,b 66 | ld [hl-],a 67 | ld [hl],c 68 | ret 69 | 70 | ; 71 | ; According to tools/rand.py, after srand(1) then ten rand() calls, 72 | ; first ten BC results should be 73 | ; b4b4 85d1 8e08 9b0d 2d92 74 | ; 794b 64eb 8a25 35ab 6731 75 | ; Verify this with SHOW_RNG in placeholder.z80 76 | -------------------------------------------------------------------------------- /nes/tools/nestobin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | iNES to raw binary tool for python 2.7 and 3.x 4 | 5 | Copyright 2015 Damian Yerrick 6 | Copying and distribution of this file, with or without 7 | modification, are permitted in any medium without royalty 8 | provided the copyright notice and this notice are preserved. 9 | This file is offered as-is, without any warranty. 10 | """ 11 | from __future__ import with_statement, division, print_function 12 | import sys, os 13 | 14 | eprom_names = { 15 | 8192: '27C64', 16 | 16384: '27C128', 17 | 32768: '27C256', 18 | 65536: '27C512', 19 | 131072: '27C010', 20 | 262144: '27C020', 21 | 524288: '27C040' 22 | } 23 | 24 | default_filename = "../240pee.nes" 25 | usage_msg = """usage: %s [FILE1.nes [FILE2.nes ...]] 26 | Converts an NES program in iNES format to raw PRG and CHR binary files, 27 | doubling the data to fill the entire PRG ROM. 28 | If no ROM filename given, converts %s""" 29 | 30 | def doubleup(data, nameprefix, ext): 31 | if not data: 32 | return 33 | maxsize = max(eprom_names) 34 | while len(data) <= maxsize: 35 | try: 36 | eprom_name = eprom_names[len(data)] 37 | except KeyError: 38 | print("no EPROM name for %d bytes" % len(prgrom), file=sys.stderr) 39 | filename = '%s-%s.%s' % (nameprefix, eprom_name, ext) 40 | with open(filename, 'wb') as outfp: 41 | outfp.write(data) 42 | data = data + data 43 | 44 | def main(argv=None): 45 | argv = argv or sys.argv 46 | if len(argv) < 2: 47 | filenames = [default_filename] 48 | elif argv[1] in ['-h', '-?', '--help', '/?']: 49 | progname = os.path.basename(argv[0]) 50 | print(usage_msg % (progname, default_filename)) 51 | return 52 | else: 53 | filenames = argv[1:] 54 | 55 | for infilename in filenames: 56 | with open(infilename, "rb") as infp: 57 | romdata = infp.read() 58 | header = bytearray(romdata[:16]) 59 | prgsize = header[4] * 16384 60 | chrsize = header[5] * 8192 61 | 62 | pfx, oldext = os.path.splitext(infilename) 63 | prgrom = romdata[16:16 + prgsize] 64 | doubleup(prgrom, pfx, "prg") 65 | chrrom = romdata[16 + prgsize:16 + prgsize + chrsize] 66 | doubleup(chrrom, pfx, "chr") 67 | 68 | if __name__=='__main__': 69 | main() 70 | -------------------------------------------------------------------------------- /nes/src/unrom.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; UNROM driver for NES 3 | ; Copyright 2011-2016 Damian Yerrick 4 | ; 5 | ; Copying and distribution of this file, with or without 6 | ; modification, are permitted in any medium without royalty provided 7 | ; the copyright notice and this notice are preserved in all source 8 | ; code copies. This file is offered as-is, without any warranty. 9 | ; 10 | 11 | .include "nes2header.inc" 12 | nes2prg 65536 13 | nes2chr 0 14 | nes2chrram 8192 15 | nes2mirror 'V' 16 | nes2mapper 2 17 | nes2tv 'N','P' 18 | nes2end 19 | 20 | .import reset_handler 21 | .importzp nmis 22 | .import unpb53_some, unpb53_file_cb, load_sb53_file_cb, load_iu53_file_cb 23 | .export unpb53_gate, unpb53_file, load_sb53_file, load_iu53_file 24 | .import rf_vwfClearPuts_cb, rf_load_layout_cb 25 | .export rf_vwfClearPuts, rf_load_layout 26 | .export rtl 27 | 28 | ; Fixed code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 29 | 30 | 31 | MAIN_CODE_BANK = $02 32 | GATE_DATA_BANK = $01 33 | RESETSTUB_BASE = $FF90 34 | 35 | .macro resetstub_in segname, scopename 36 | .segment segname 37 | .proc scopename 38 | .assert * = ::RESETSTUB_BASE, error, "RESETSTUB_BASE doesn't match linker configuration" 39 | 40 | ; Call gates 41 | unpb53_gate: 42 | lda #GATE_DATA_BANK 43 | sta unpb53_gate+1 44 | jsr unpb53_some 45 | ; Switch back to the main code bank 46 | rtl: 47 | ldx #MAIN_CODE_BANK 48 | stx rtl+1 49 | rts 50 | 51 | load_sb53_file: 52 | jsr load_sb53_file_cb 53 | jmp rtl 54 | 55 | load_iu53_file: 56 | jsr load_iu53_file_cb 57 | jmp rtl 58 | 59 | unpb53_file: 60 | jsr unpb53_file_cb 61 | jmp rtl 62 | 63 | rf_load_layout: 64 | ldx #GATE_DATA_BANK 65 | stx rf_load_layout+1 66 | jsr rf_load_layout_cb 67 | jmp rtl 68 | 69 | nmi_handler: 70 | inc nmis 71 | irq_handler: 72 | rti 73 | resetstub_entry: 74 | sei 75 | ldx #$FF 76 | txs 77 | stx resetstub_entry+2 78 | jmp reset_handler 79 | .assert * <= $FFE0, warn, "reset stub extends into Famicom Box header area" 80 | .res ::scopename - ::RESETSTUB_BASE + $FFFA - * 81 | .addr nmi_handler, resetstub_entry, irq_handler 82 | .endproc 83 | .endmacro 84 | 85 | resetstub_in "STUB15", stub1 86 | unpb53_gate = stub1::unpb53_gate 87 | unpb53_file = stub1::unpb53_file 88 | load_sb53_file = stub1::load_sb53_file 89 | load_iu53_file = stub1::load_iu53_file 90 | rf_vwfClearPuts = rf_vwfClearPuts_cb 91 | rf_load_layout = stub1::rf_load_layout 92 | rtl := stub1::rtl 93 | -------------------------------------------------------------------------------- /nes/src/paldetect.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; NES TV system detection code 3 | ; Copyright 2011 Damian Yerrick 4 | ; 5 | ; Copying and distribution of this file, with or without 6 | ; modification, are permitted in any medium without royalty provided 7 | ; the copyright notice and this notice are preserved in all source 8 | ; code copies. This file is offered as-is, without any warranty. 9 | ; 10 | .export getTVSystem 11 | .importzp nmis 12 | 13 | .segment "CODE" 14 | .align 32 ; ensure that branches do not cross a page boundary 15 | 16 | ;; 17 | ; Detects which of NTSC, PAL, or Dendy is in use by counting cycles 18 | ; between NMIs. 19 | ; 20 | ; NTSC NES produces 262 scanlines, with 341/3 CPU cycles per line. 21 | ; PAL NES produces 312 scanlines, with 341/3.2 CPU cycles per line. 22 | ; Its vblank is longer than NTSC, and its CPU is slower. 23 | ; Dendy is a Russian famiclone distributed by Steepler that uses the 24 | ; PAL signal with a CPU as fast as the NTSC CPU. Its vblank is as 25 | ; long as PAL's, but its NMI occurs toward the end of vblank (line 26 | ; 291 instead of 241) so that cycle offsets from NMI remain the same 27 | ; as NTSC, keeping Balloon Fight and any game using a CPU cycle- 28 | ; counting mapper (e.g. FDS, Konami VRC) working. 29 | ; 30 | ; nmis is a variable that the NMI handler modifies every frame. 31 | ; Make sure your NMI handler finishes within 1500 or so cycles (not 32 | ; taking the whole NMI or waiting for sprite 0) while calling this, 33 | ; or the result in A will be wrong. 34 | ; 35 | ; (This is the old version, from before the rewrite in Pently 5) 36 | ; 37 | ; @return A: TV system (0: NTSC, 1: PAL, 2: Dendy; 3: unknown 38 | ; Y: high byte of iterations used (1 iteration = 11 cycles) 39 | ; X: low byte of iterations used 40 | .proc getTVSystem 41 | ldx #0 42 | ldy #0 43 | lda nmis 44 | nmiwait1: 45 | cmp nmis 46 | beq nmiwait1 47 | lda nmis 48 | 49 | nmiwait2: 50 | ; Each iteration takes 11 cycles. 51 | ; NTSC NES: 29780 cycles or 2707 = $A93 iterations 52 | ; PAL NES: 33247 cycles or 3022 = $BCE iterations 53 | ; Dendy: 35464 cycles or 3224 = $C98 iterations 54 | ; so we can divide by $100 (rounding down), subtract ten, 55 | ; and end up with 0=ntsc, 1=pal, 2=dendy, 3=unknown 56 | inx 57 | bne :+ 58 | iny 59 | : 60 | cmp nmis 61 | beq nmiwait2 62 | tya 63 | sec 64 | sbc #10 65 | cmp #3 66 | bcc notAbove3 67 | lda #3 68 | notAbove3: 69 | rts 70 | .endproc 71 | 72 | -------------------------------------------------------------------------------- /nes/tgrom512kbit.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # Linker script for UNROM (512 kbit) 3 | # Copyright 2010-2015 Damian Yerrick 4 | # 5 | # Copying and distribution of this file, with or without 6 | # modification, are permitted in any medium without royalty 7 | # provided the copyright notice and this notice are preserved. 8 | # This file is offered as-is, without any warranty. 9 | # 10 | MEMORY { 11 | ZP: start = $10, size = $f0, type = rw; 12 | # use first $10 zeropage locations as locals 13 | HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00; 14 | RAM: start = $0300, size = $0500, type = rw; 15 | ROM00: start = $8000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=0; 16 | ROM01: start = $8000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=2; 17 | ROM02: start = $8000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=4; 18 | ROM15: start = $C000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF, bank=4; 19 | } 20 | 21 | SEGMENTS { 22 | ZEROPAGE: load = ZP, type = zp; 23 | BSS: load = RAM, type = bss, define = yes, align = $100; 24 | 25 | INESHDR: load = HEADER, type = ro, align = $10; 26 | BANK00: load = ROM00, type = ro, align = $100, optional = yes; 27 | BANK01: load = ROM01, type = ro, align = $100, optional = yes; 28 | GATEDATA: load = ROM01, type = ro, optional = yes; 29 | CODE02: load = ROM02, type = ro, align = $40; 30 | HELPDATA: load = ROM02, type = ro, optional = yes; 31 | 32 | # To make FamicomBox system software calculate a correct checksum 33 | # even if the MMC3 powers up with $8000 and $C000 swapped, the 34 | # first $00 byte in $C000-$DFFF should occur at an even address. 35 | # Rearrange segs to make it so. 36 | LIBDATA: load = ROM15, type = ro, align = $100, optional = yes; 37 | DMC: load = ROM15, type = ro, align = 64, optional = yes; 38 | LIBCODE: load = ROM15, type = ro, align = $80, optional = yes; 39 | CODE: load = ROM15, type = ro, align = $40, optional = yes; 40 | RODATA: load = ROM15, type = ro, align = $10; 41 | PB53CODE: load = ROM15, type = ro, optional = yes; 42 | PB53TABLES: load = ROM15, type = ro, optional = yes; 43 | 44 | # Because UNROM/UOROM (mapper 2) has a fixed bank at $C000-$FFFF, 45 | # we need not include the reset stub in all banks. 46 | STUB15: load = ROM15, type = ro, start = $FF70; 47 | } 48 | 49 | FILES { 50 | %O: format = bin; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /nes/tools/mktables.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Lookup table generator for note periods 4 | # Copyright 2010, 2020 Damian Yerrick 5 | # 6 | # Copying and distribution of this file, with or without 7 | # modification, are permitted in any medium without royalty 8 | # provided the copyright notice and this notice are preserved. 9 | # This file is offered as-is, without any warranty. 10 | # 11 | assert str is not bytes 12 | import sys 13 | 14 | lowestFreq = 55.0 15 | ntscOctaveBase = 39375000.0/(22 * 16 * lowestFreq) 16 | palOctaveBase = 266017125.0/(10 * 16 * 16 * lowestFreq) 17 | maxNote = 87 18 | 19 | def makePeriodTable(filename, pal=False): 20 | semitone = 2.0**(1./12) 21 | octaveBase = palOctaveBase if pal else ntscOctaveBase 22 | relFreqs = [(1 << (i // 12)) * semitone**(i % 12) 23 | for i in range(maxNote)] 24 | periods = [int(round(octaveBase / freq)) - 1 for freq in relFreqs] 25 | systemName = "PAL" if pal else "NTSC" 26 | with open(filename, 'wt') as outfp: 27 | outfp.write("""; %s period table generated by mktables.py 28 | .export periodTableLo, periodTableHi 29 | .segment "RODATA" 30 | periodTableLo:\n""" 31 | % systemName) 32 | for i in range(0, maxNote, 12): 33 | outfp.write(' .byt ' 34 | + ','.join('$%02x' % (i % 256) 35 | for i in periods[i:i + 12]) 36 | + '\n') 37 | outfp.write('periodTableHi:\n') 38 | for i in range(0, maxNote, 12): 39 | outfp.write(' .byt ' 40 | + ','.join('$%02x' % (i >> 8) 41 | for i in periods[i:i + 12]) 42 | + '\n') 43 | 44 | def makePALPeriodTable(filename): 45 | return makePeriodTable(filename, pal=True) 46 | 47 | tableNames = { 48 | 'period': makePeriodTable, 49 | 'palperiod': makePALPeriodTable 50 | } 51 | 52 | def main(argv): 53 | if len(argv) >= 2 and argv[1] in ('/?', '-?', '-h', '--help'): 54 | print("usage: %s TABLENAME FILENAME" % argv[0]) 55 | print("known tables:", ' '.join(sorted(tableNames))) 56 | elif len(argv) < 3: 57 | print("mktables: too few arguments; try %s --help" % argv[0]) 58 | elif argv[1] in tableNames: 59 | tableNames[argv[1]](argv[2]) 60 | else: 61 | print("mktables: no such table %s; try %s --help" % (argv[1], argv[0])) 62 | 63 | if __name__=='__main__': 64 | main(sys.argv) 65 | -------------------------------------------------------------------------------- /gameboy/tools/mapunused.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Calculate unused space from an RGBLINK map file 4 | Copyright 2020 Damian Yerrick 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | """ 22 | import sys 23 | import os 24 | import re 25 | from collections import defaultdict 26 | 27 | filename = "gb240p.map" 28 | if 'idlelib' in sys.modules: 29 | filename = os.path.join("..", filename) 30 | 31 | sectionRE = re.compile( 32 | r"""SECTION:\s+\$([0-7][0-9A-F]*)-\$([0-7][0-9A-F]*)""", 33 | re.IGNORECASE 34 | ) 35 | bankRE = re.compile( 36 | r"""ROM[0X] BANK #?([0-9A-F])+""", 37 | re.IGNORECASE 38 | ) 39 | 40 | used_ranges = defaultdict(list) 41 | banknumber = None 42 | 43 | with open(filename, "r") as infp: 44 | lines = [x.strip() for x in infp] 45 | for line in lines: 46 | m = sectionRE.match(line) 47 | if m: 48 | sl = int(m.group(1), 16), int(m.group(2), 16) + 1 49 | used_ranges[banknumber].append(sl) 50 | continue 51 | m = bankRE.match(line) 52 | if m: 53 | banknumber = m.group(1) 54 | continue 55 | 56 | for banknumber, ranges in used_ranges.items(): 57 | ranges.sort() 58 | freestart = ranges[0][0] & -0x4000 59 | bankend = 0x4000 + freestart 60 | unused_here = [] 61 | for s, e in ranges: 62 | if s > freestart: 63 | unused_here.append((freestart, s)) 64 | freestart = e 65 | if freestart < bankend: 66 | unused_here.append((e, bankend)) 67 | print("Bank %s" % banknumber) 68 | print("\n".join( 69 | "$%04x-$%04x (%d %s)" 70 | % (s, e - 1, e - s, "bytes" if e - s >= 2 else "byte") 71 | for s, e in unused_here 72 | )) 73 | -------------------------------------------------------------------------------- /gba/src/vwflabels.c: -------------------------------------------------------------------------------- 1 | /* 2 | Variable width font label drawing for Game Boy Advance 3 | Copyright 2018 Damian Yerrick 4 | 5 | This work is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any 7 | damages arising from the use of this work. 8 | 9 | Permission is granted to anyone to use this work for any purpose, 10 | including commercial applications, and to alter it and redistribute 11 | it freely, subject to the following restrictions: 12 | 13 | 1. The origin of this work must not be misrepresented; you must 14 | not claim that you wrote the original work. If you use 15 | this work in a product, an acknowledgment in the product 16 | documentation would be appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must 18 | not be misrepresented as being the original work. 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | 22 | "Source" is the preferred form of a work for making changes to it. 23 | 24 | */ 25 | #include 26 | #include "global.h" 27 | 28 | // Single label: 29 | // X (1 byte), Y (1 byte, multiple of 8), printable text code units 30 | // Label set: 31 | // (Single label, '\n')*, Single label, '\0' 32 | 33 | /** 34 | * @param labelset a '\n'-separated, '\0'-terminated list of 35 | * (x, y, printables) 36 | * @param nt which SCREENMAT (tilemap, screenbaseblock) to draw into 37 | * @param tilenum bits 10-0: which tile number to start at; 38 | * bits 15-11: palette id 39 | */ 40 | void vwfDrawLabels(const char *labelset, unsigned int sbb, unsigned int tilenum) { 41 | while (1) { 42 | unsigned int x = labelset[0]; 43 | unsigned int y = labelset[1] & 0xF8; 44 | unsigned int txtw = vwf8StrWidth(labelset + 2); 45 | 46 | // Calculate the width in pixels of tiles occupied by this string, 47 | // including left and right partial tiles 48 | txtw += x & 0x07; // Add left partial tile 49 | txtw = (((txtw - 1) | 0x07) + 1) >> 3; // Round up to whole tile 50 | 51 | // Clear this many tiles to color 0 and draw into them using color 1 52 | void *chrdst = &(tile_mem[0][tilenum & 0x07FF].data); 53 | memset16(chrdst, 0x0000, 16 * txtw); 54 | const char *strend = vwf8Puts(chrdst, labelset + 2, x & 0x07, 1); 55 | 56 | // Fill the SCREENMAT 57 | loadMapRowMajor(&(se_mat[sbb][y >> 3][x >> 3]), tilenum & 0xF3FF, txtw, 1); 58 | tilenum += txtw; 59 | 60 | if (*strend == 0) break; 61 | labelset = strend + 1; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /.github/workflows/build-artifact.yml: -------------------------------------------------------------------------------- 1 | name: build-artifact 2 | on: push 3 | 4 | jobs: 5 | nes-build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@master 9 | with: 10 | fetch-depth: 0 11 | - name: Setup dependencies 12 | shell: bash 13 | run: | 14 | sudo apt-get update 15 | sudo apt-get install build-essential python3-pil -y 16 | 17 | - uses: ./.github/actions/cache_cc65 18 | - name: Run makefile 19 | run: | 20 | cd nes 21 | make 240pee.nes 240pee-bnrom.nes mdfourier.nsf mdfourier4k.nes 22 | 23 | - name: Upload ROM binaries 24 | uses: actions/upload-artifact@master 25 | with: 26 | name: 240pee-NES 27 | path: | 28 | nes/240pee.nes 29 | nes/240pee-bnrom.nes 30 | nes/mdfourier.nsf 31 | nes/mdfourier4k.nes 32 | nes/map.txt 33 | nes/bnmap.txt 34 | nes/mdfmap.txt 35 | nes/nsfmap.txt 36 | gb-build: 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@master 40 | with: 41 | fetch-depth: 0 42 | - name: Setup dependencies 43 | shell: bash 44 | run: | 45 | sudo apt-get update 46 | sudo apt-get install libpng-dev python3-pil build-essential bison pkg-config -y 47 | 48 | - uses: ./.github/actions/cache_rgbds 49 | - name: Run makefile 50 | run: | 51 | cd gameboy 52 | make gb240p.gb 53 | 54 | - name: Upload ROM binaries 55 | uses: actions/upload-artifact@master 56 | with: 57 | name: 240pee-GB 58 | path: | 59 | gameboy/gb240p.gb 60 | gameboy/gb240p.map 61 | gameboy/gb240p.sym 62 | 63 | gba-build: 64 | runs-on: ubuntu-latest 65 | container: devkitpro/devkitarm:latest 66 | steps: 67 | - uses: actions/checkout@master 68 | with: 69 | fetch-depth: 0 70 | - name: Setup dependencies 71 | shell: bash 72 | run: | 73 | sudo apt-get update 74 | sudo apt-get install python3-pil -y 75 | 76 | - name: Run devkitARM makefile 77 | run: | 78 | cd gba 79 | make -f dkaMakefile 80 | 81 | - name: Upload ROM binaries 82 | uses: actions/upload-artifact@master 83 | with: 84 | name: 240pee-GBA 85 | path: | 86 | gba/240pee_mb.gba 87 | gba/240pee_mb.elf 88 | -------------------------------------------------------------------------------- /nes/src/rectfill.inc: -------------------------------------------------------------------------------- 1 | ; 2 | ; Nametable rectangle fill engine 3 | ; Copyright 2015 Damian Yerrick 4 | ; 5 | ; Copying and distribution of this file, with or without 6 | ; modification, are permitted in any medium without royalty provided 7 | ; the copyright notice and this notice are preserved in all source 8 | ; code copies. This file is offered as-is, without any warranty. 9 | ; 10 | .ifndef RECTFILL_INC 11 | RECTFILL_INC = 1 12 | 13 | ; rectfill layout API 14 | ; engine in rectfill.s; layouts are defined in rectfiles.s 15 | .global rf_load_layout 16 | 17 | ; used internally by bank switch driver 18 | .global rf_vwfClearPuts_cb, rf_vwfClearPuts 19 | 20 | ; other related APIs 21 | .global rf_load_tiles, rf_load_tiles_1000, rf_color_lineImgBuf, rf_copy8tiles 22 | .global rf_load_yrgb_palette 23 | 24 | ; Macros for defining rects and labels 25 | 26 | .macro rf_rect in_left, in_top, in_right, in_bottom, in_tile, in_flags 27 | .local rectx, recty, rectw, recth 28 | rectx = ((in_left) + 4) >> 3 29 | recty = ((in_top) + 4) >> 3 30 | rectw = (((in_right) + 4) >> 3) - rectx 31 | recth = (((in_bottom) + 4) >> 3) - recty 32 | .assert 0 <= rectx && rectx < 32, error, "left side out of bounds" 33 | .assert 0 <= recty && recty < 30, error, "top out of bounds" 34 | .assert 0 < rectw && rectx + rectw <= 32, error, "left side out of bounds" 35 | .assert 0 < recth && recth + recty <= 30, error, "top out of bounds" 36 | .dbyt (rectw << 10) | (recty << 5) | rectx 37 | .byte in_tile 38 | .byte (recth << 3) | (in_flags) 39 | .endmacro 40 | 41 | RF_INCR = $04 42 | RF_ROWXOR = $02 43 | RF_COLXOR = $01 44 | 45 | .macro rf_attr in_left, in_top, in_right, in_bottom, in_attr 46 | .local rectx, recty, rectw, recth 47 | rectx = ((in_left) + 8) >> 4 48 | recty = ((in_top) + 8) >> 4 49 | rectw = (((in_right) + 8) >> 4) - rectx 50 | recth = (((in_bottom) + 8) >> 4) - recty 51 | .assert 0 <= rectx && rectx < 16, error, "left side out of bounds" 52 | .assert 0 <= recty && recty < 15, error, "top out of bounds" 53 | .assert 0 < rectw && rectx + rectw <= 16, error, "left side out of bounds" 54 | .assert 0 < recth && recth + recty <= 15, error, "top out of bounds" 55 | .byte recth 56 | .byte (rectw << 3) | (in_attr) 57 | .byte ((recty << 4) & $E0) | (rectx << 1) | (recty & $01) 58 | .endmacro 59 | 60 | ;; 61 | ; Sets the X, Y, and color of the following nul-terminated string. 62 | .macro rf_label in_left, in_top, in_fgc, in_bgc 63 | .local txtx, txty, fgc, bgc 64 | txty = ((in_top) + 4) >> 3 65 | .assert 0 <= txty && txty < 30, error, "top out of bounds" 66 | .byte in_bgc * 5 ^ in_fgc, txty, in_left 67 | .endmacro 68 | 69 | .endif 70 | -------------------------------------------------------------------------------- /gameboy/tools/bitbyte.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | bit packing for compressors 4 | Copyright 2019 Damian Yerrick 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | """ 22 | CHAR_BIT = 8 23 | 24 | class BitByteInterleave(object): 25 | """Interleave bytes with big-endian bits.""" 26 | def __init__(self): 27 | self.out, self.bitsleft = bytearray(), 0 28 | 29 | def putbyte(self, c): 30 | """Add one full byte at the end.""" 31 | self.out.append(c) 32 | 33 | def putbytes(self, c): 34 | """Add full bytes at the end.""" 35 | self.out.extend(c) 36 | 37 | def putbits(self, value, length=1): 38 | """Insert bits into a partially full byte, then add bytes as needed.""" 39 | while length > 0: 40 | # Make room for at least 1 more bit 41 | if self.bitsleft == 0: 42 | self.bitsindex, self.bitsleft = len(self.out), CHAR_BIT 43 | self.out.append(0) 44 | 45 | # How much of this value can we pack? 46 | value &= (1 << length) - 1 47 | length -= self.bitsleft 48 | if length >= 0: 49 | self.bitsleft = 0 # squeeze as many bits as we can 50 | self.out[self.bitsindex] |= value >> length 51 | else: 52 | self.bitsleft = -length # space left for more bits 53 | self.out[self.bitsindex] |= value << -length 54 | 55 | def __len__(self): 56 | """Return len(bytes(self)), the length of contents in bytes.""" 57 | return len(self.out) 58 | 59 | def __bytes__(self): 60 | """Return contents as a bytes object.""" 61 | return bytes(self.out) 62 | 63 | def __iter__(self): 64 | """Return an iterator over the contents as bytes.""" 65 | return iter(self.out) 66 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # 3 | # Makefile for 240p test suite outer packaging 4 | # Copyright 2021-2025 Damian Yerrick 5 | # 6 | # Copying and distribution of this file, with or without 7 | # modification, are permitted in any medium without royalty 8 | # provided the copyright notice and this notice are preserved. 9 | # This file is offered as-is, without any warranty. 10 | # 11 | title := 240p-test-mini 12 | version := 0.23 13 | 14 | # Make $(MAKE) work correctly even when Make is installed inside 15 | # C:\Program Files 16 | ifneq ($(words $(MAKE)), 1) 17 | MAKE:="$(MAKE)" 18 | endif 19 | 20 | alltargets:=\ 21 | nes/240pee.nes nes/240pee-bnrom.nes nes/240pee-tgrom.nes \ 22 | nes/mdfourier.nsf nes/mdfourier4k.nes nes/mdfourier4k-chrrom.nes \ 23 | nes/mdfourier4k-autostart.nes nes/mdfourier4k-chrrom-autostart.nes \ 24 | gameboy/gb240p.gb gba/240pee_mb.gba nes/240pee-sgrom.nes 25 | 26 | .PHONY: all dist clean $(alltargets) 27 | all: $(alltargets) 28 | dist: $(title)-docsrc-$(version).zip $(title)-$(version).zip 29 | 30 | # Try to minimize the harm of recursive make. 31 | # 240pee-bnrom.nes depends on 240pee.nes so that parallel make (-j2) 32 | # doesn't try double-building each file in both 33 | nes/240pee.nes: 34 | $(MAKE) -C nes $(notdir $@) 35 | 36 | # These two rules have exactly the same prerequisites and recipe yet 37 | # cannot be combined lest GNU Make 3.82 and newer emit a fatal error: 38 | # makefile:35: *** mixed implicit and normal rules. Stop. 39 | # 40 | nes/240pee-%rom.nes: nes/240pee.nes 41 | $(MAKE) -C nes $(notdir $@) 42 | nes/mdfourier.nsf nes/mdfourier4k.nes: nes/240pee.nes 43 | $(MAKE) -C nes $(notdir $@) 44 | 45 | nes/mdfourier4k-%.nes: nes/mdfourier4k.nes 46 | $(MAKE) -C nes $(notdir $@) 47 | 48 | gameboy/gb240p.gb: 49 | $(MAKE) -C gameboy $(notdir $@) 50 | gba/240pee_mb.gba: 51 | $(MAKE) -C gba -f dkaMakefile $(notdir $@) 52 | 53 | # Packaging 54 | DOCSRC_RE:=\.xcf$$|\.odg$$|/Gus_sketch_by_.*.png 55 | 56 | $(title)-$(version).zip: zip.in all makefile README.md 57 | zip -9 -u $@ -@ < $< 58 | 59 | $(title)-docsrc-$(version).zip: docsrc.zip.in makefile 60 | zip -9 -u $@ -@ < $< 61 | 62 | zip.in: makefile nes/makefile gameboy/makefile \ 63 | gba/dkaMakefile gba/wfMakefile 64 | git ls-files | grep -Eiv "^\.|$(DOCSRC_RE)" > $@ 65 | printf '%s\n' $(alltargets) >> $@ 66 | echo $@ >> $@ 67 | 68 | docsrc.zip.in: makefile nes/makefile gameboy/makefile \ 69 | gba/dkaMakefile gba/wfMakefile 70 | git ls-files | grep -E "$(DOCSRC_RE)" > $@ 71 | echo LICENSE >> $@ 72 | echo $@ >> $@ 73 | 74 | clean: 75 | $(MAKE) -C nes clean 76 | $(MAKE) -C gameboy clean 77 | $(MAKE) -C gba clean 78 | -------------------------------------------------------------------------------- /nes/src/rand.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; Randum number generator 3 | ; 4 | ; Written and donated by Sidney Cadot - sidney@ch.twi.tudelft.nl 5 | ; 2016-11-07, modified by Brad Smith 6 | ; 7 | ; May be distributed with the cc65 runtime using the same license: 8 | ; 9 | ; This software is provided 'as-is', without any express or implied 10 | ; warranty. In no event will the authors be held liable for any damages 11 | ; arising from the use of this software. 12 | ; 13 | ; Permission is granted to anyone to use this software for any purpose, 14 | ; including commercial applications, and to alter it and redistribute it 15 | ; freely, subject to the following restrictions: 16 | ; 17 | ; 1. The origin of this software must not be misrepresented; you must not 18 | ; claim that you wrote the original software. If you use this software 19 | ; in a product, an acknowledgment in the product documentation would be 20 | ; appreciated but is not required. 21 | ; 2. Altered source versions must be plainly marked as such, and must not be 22 | ; misrepresented as being the original software. 23 | ; 3. This notice may not be removed or altered from any source distribution. 24 | ; 25 | ; 26 | ; int rand (void); 27 | ; void srand (unsigned seed); 28 | ; 29 | ; Uses 4-byte state. 30 | ; Multiplier must be 1 (mod 4) 31 | ; Added value must be 1 (mod 2) 32 | ; This guarantees max. period (2**32) 33 | ; The lowest bits have poor entropy and 34 | ; exhibit easily detectable patterns, so 35 | ; only the upper bits 16-22 and 24-31 of the 36 | ; 4-byte state are returned. 37 | ; 38 | ; The best 8 bits, 24-31 are returned in the 39 | ; low byte A to provide the best entropy in the 40 | ; most commonly used part of the return value. 41 | ; 42 | 43 | .export _rand, _srand 44 | 45 | .bss 46 | 47 | ; The seed. When srand() is not called, the C standard says that that rand() 48 | ; should behave as if srand() was called with an argument of 1 before. 49 | rand: .res 4 50 | 51 | .code 52 | 53 | _rand: clc 54 | lda rand+0 ; SEED += $B3 55 | adc #$B3 56 | sta rand+0 57 | adc rand+1 ; SEED *= $01010101 58 | sta rand+1 59 | adc rand+2 60 | sta rand+2 61 | and #$7f ; Suppress sign bit (make it positive) 62 | tax 63 | lda rand+2 64 | adc rand+3 65 | sta rand+3 66 | rts ; return bit (16-22,24-31) in (X,A) 67 | 68 | _srand: sta rand+0 ; Store the seed 69 | stx rand+1 70 | lda #0 71 | sta rand+2 ; Set MSW to zero 72 | sta rand+3 73 | rts 74 | 75 | -------------------------------------------------------------------------------- /gba/src/undte.c: -------------------------------------------------------------------------------- 1 | /* 2 | Digram tree encoding (DTE) unpacker 3 | Copyright 2018 Damian Yerrick 4 | 5 | This work is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any 7 | damages arising from the use of this work. 8 | 9 | Permission is granted to anyone to use this work for any purpose, 10 | including commercial applications, and to alter it and redistribute 11 | it freely, subject to the following restrictions: 12 | 13 | 1. The origin of this work must not be misrepresented; you must 14 | not claim that you wrote the original work. If you use 15 | this work in a product, an acknowledgment in the product 16 | documentation would be appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must 18 | not be misrepresented as being the original work. 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | 22 | "Source" is the preferred form of a work for making changes to it. 23 | 24 | */ 25 | #include "global.h" 26 | 27 | #define DTE_STACK_SIZE 8 28 | #define DTE_MIN_CODEUNIT 136 29 | #define MIN_PRINTABLE 32 30 | 31 | extern const unsigned char dte_replacements[][2]; 32 | 33 | /** 34 | * Decompresses digram tree encoded (DTE) text from src to dst. 35 | * Stops at the first code unit less than MIN_PRINTABLE. 36 | * @param src source address 37 | * @param dst destination address 38 | * @param srcend If not NULL, 1 byte after final low code unit 39 | * @return pointer to low code unit in dst 40 | */ 41 | char *undte_src(char *dst, const char *src, const char **srcend) { 42 | unsigned char dtestack[DTE_STACK_SIZE]; 43 | unsigned int dtedepth = 0; 44 | unsigned int ctoprint = 0; 45 | 46 | while (1) { 47 | // charloop 48 | 49 | // If there's a code on the stack, print it. Otherwise, 50 | // retrieve a code from the compressed text. 51 | if (dtedepth > 0) { 52 | ctoprint = (unsigned char)dtestack[--dtedepth]; 53 | } else { 54 | ctoprint = (unsigned char)*src++; 55 | } 56 | while (1) { 57 | // print_code_a 58 | // If not a pair, save it to the string. 59 | if (ctoprint < DTE_MIN_CODEUNIT) { 60 | *dst = ctoprint; 61 | if (ctoprint < MIN_PRINTABLE) { 62 | if (srcend) *srcend = src; 63 | return dst; 64 | } 65 | ++dst; 66 | break; 67 | } 68 | 69 | // code_is_pair 70 | const unsigned char *pair = dte_replacements[ctoprint - DTE_MIN_CODEUNIT]; 71 | if (dtedepth < DTE_STACK_SIZE) { 72 | dtestack[dtedepth++] = pair[1]; 73 | } 74 | ctoprint = pair[0]; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /nes/src/bnrom.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; BNROM driver for NES 3 | ; Copyright 2011-2016 Damian Yerrick 4 | ; 5 | ; Copying and distribution of this file, with or without 6 | ; modification, are permitted in any medium without royalty provided 7 | ; the copyright notice and this notice are preserved in all source 8 | ; code copies. This file is offered as-is, without any warranty. 9 | ; 10 | 11 | .include "nes2header.inc" 12 | nes2prg 65536 13 | nes2chr 0 14 | nes2chrram 8192 15 | nes2mirror 'V' 16 | nes2mapper 34 17 | nes2tv 'N','P' 18 | nes2end 19 | 20 | .import reset_handler 21 | .importzp nmis 22 | .import unpb53_some, unpb53_file_cb, load_sb53_file_cb, load_iu53_file_cb 23 | .export unpb53_gate, unpb53_file, load_sb53_file, load_iu53_file 24 | .import rf_vwfClearPuts_cb, rf_load_layout_cb 25 | .export rf_vwfClearPuts, rf_load_layout 26 | .export rtl 27 | 28 | ; Fixed code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 29 | 30 | MAIN_CODE_BANK = $01 31 | RESETSTUB_BASE = $FF90 32 | 33 | .macro resetstub_in segname, scopename 34 | .segment segname 35 | .proc scopename 36 | .assert * = ::RESETSTUB_BASE, error, "RESETSTUB_BASE doesn't match linker configuration" 37 | 38 | ; Call gates 39 | zerobyte: .byte $00 40 | unpb53_gate: 41 | lsr zerobyte 42 | jsr unpb53_some 43 | ; Switch back to the main code bank 44 | rtl: 45 | ldx #MAIN_CODE_BANK 46 | stx rtl+1 47 | rts 48 | 49 | load_sb53_file: 50 | lsr zerobyte 51 | jsr load_sb53_file_cb 52 | jmp rtl 53 | 54 | load_iu53_file: 55 | lsr zerobyte 56 | jsr load_iu53_file_cb 57 | jmp rtl 58 | 59 | unpb53_file: 60 | lsr zerobyte 61 | jsr unpb53_file_cb 62 | jmp rtl 63 | 64 | rf_load_layout: 65 | lsr zerobyte ; lsr zerobyte 66 | jsr rf_load_layout_cb 67 | jmp rtl 68 | 69 | rf_vwfClearPuts: 70 | lda #MAIN_CODE_BANK 71 | sta rtl+1 72 | jsr rf_vwfClearPuts_cb 73 | lsr zerobyte ; lsr zerobyte 74 | rts 75 | 76 | nmi_handler: 77 | inc nmis 78 | irq_handler: 79 | rti 80 | resetstub_entry: 81 | sei 82 | ldx #$FF 83 | txs 84 | stx resetstub_entry+2 85 | jmp reset_handler 86 | .assert * <= $FFE0, warn, "reset stub extends into Famicom Box header area" 87 | .res ::scopename - ::RESETSTUB_BASE + $FFFA - * 88 | .addr nmi_handler, resetstub_entry, irq_handler 89 | .endproc 90 | .endmacro 91 | 92 | resetstub_in "STUB0",stub0 93 | resetstub_in "STUB1",stub1 94 | unpb53_gate = stub1::unpb53_gate 95 | unpb53_file = stub1::unpb53_file 96 | load_sb53_file = stub1::load_sb53_file 97 | load_iu53_file = stub1::load_iu53_file 98 | rf_vwfClearPuts = stub1::rf_vwfClearPuts 99 | rf_load_layout = stub1::rf_load_layout 100 | rtl := stub1::rtl 101 | -------------------------------------------------------------------------------- /nes/tools/ld65ramuse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Tool to measure RAM use and ROM use by object file 4 | Copyright 2017-2018 Damian Yerrick 5 | [insert zlib license here] 6 | """ 7 | import sys 8 | import os 9 | from collections import defaultdict 10 | 11 | def ld65_map_get_sections(filename): 12 | with open(filename, "r", encoding="utf-8") as infp: 13 | lines = [line.rstrip() for line in infp] 14 | sectbreaks = [ 15 | i - 1 for i, line in enumerate(lines) 16 | if len(line) >= 4 and not line.rstrip('-') 17 | ] 18 | sections = [lines[i:j] for i, j in zip(sectbreaks, sectbreaks[1:])] 19 | sections.append(lines[sectbreaks[-1]:]) 20 | return { 21 | s[0].rstrip(':').lower(): s[2:] 22 | for s in sections 23 | } 24 | 25 | def ld65_parse_modules_list(modules_list): 26 | module = None 27 | for line in modules_list: 28 | if not line: 29 | continue 30 | leading_spc = len(line) - len(line.lstrip()) 31 | if not leading_spc and line.endswith(':'): 32 | module = line.rstrip(':') 33 | continue 34 | 35 | line = line.split() 36 | segment = line.pop(0) 37 | nvp = [s.split("=", 1) for s in line] 38 | nvp = {k.lower(): int(v, base=16) for k, v in nvp} 39 | size = nvp['size'] 40 | yield module, segment, size 41 | 42 | def main(argv=None): 43 | argv = argv or sys.argv 44 | prog = os.path.basename(argv[0]) 45 | if len(argv) < 2: 46 | print("%s: no map file; try %s --help" 47 | % (prog, prog), file=sys.stderr) 48 | sys.exit(1) 49 | if argv[1] in ['-h', '--help']: 50 | print("usage: %s map.txt" % prog) 51 | return 52 | filename = argv[1] 53 | 54 | sections_by_title = ld65_map_get_sections(filename) 55 | ml = sections_by_title['modules list'] 56 | 57 | # sizes['RODATA']['vwf7.o'] 58 | sizes_by_segment = defaultdict(dict) 59 | for module, segment, size in ld65_parse_modules_list(ml): 60 | sizes_by_segment[segment][module] = size 61 | for segment, module_sizes in sorted(sizes_by_segment.items()): 62 | segtotal = sum(module_sizes.values()) 63 | print("%s: %d bytes" % (segment, segtotal)) 64 | szs = sorted(module_sizes.items(), key=lambda x: -x[1]) 65 | tszs = [" %s: %d (%.1f%%)" 66 | % (module, sz, round(100 * sz / segtotal, 1)) 67 | for module, sz in szs] 68 | print("\n".join(tszs)) 69 | 70 | if __name__=='__main__': 71 | in_IDLE = 'idlelib.__main__' in sys.modules or 'idlelib.run' in sys.modules 72 | if in_IDLE: 73 | main(['ld65ramuse.py', '../map.txt']) 74 | else: 75 | main() 76 | -------------------------------------------------------------------------------- /gameboy/tools/uniq.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Convert binary file to unique tiles and a tilemap 4 | Copyright 2018, 2023 Damian Yerrick 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | """ 22 | import sys 23 | import argparse 24 | 25 | def parse_argv(argv): 26 | parser = argparse.ArgumentParser() 27 | parser.add_argument("INFILE") 28 | parser.add_argument("TILEFILE") 29 | parser.add_argument("MAPFILE") 30 | parser.add_argument("--block-size", type=int, default=16, 31 | help="size of tile in bytes (NES, GB: 16; SMS, MD, SNES: 32)") 32 | parser.add_argument('-b', "--map-add", "--base-tiles", type=lambda x: int(x, 0), default=0, 33 | help="add this value to all map entries") 34 | args = parser.parse_args(argv[1:]) 35 | return args 36 | 37 | def uniq(it): 38 | tiles = [] 39 | tile2id = {} 40 | tilemap = [] 41 | for tile in it: 42 | if tile not in tiles: 43 | tile2id[tile] = len(tiles) 44 | tiles.append(tile) 45 | tilemap.append(tile2id[tile]) 46 | return tiles, tilemap 47 | 48 | def main(argv=None): 49 | args = parse_argv(argv or sys.argv) 50 | with open(args.INFILE, "rb") as infp: 51 | data = infp.read() 52 | block_size = args.block_size 53 | data = [data[i:i + block_size] for i in range(0, len(data), block_size)] 54 | tiles, tilemap = uniq(data) 55 | if len(tiles) > 256: 56 | print("%s: too many tiles (%d)" 57 | % (args.INFILE, len(tiles)), file=sys.stderr) 58 | sys.exit(1) 59 | map_add = args.map_add 60 | tilemap = bytes((map_add + b) & 0xFF for b in tilemap) 61 | with open(args.TILEFILE, "wb") as outfp: 62 | outfp.writelines(tiles) 63 | with open(args.MAPFILE, "wb") as outfp: 64 | outfp.write(tilemap) 65 | 66 | if __name__=='__main__': 67 | main() 68 | ## main(["uniq.py", "testbg.chr", "woods.chr", "testbg.map"]) 69 | -------------------------------------------------------------------------------- /nes/src/init.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; Init code for 240p test suite 3 | ; Copyright 2015 Damian Yerrick 4 | ; 5 | ; Copying and distribution of this file, with or without 6 | ; modification, are permitted in any medium without royalty provided 7 | ; the copyright notice and this notice are preserved in all source 8 | ; code copies. This file is offered as-is, without any warranty. 9 | ; 10 | .include "nes.inc" 11 | .include "global.inc" 12 | 13 | .segment "LIBCODE" 14 | .proc reset_handler 15 | ; The very first thing to do when powering on is to put all sources 16 | ; of interrupts into a known state. 17 | sei ; Disable interrupts 18 | ldx #$00 19 | stx PPUCTRL ; Disable NMI and set VRAM increment to 32 20 | stx PPUMASK ; Disable rendering 21 | stx $4010 ; Disable DMC IRQ 22 | dex ; Subtracting 1 from $00 gives $FF, which is a 23 | txs ; quick way to set the stack pointer to $01FF 24 | bit PPUSTATUS ; Acknowledge stray vblank NMI across reset 25 | bit SNDCHN ; Acknowledge DMC IRQ 26 | lda #$40 27 | sta P2 ; Disable APU Frame IRQ 28 | lda #$0F 29 | sta SNDCHN ; Disable DMC playback, initialize other channels 30 | 31 | vwait1: 32 | bit PPUSTATUS ; It takes one full frame for the PPU to become 33 | bpl vwait1 ; stable. Wait for the first frame's vblank. 34 | 35 | ; We have about 29700 cycles to burn until the second frame's 36 | ; vblank. Use this time to get most of the rest of the chipset 37 | ; into a known state. 38 | 39 | ; Disable (normally nonfunctional) BCD mode for compatibility 40 | ; with debuggers and post-patent famiclones 41 | cld 42 | 43 | ; Clear OAM and the zero page here. 44 | ; We don't copy the cleared OAM to the PPU until later. 45 | ldx #0 46 | jsr ppu_clear_oam ; clear out OAM from X to end and set X to 0 47 | 48 | txa 49 | clear_zp: 50 | sta $00,x 51 | inx 52 | bne clear_zp 53 | 54 | ; Other things that can be done here (not shown): 55 | ; Set up PRG RAM 56 | ; Copy initial high scores, bankswitching trampolines, etc. to RAM 57 | ; Set up your sound engine 58 | ; Set up the mapper 59 | 60 | vwait2: 61 | bit PPUSTATUS ; After the second vblank, we know the PPU has 62 | bpl vwait2 ; fully stabilized. 63 | 64 | ; There are two ways to wait for vertical blanking: spinning on 65 | ; bit 7 of PPUSTATUS (as seen above) and waiting for the NMI 66 | ; handler to run. Before the PPU has stabilized, you want to use 67 | ; the PPUSTATUS method because NMI might not be reliable. But 68 | ; afterward, you want to use the NMI method because if you read 69 | ; PPUSTATUS at the exact moment that the bit turns on, it'll flip 70 | ; from off to on to off faster than the CPU can see. 71 | 72 | jmp main 73 | .endproc 74 | 75 | -------------------------------------------------------------------------------- /gba/tools/vwfbuild.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from __future__ import with_statement 3 | from PIL import Image 4 | import array 5 | 6 | def ca65_bytearray(s): 7 | s = [' .byte ' + ','.join("%3d" % ch for ch in s[i:i + 16]) 8 | for i in range(0, len(s), 16)] 9 | return '\n'.join(s) 10 | 11 | def vwfcvt(filename, tileHt=8): 12 | im = Image.open(filename) 13 | pixels = im.load() 14 | (w, h) = im.size 15 | (xparentColor, sepColor) = im.getextrema() 16 | widths = bytearray() 17 | tiledata = bytearray() 18 | for yt in range(0, h, tileHt): 19 | for xt in range(0, w, 8): 20 | # step 1: find the glyph width 21 | tilew = 8 22 | for x in range(8): 23 | if pixels[x + xt, yt] == sepColor: 24 | tilew = x 25 | break 26 | # step 2: encode the pixels 27 | widths.append(tilew) 28 | for y in range(tileHt): 29 | rowdata = 0 30 | for x in range(8): 31 | pxhere = pixels[x + xt, y + yt] 32 | pxhere = 0 if pxhere in (xparentColor, sepColor) else 1 33 | rowdata |= pxhere << x 34 | tiledata.append(rowdata) 35 | return (widths, tiledata) 36 | 37 | def main(argv=None): 38 | import sys 39 | if argv is None: 40 | argv = sys.argv 41 | if len(argv) > 1 and argv[1] == '--help': 42 | print("usage: %s font.png font.s [font.h]" % argv[0]) 43 | return 44 | if len(argv) < 3 or len(argv) > 4: 45 | print("wrong number of options; try %s --help" % argv[0], file=sys.stderr) 46 | sys.exit(1) 47 | 48 | (widths, tiledata) = vwfcvt(argv[1]) 49 | out = ["@ Generated by vwfbuild", 50 | ".global vwfChrWidths, vwfChrData", 51 | ".hidden vwfChrWidths, vwfChrData", 52 | '.section .rodata', 53 | '.align 2', 54 | 'vwfChrData:', 55 | ca65_bytearray(tiledata), 56 | "vwfChrWidths:", 57 | ca65_bytearray(widths), 58 | ''] 59 | with open(argv[2], 'w') as outfp: 60 | outfp.write('\n'.join(out)) 61 | if len(argv) > 3: 62 | out = ["// Generated by vwfbuild", 63 | "#ifndef VWFCHR_H__", 64 | "#define VWFCHR_H__", 65 | "extern const unsigned char vwfChrData[" + str(int(len(tiledata) / 8)) + "][8];\n" 66 | "extern const unsigned char vwfChrWidths[" + str(len(widths)) + "];\n" 67 | "#endif", 68 | ''] 69 | with open(argv[3], 'w') as outfp: 70 | outfp.write('\n'.join(out)) 71 | 72 | if __name__ == '__main__': 73 | ## main(['vwfbuild', '../tilesets/vwf7.png', '../obj/vwf7.s']) 74 | main() 75 | -------------------------------------------------------------------------------- /common/tools/cp144p.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Character encoding for 144p Test Suite 4 | """ 5 | import codecs 6 | 7 | # The first 96 glyphs in the font correspond to the 96 printable code 8 | # points of the Basic Latin (ASCII) block, U+0020 through U+007F. 9 | # The rest are as follows, beginning at 0x80. 10 | name = 'cp144p' 11 | extra_codepoints = [ 12 | # mammoth, unknown, copyright, double-struck X 13 | 0x1F9A3, 0xFFFE, 0x00A9, 0x1D54F, 14 | # right arrow, left arrow, up arrow, down arrow 15 | 0x2192, 0x2190, 0x2191, 0x2193 16 | ] 17 | 18 | # Codecs API boilerplate ############################################ 19 | 20 | ### Codec APIs 21 | 22 | class Codec(codecs.Codec): 23 | 24 | def encode(self,input,errors='strict'): 25 | return codecs.charmap_encode(input,errors,encoding_table) 26 | 27 | def decode(self,input,errors='strict'): 28 | return codecs.charmap_decode(input,errors,decoding_table) 29 | 30 | class IncrementalEncoder(codecs.IncrementalEncoder): 31 | def encode(self, input, final=False): 32 | return codecs.charmap_encode(input,self.errors,encoding_table)[0] 33 | 34 | class IncrementalDecoder(codecs.IncrementalDecoder): 35 | def decode(self, input, final=False): 36 | return codecs.charmap_decode(input,self.errors,decoding_table)[0] 37 | 38 | class StreamWriter(Codec,codecs.StreamWriter): 39 | pass 40 | 41 | class StreamReader(Codec,codecs.StreamReader): 42 | pass 43 | 44 | ### encodings module API 45 | 46 | def getregentry(): 47 | return codecs.CodecInfo( 48 | name=name, 49 | encode=Codec().encode, 50 | decode=Codec().decode, 51 | incrementalencoder=IncrementalEncoder, 52 | incrementaldecoder=IncrementalDecoder, 53 | streamreader=StreamReader, 54 | streamwriter=StreamWriter, 55 | ) 56 | 57 | def register(): 58 | ci = getregentry() 59 | def lookup(encoding): 60 | if encoding == name: 61 | return ci 62 | codecs.register(lookup) 63 | 64 | # End boilerplate ################################################### 65 | 66 | ### Translate the decoding 67 | 68 | decoding_table = list(range(128)) 69 | decoding_table[24:32] = extra_codepoints 70 | decoding_table.extend([0xFFFE] * (256 - len(decoding_table))) 71 | decoding_table = ''.join(chr(x) for x in decoding_table) 72 | encoding_table=codecs.charmap_build(decoding_table) 73 | register() 74 | 75 | # Transitional API 76 | 77 | def decode_240ptxt(blo, errors='strict'): 78 | return str(blo, name, errors) 79 | 80 | def encode_240ptxt(s, errors='strict'): 81 | return bytes(s, name, errors) 82 | 83 | ### Testing 84 | 85 | def main(): 86 | s = "HELLO\u00A9\U0001F426" 87 | b = s.encode(name) 88 | print(s) 89 | print(b.hex()) 90 | 91 | if __name__=='__main__': 92 | main() 93 | -------------------------------------------------------------------------------- /common/tools/cp240p.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Character encoding for 144p Test Suite 4 | """ 5 | import codecs 6 | 7 | # The first 96 glyphs in the font correspond to the 96 printable code 8 | # points of the Basic Latin (ASCII) block, U+0020 through U+007F. 9 | # The rest are as follows, beginning at 0x80. 10 | name = 'cp240p' 11 | extra_codepoints = [ 12 | # copyright, double-struck X, Greek lowercase mu, mammoth 13 | 0x00A9, 0x1D54F, 0x03bc, 0x1F9A3, 14 | # up arrow, down arrow, left arrow, right arrow 15 | 0x2191, 0x2193, 0x2190, 0x2192, 16 | ] 17 | 18 | # Codecs API boilerplate ############################################ 19 | 20 | ### Codec APIs 21 | 22 | class Codec(codecs.Codec): 23 | 24 | def encode(self,input,errors='strict'): 25 | return codecs.charmap_encode(input,errors,encoding_table) 26 | 27 | def decode(self,input,errors='strict'): 28 | return codecs.charmap_decode(input,errors,decoding_table) 29 | 30 | class IncrementalEncoder(codecs.IncrementalEncoder): 31 | def encode(self, input, final=False): 32 | return codecs.charmap_encode(input,self.errors,encoding_table)[0] 33 | 34 | class IncrementalDecoder(codecs.IncrementalDecoder): 35 | def decode(self, input, final=False): 36 | return codecs.charmap_decode(input,self.errors,decoding_table)[0] 37 | 38 | class StreamWriter(Codec,codecs.StreamWriter): 39 | pass 40 | 41 | class StreamReader(Codec,codecs.StreamReader): 42 | pass 43 | 44 | ### encodings module API 45 | 46 | def getregentry(): 47 | return codecs.CodecInfo( 48 | name=name, 49 | encode=Codec().encode, 50 | decode=Codec().decode, 51 | incrementalencoder=IncrementalEncoder, 52 | incrementaldecoder=IncrementalDecoder, 53 | streamreader=StreamReader, 54 | streamwriter=StreamWriter, 55 | ) 56 | 57 | def register(): 58 | ci = getregentry() 59 | def lookup(encoding): 60 | if encoding == name: 61 | return ci 62 | codecs.register(lookup) 63 | 64 | # End boilerplate ################################################### 65 | 66 | ### Translate the decoding 67 | 68 | decoding_table = list(range(128)) 69 | decoding_table.extend(extra_codepoints) 70 | decoding_table.extend([0xFFFE] * (256 - len(decoding_table))) 71 | decoding_table = ''.join(chr(x) for x in decoding_table) 72 | encoding_table=codecs.charmap_build(decoding_table) 73 | register() 74 | 75 | # Transitional API 76 | 77 | def decode_240ptxt(blo, errors='strict'): 78 | return str(blo, name, errors) 79 | 80 | def encode_240ptxt(s, errors='strict'): 81 | return bytes(s, name, errors) 82 | 83 | ### Testing 84 | 85 | def main(): 86 | s = "HELLO\u00A9\U0001F426" 87 | b = s.encode(name) 88 | print(s) 89 | print(b.hex()) 90 | 91 | if __name__=='__main__': 92 | main() 93 | -------------------------------------------------------------------------------- /nes/tools/chnutils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Nametable compression for cut scenes 4 | # Copyright 2011-2015 Damian Yerrick 5 | # 6 | # Copying and distribution of this file, with or without 7 | # modification, are permitted in any medium without royalty 8 | # provided the copyright notice and this notice are preserved. 9 | # This file is offered as-is, without any warranty. 10 | # 11 | from __future__ import with_statement 12 | 13 | def histo(it): 14 | """Count occurrences of each distinct element in an iterable.""" 15 | out = {} 16 | for el in it: 17 | out.setdefault(el, 0) 18 | out[el] += 1 19 | return out 20 | 21 | def dedupe_chr(chrdata): 22 | seen_chrdata = {} 23 | nt = [] 24 | for tile in chrdata: 25 | seen_chrdata.setdefault(tile, len(seen_chrdata)) 26 | nt.append(seen_chrdata[tile]) 27 | seen_chrdata = sorted(seen_chrdata.items(), key=lambda x: x[1]) 28 | seen_chrdata = [row[0] for row in seen_chrdata] 29 | return (seen_chrdata, nt) 30 | 31 | def compress_nt(ntdata): 32 | from sys import stderr as red 33 | from bitbuilder import BitBuilder, log2 34 | 35 | runcounts = {} # used for determining backref 36 | base = 0 37 | runs = [] 38 | greatest = -1 39 | while base < len(ntdata): 40 | 41 | # measure the run of new tiles (greatest+i+1) 42 | # starting at t 43 | i = 0 44 | imax = min(128, len(ntdata) - base) 45 | while (i < imax 46 | and (greatest + i + 1) % 256 == ntdata[base + i]): 47 | i += 1 48 | if i > 0: 49 | greatest += i 50 | base += i 51 | runs.append((-1, i)) 52 | continue 53 | 54 | # measure the +0 run starting at t 55 | i = 1 56 | imax = min(128, len(ntdata) - base) 57 | while (i < imax 58 | and ntdata[base] == ntdata[base + i]): 59 | i += 1 60 | 61 | # we use the same number of bits for a backreference 62 | # that are in greatest 63 | runs.append((ntdata[base], i, log2(greatest) + 1)) 64 | runcounts.setdefault(ntdata[base], 0) 65 | runcounts[ntdata[base]] += 1 66 | base += i 67 | runcounts = sorted(runcounts.items(), key=lambda x: -x[1]) 68 | most_common_backref = runcounts[0][0] if len(runcounts) else 0 69 | 70 | out = BitBuilder() 71 | out.append(most_common_backref, 8) 72 | for row in runs: 73 | idx, runlength = row[:2] 74 | out.appendGamma(runlength - 1) 75 | if idx < 0: 76 | out.append(2, 2) 77 | elif idx == most_common_backref: 78 | out.append(3, 2) 79 | else: 80 | nbits = row[2] 81 | if idx >= 1 << nbits: 82 | print("index FAIL! %d can't fit in %d bits" % (idx, nbits), 83 | file=red) 84 | out.append(idx, nbits + 1) 85 | return str(out) 86 | -------------------------------------------------------------------------------- /nes/src/mmc1.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; MMC1 driver for NES 3 | ; Copyright 2011-2023 Damian Yerrick 4 | ; 5 | ; Copying and distribution of this file, with or without 6 | ; modification, are permitted in any medium without royalty provided 7 | ; the copyright notice and this notice are preserved in all source 8 | ; code copies. This file is offered as-is, without any warranty. 9 | ; 10 | 11 | .include "nes2header.inc" 12 | nes2prg 65536 13 | nes2chr 0 14 | nes2chrram 8192 15 | nes2mirror 'V' 16 | nes2mapper 1 17 | nes2tv 'N','P' 18 | nes2end 19 | 20 | .import reset_handler 21 | .importzp nmis 22 | .import unpb53_some, unpb53_file_cb, load_sb53_file_cb, load_iu53_file_cb 23 | .export unpb53_gate, unpb53_file, load_sb53_file, load_iu53_file 24 | .import rf_vwfClearPuts_cb, rf_load_layout_cb 25 | .export rf_vwfClearPuts, rf_load_layout 26 | .export rtl, mmc_bank_a 27 | 28 | ; Fixed code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 29 | 30 | MAIN_CODE_BANK = $02 31 | GATE_DATA_BANK = $01 32 | RESETSTUB_BASE = $FFD0 33 | 34 | .code 35 | ; Second stage reset handler: init $8000 and $A000 36 | reset_stage2: 37 | ldx #1 38 | lda #0 39 | sta $8000 ; vertical mirroring 40 | stx $8000 ; not 1-screen mirroring 41 | stx $8000 ; fix last bank 42 | stx $8000 ; not 32k mode 43 | sta $8000 ; 8K CHR bank 44 | sta $A000 45 | sta $A000 46 | sta $A000 47 | sta $A000 48 | sta $A000 49 | jmp reset_handler 50 | 51 | ; Call gates 52 | unpb53_gate: 53 | lda #GATE_DATA_BANK 54 | jsr mmc_bank_a 55 | jsr unpb53_some 56 | rtl: 57 | lda #MAIN_CODE_BANK 58 | mmc_bank_a: 59 | sta $E000 60 | lsr a 61 | sta $E000 62 | lsr a 63 | sta $E000 64 | lsr a 65 | sta $E000 66 | lsr a 67 | sta $E000 68 | rts 69 | 70 | load_sb53_file: 71 | jsr load_sb53_file_cb 72 | jmp rtl 73 | 74 | load_iu53_file: 75 | jsr load_iu53_file_cb 76 | jmp rtl 77 | 78 | unpb53_file: 79 | jsr unpb53_file_cb 80 | jmp rtl 81 | 82 | rf_load_layout: 83 | pha 84 | lda #GATE_DATA_BANK 85 | jsr mmc_bank_a 86 | pla 87 | jsr rf_load_layout_cb 88 | jmp rtl 89 | 90 | nmi_handler: 91 | inc nmis 92 | irq_handler: 93 | rti 94 | 95 | .macro resetstub_in segname, scopename 96 | .segment segname 97 | .proc scopename 98 | .assert * = ::RESETSTUB_BASE || * = ::RESETSTUB_BASE - $4000, error, "RESETSTUB_BASE doesn't match linker configuration" 99 | 100 | resetstub_entry: 101 | sei 102 | ldx #$FF 103 | txs 104 | stx resetstub_entry+2 105 | jmp reset_stage2 106 | .assert * <= $FFE0, warn, "reset stub extends into Famicom Box header area" 107 | .res ::scopename - ::RESETSTUB_BASE + $FFFA - * 108 | .addr nmi_handler, resetstub_entry, irq_handler 109 | .endproc 110 | .endmacro 111 | 112 | resetstub_in "STUB00", stub00 113 | resetstub_in "STUB01", stub01 114 | resetstub_in "STUB02", stub02 115 | resetstub_in "STUB15", stub15 116 | 117 | rf_vwfClearPuts = rf_vwfClearPuts_cb 118 | -------------------------------------------------------------------------------- /nes/src/ppuclear.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; NES PPU common functions 3 | ; Copyright 2011-2016 Damian Yerrick 4 | ; 5 | ; Copying and distribution of this file, with or without 6 | ; modification, are permitted in any medium without royalty provided 7 | ; the copyright notice and this notice are preserved in all source 8 | ; code copies. This file is offered as-is, without any warranty. 9 | ; 10 | .include "nes.inc" 11 | .export ppu_clear_nt, ppu_clear_oam, ppu_wait_vblank 12 | .export ppu_oam_dma_screen_on_xy0, ppu_screen_on_xy0, ppu_screen_on 13 | .import OAM 14 | .importzp nmis 15 | 16 | .segment "LIBCODE" 17 | ;; 18 | ; Clears a nametable to a given tile number and attribute value. 19 | ; (Turn off rendering in PPUMASK and set the VRAM address increment 20 | ; to 1 in PPUCTRL first.) 21 | ; @param A tile number 22 | ; @param X base address of nametable ($20, $24, $28, or $2C) 23 | ; @param Y attribute value ($00, $55, $AA, or $FF) 24 | ; @return X=0, AY unchanged 25 | .proc ppu_clear_nt 26 | 27 | ; Set base PPU address to XX00 28 | stx PPUADDR 29 | ldx #$00 30 | stx PPUADDR 31 | 32 | ; Clear the 960 spaces of the main part of the nametable, 33 | ; using a 4 times unrolled loop 34 | ldx #960/4 35 | loop1: 36 | .repeat 4 37 | sta PPUDATA 38 | .endrepeat 39 | dex 40 | bne loop1 41 | 42 | ; Clear the 64 entries of the attribute table 43 | ldx #64 44 | loop2: 45 | sty PPUDATA 46 | dex 47 | bne loop2 48 | rts 49 | .endproc 50 | 51 | ;; 52 | ; Moves all sprites starting at address X (e.g, $04, $08, ..., $FC) 53 | ; below the visible area. 54 | ; X is 0 at the end. 55 | .proc ppu_clear_oam 56 | 57 | ; First round the address down to a multiple of 4 so that it won't 58 | ; freeze should the address get corrupted. 59 | txa 60 | and #%11111100 61 | tax 62 | lda #$FF ; Any Y value from $EF through $FF will work 63 | loop: 64 | sta OAM,x 65 | inx 66 | inx 67 | inx 68 | inx 69 | bne loop 70 | rts 71 | .endproc 72 | 73 | ;; 74 | ; Runs OAM DMA, sets the scroll to (0, 0), and turns on rendering. 75 | ; @param A value for PPUCTRL 76 | .proc ppu_oam_dma_screen_on_xy0 77 | ldx #0 78 | stx OAMADDR 79 | ldx #>OAM 80 | stx OAM_DMA 81 | sec 82 | .endproc 83 | .proc ppu_screen_on_xy0 84 | ldx #0 85 | ldy #0 86 | .endproc 87 | 88 | ;; 89 | ; Sets the scroll position and turns PPU rendering on. 90 | ; @param A value for PPUCTRL ($2000) including scroll position 91 | ; MSBs; see nes.h 92 | ; @param X horizontal scroll position (0-255) 93 | ; @param Y vertical scroll position (0-239) 94 | ; @param C if true, sprites will be visible 95 | .proc ppu_screen_on 96 | stx PPUSCROLL 97 | sty PPUSCROLL 98 | sta PPUCTRL 99 | lda #BG_ON 100 | bcc :+ 101 | lda #BG_ON|OBJ_ON 102 | : 103 | sta PPUMASK 104 | rts 105 | .endproc 106 | 107 | .proc ppu_wait_vblank 108 | lda nmis 109 | : 110 | cmp nmis 111 | beq :- 112 | rts 113 | .endproc 114 | -------------------------------------------------------------------------------- /common/docs/release_checklist.md: -------------------------------------------------------------------------------- 1 | Checklist before release 2 | ======================== 3 | 4 | Day 1 5 | ----- 6 | First test all six NES builds on NES, the GB build on all GB models, 7 | and the GBA build on a GBA. Start with Credits and touch every 8 | test at least once. Make screenshots of new features and alt text 9 | for those screenshots. Sleep on it. 10 | 11 | Day 2 12 | ----- 13 | Open relevant files and forums: 14 | 15 | mousepad nes/CHANGES.txt gameboy/CHANGES.txt gba/CHANGES.txt \ 16 | makefile nes/makefile gameboy/makefile nes/src/helppages.txt \ 17 | gameboy/src/helppages.txt gba/src/helppages.txt \ 18 | common/docs/junkerhq/index.html 19 | xdg-open private/ 20 | firefox 'common/docs/junkerhq/index.html' \ 21 | 'https://github.com/pinobatch/240p-test-mini/releases' \ 22 | 'https://forums.nesdev.org/viewtopic.php?f=22&t=13394' \ 23 | 'https://gbdev.gg8.se/forums/viewtopic.php?id=542' \ 24 | 'https://forum.gbadev.net/topic/22-160p-test-suite' \ 25 | 'https://www.patreon.com/' 'https://peoplemaking.games/' \ 26 | 'https://www.retroveteran.com/masthead/' & 27 | git log --oneline 28 | 29 | 1. `CHANGES.txt` (3): Ensure version and release date are updated, 30 | and all common changes have the same wording 31 | 2. Make a common change highlights list for the GitHub release, 32 | based on this version's `CHANGES.txt` files, and split into 33 | user-visible and behind-the-scenes changes 34 | 3. `makefile` (3): Ensure version is updated 35 | 4. `helppages.txt` (3): Update release year and patron list 36 | 5. `make all` to ensure all versions are built, including the 37 | sometimes neglected alternate mapper NES versions 38 | 6. Test zipfile for completeness 39 | 40 | rm -r build 41 | make dist && mkdir build && cd build 42 | unzip ../240p-test-mini-0.xx.zip 43 | make -j2 all 44 | 45 | 7. `git status` and correct anything out of the ordinary 46 | 8. `git add -u && git commit` 47 | 9. `git tag v0.xx && git push && git push --tags` 48 | 10. `make clean && make -j2 dist` to put the new tag in credits 49 | 11. On GitHub, draft a new release titled 50 | `240p Test Suite (NES, GB, GBA) v0.xx` 51 | and attach six .nes files, one .nsf, one .gb, and one .gba, 52 | along with the screenshots 53 | 12. In `junkerhq/index.html`, update file sizes and release year. 54 | Upload to junkerhq per private steps, then verify at 55 | 56 | 13. Post highlights and link to GitHub release page to NESdev topic 57 | and Patreon, attaching screenshots and ROM and source zipfile 58 | 14. Post platform-relevant highlights, link to GitHub release, and 59 | link to NESdev attachment on gbdev.gg8.se and forum.gbadev.net 60 | 15. Mail highlights and link to GitHub release page to the 61 | address shown at 62 | 16. Post highlights, screenshots, and link to GitHub release to 63 | Mastodon 64 | -------------------------------------------------------------------------------- /gameboy/tools/attic/vwfrectexperiment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Experimental compression for VWF tile set, which is nominally 4 | 936 bytes, or 9 bytes for each of 104 tiles. On NES we store it 5 | uncompressed in the ROM. On GB we have more RAM so we can pack it 6 | tighter in ROM and decompress it on startup. 7 | 8 | Normally each tile's pen advance is 8 bits, of which only values 1-8 9 | are ever used. No tile fills all 64 bits of its 8x8 rectangle 10 | either. So by storing the pen advance, nonblank width, nonblank 11 | top, and nonblank bottom 12 | 13 | Pen advance, nonblank width, and nonblank top and bottom can be 14 | encoded in 3 bits each for 12 bits total, and then each glyph is 15 | w*h bits. 16 | 17 | Result: 18 | 104 tiles from 936 to 459 bytes saving 477 19 | 20 | But this doesn't include the unpacker. 21 | """ 22 | import sys, os, argparse 23 | from PIL import Image 24 | from operator import or_ as bitwise_or 25 | from functools import reduce 26 | 27 | def hexdump(b, w=32): 28 | print("\n".join(b[i:i + w].hex() for i in range(0, len(b), w))) 29 | 30 | def pixelstom7tiles(im, size=None): 31 | if isinstance(im, bytes): 32 | b = im 33 | else: 34 | b, size = im.tobytes(), im.size 35 | w, h = size 36 | out = [] 37 | for rowidx in range(0, len(b), w * 8): 38 | tilerow = b[rowidx:rowidx + w * 8] 39 | for x in range(0, w, 8): 40 | tile = b''.join(tilerow[i:i + 8] for i in range(x, w * 8, w)) 41 | out.append(tile) 42 | return out 43 | 44 | def m7tileto1btile(tile): 45 | out = bytearray() 46 | for y in range(0, len(tile), 8): 47 | row = tile[y:y + 8] 48 | byte = 0 49 | for c in row: 50 | byte = (byte << 1) | (c & 1) 51 | out.append(byte) 52 | return bytes(out) 53 | 54 | def ctz(b): 55 | """Count trailing zero bits in a binary number.""" 56 | 57 | # bit_length() 58 | 59 | return (b ^ (b - 1)).bit_length() - 1 60 | 61 | def main(argv=None): 62 | argv = argv or sys.argv 63 | im = Image.open("../../common/tilesets/vwf7_cp144p.png") 64 | imbytes = bytes(1 if c == 1 else 0 for c in im.tobytes()) 65 | tiles = [m7tileto1btile(x) for x in pixelstom7tiles(imbytes, im.size)] 66 | bitcount = 0 67 | for tile1b in tiles: 68 | nonempty_rows = [i for i, row in enumerate(tile1b) if row] 69 | if not nonempty_rows: 70 | bitcount += 12 71 | continue 72 | top, bottom = nonempty_rows[0], nonempty_rows[-1] 73 | occupiedcols = reduce(bitwise_or, tile1b, 0) 74 | tilew, tileh = 8 - ctz(occupiedcols), bottom + 1 - top 75 | assert tilew != 0 76 | bitlength = tilew * tileh 77 | bitcount += 12 + bitlength 78 | 79 | oldbytecount, newbytecount = len(tiles) * 9, -(-bitcount // 8) 80 | print("%d tiles from %d to %d bytes saving %d" 81 | % (len(tiles), oldbytecount, newbytecount, 82 | oldbytecount - newbytecount)) 83 | 84 | if __name__=='__main__': 85 | main() 86 | -------------------------------------------------------------------------------- /gba/src/backlight.c: -------------------------------------------------------------------------------- 1 | /* 2 | Backlight zone test for 160p Test Suite 3 | Copyright 2018 Damian Yerrick 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | 19 | */ 20 | #include "global.h" 21 | #include 22 | 23 | void activity_backlight_zone(void) { 24 | unsigned inverted = 0, hidden = 0, high_gear = 0, held_keys = 0, sz = 1; 25 | unsigned int x = 119, y = 79; 26 | 27 | load_common_obj_tiles(); 28 | while (1) { 29 | read_pad_help_check(helpsect_backlight_zones); 30 | held_keys |= new_keys; 31 | if (new_keys & KEY_SELECT) { 32 | inverted = !inverted; 33 | } 34 | if (new_keys & KEY_B) { 35 | return; 36 | } 37 | if (cur_keys & KEY_A) { 38 | if (new_keys & KEY_UP) { 39 | sz = (sz + 1) & 0x03; 40 | held_keys &= ~KEY_A; 41 | } 42 | if (new_keys & KEY_DOWN) { 43 | sz = (sz - 1) & 0x03; 44 | held_keys &= ~KEY_A; 45 | } 46 | if (new_keys & (KEY_LEFT | KEY_RIGHT)) { 47 | high_gear = !high_gear; 48 | held_keys &= ~KEY_A; 49 | } 50 | } else { 51 | if (held_keys & KEY_A) { 52 | held_keys &= ~KEY_A; 53 | hidden = !hidden; 54 | } 55 | unsigned move_speed = high_gear ? (1 << sz) : 1; 56 | if (cur_keys & KEY_UP) { 57 | y = y >= move_speed ? y - move_speed : 0; 58 | } 59 | if (cur_keys & KEY_LEFT) { 60 | x = x >= move_speed ? x - move_speed : 0; 61 | } 62 | if (cur_keys & KEY_DOWN) { 63 | y += move_speed; 64 | } 65 | if (cur_keys & KEY_RIGHT) { 66 | x += move_speed; 67 | } 68 | } 69 | 70 | unsigned ymax = 160 - (1 << sz); 71 | if (y > ymax) y = ymax; 72 | unsigned xmax = 240 - (1 << sz); 73 | if (x > xmax) x = xmax; 74 | 75 | // Draw the sprite 76 | oam_used = 0; 77 | if (!hidden) { 78 | unsigned int i = oam_used; 79 | SOAM[i].attr0 = ATTR0_Y(y) | ATTR0_4BPP | ATTR0_SQUARE; 80 | SOAM[i].attr1 = ATTR1_X(x) | ATTR1_SIZE_8; 81 | SOAM[i].attr2 = (sz < 3) ? sz + 0x22 : 1; 82 | oam_used = i + 1; 83 | } 84 | ppu_clear_oam(oam_used); 85 | 86 | VBlankIntrWait(); 87 | pal_bg_mem[0] = inverted ? RGB5(31, 31, 31) : RGB5(0, 0, 0); 88 | pal_obj_mem[1] = inverted ? RGB5(0, 0, 0): RGB5(31, 31, 31); 89 | ppu_copy_oam(); 90 | REG_DISPCNT = DCNT_MODE0 | DCNT_OBJ; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /gba/src/audiosync.c: -------------------------------------------------------------------------------- 1 | /* 2 | Audio sync test for 160p Test Suite 3 | Copyright 2018 Damian Yerrick 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | 19 | */ 20 | #include "global.h" 21 | #include 22 | 23 | #define PFMAP 23 24 | 25 | static const BarsListEntry audiosync_rects[] = { 26 | { 0,128,240,136, 1}, 27 | { 0, 24, 24, 40, 2}, 28 | { 24, 24, 48, 40, 3}, 29 | { 48, 24, 72, 40, 4}, 30 | { 72, 24, 96, 40, 5}, 31 | {144, 24,168, 40, 5}, 32 | {168, 24,192, 40, 4}, 33 | {192, 24,216, 40, 3}, 34 | {216, 24,240, 40, 2}, 35 | {0xFF} 36 | }; 37 | 38 | static const unsigned char min_progress[6] = { 39 | 120, 0, 40, 60, 80, 100 40 | }; 41 | 42 | void activity_audio_sync() { 43 | unsigned int progress = 0, running = 0; 44 | 45 | REG_SOUNDCNT_X = 0x0080; // 00: reset; 80: run 46 | REG_SOUNDBIAS = 0xC200; // C200: 262 kHz PWM (for PSG) 47 | REG_SOUNDCNT_H = 0x0002; // PSG/PCM mixing 48 | REG_SOUNDCNT_L = 0xFF77; // PSG vol/pan 49 | REG_SOUND1CNT_L = 0x08; // no sweep 50 | 51 | draw_barslist(audiosync_rects); 52 | load_common_obj_tiles(); 53 | 54 | do { 55 | if (progress < 120) { 56 | REG_SOUND1CNT_H = 0; // note cut 57 | REG_SOUND1CNT_X = 0x8000; 58 | read_pad_help_check(helpsect_audio_sync); 59 | 60 | if (new_keys & KEY_A) { 61 | running = !running; 62 | if (running) progress = 0; 63 | } 64 | } 65 | if (running) { 66 | progress += 1; 67 | if (progress >= 122) progress = 0; 68 | } 69 | 70 | unsigned int y = (progress < 60) ? 128 - progress : 8 + progress; 71 | SOAM[0].attr0 = ATTR0_Y(y) | ATTR0_4BPP | ATTR0_SQUARE; 72 | SOAM[0].attr1 = 119 | ATTR1_SIZE_8; 73 | SOAM[0].attr2 = 0x0023; 74 | ppu_clear_oam(1); 75 | 76 | VBlankIntrWait(); 77 | REG_DISPCNT = DCNT_MODE0 | DCNT_BG0 | DCNT_OBJ_1D | DCNT_OBJ; 78 | REG_BGCNT[0] = BG_4BPP|BG_WID_32|BG_HT_32|BG_CBB(0)|BG_SBB(PFMAP); 79 | REG_BG_OFS[0].x = REG_BG_OFS[0].y = 0; 80 | for (unsigned int i = 0; i < 6; ++i) { 81 | pal_bg_mem[i] = progress >= min_progress[i] ? RGB5(31, 31, 31) : RGB5(0, 0, 0); 82 | } 83 | pal_obj_mem[1] = RGB5(31, 31, 31); 84 | ppu_copy_oam(); 85 | 86 | if (progress == 120) { 87 | REG_SOUND1CNT_H = 0xA080; // 2/3 volume, 50% duty 88 | REG_SOUND1CNT_X = (2048 - 131) + 0x8000; // pitch 89 | } 90 | } while (!(new_keys & KEY_B)); 91 | REG_SOUNDCNT_X = 0; // reset audio 92 | } 93 | -------------------------------------------------------------------------------- /gameboy/src/undte.z80: -------------------------------------------------------------------------------- 1 | ; 2 | ; Digram tree encoding (DTE) text decompression for Game Boy 3 | ; Copyright 2018 Damian Yerrick 4 | ; 5 | ; This software is provided 'as-is', without any express or implied 6 | ; warranty. In no event will the authors be held liable for any damages 7 | ; arising from the use of this software. 8 | ; 9 | ; Permission is granted to anyone to use this software for any purpose, 10 | ; including commercial applications, and to alter it and redistribute it 11 | ; freely, subject to the following restrictions: 12 | ; 13 | ; 1. The origin of this software must not be misrepresented; you must not 14 | ; claim that you wrote the original software. If you use this software 15 | ; in a product, an acknowledgment in the product documentation would be 16 | ; appreciated but is not required. 17 | ; 2. Altered source versions must be plainly marked as such, and must not be 18 | ; misrepresented as being the original software. 19 | ; 3. This notice may not be removed or altered from any source distribution. 20 | ; 21 | include "src/global.inc" 22 | 23 | def DTE_STACK_SIZE equ 8 24 | def DTE_MIN_CODEUNIT equ 128 25 | def FIRST_PRINTABLE_CU equ 24 26 | 27 | section "dtestack",WRAM0,align[3] 28 | dtestack: ds DTE_STACK_SIZE 29 | 30 | section "undte",ROM0 31 | 32 | ;; 33 | ; Decompresses the title of help page A to the line buffer. 34 | undte_helptitle_a:: 35 | ld de,helptitles 36 | call de_index_a 37 | fallthrough undte_to_help_line_buffer 38 | 39 | ;; Decompresses DTE text at HL to the line buffer. 40 | undte_to_help_line_buffer:: 41 | ld de,help_line_buffer 42 | fallthrough undte_line 43 | 44 | ;; 45 | ; Decompresses digram tree encoded (DTE) text from HL to DE. 46 | ; Stops at the first code unit less than FIRST_PRINTABLE_CU. 47 | ; @param HL source address 48 | ; @param DE destination address 49 | ; @return HL after a low code unit in src; 50 | ; DE at a low code unit in dst; 51 | ; A = ending low code unit 52 | undte_line:: 53 | ld bc,dtestack 54 | 55 | .charloop: 56 | ; If there's a code on the stack, print it 57 | ld a,c 58 | xor low(dtestack) 59 | jr z,.stack_empty 60 | dec bc 61 | ld a,[bc] 62 | db $fe ; short for jr .print_code_a 63 | .stack_empty: 64 | ; Otherwise, retrieve a code from the compressed text 65 | ld a,[hl+] 66 | .print_code_a: 67 | ; If not a pair, save it to the string 68 | cp DTE_MIN_CODEUNIT 69 | jr nc,.code_is_pair 70 | ld [de],a 71 | ; If not a control, advance to the next code 72 | cp FIRST_PRINTABLE_CU 73 | ret c 74 | inc de 75 | jr .charloop 76 | 77 | .code_is_pair: 78 | push hl 79 | ; For a pair, we want to push the second code to dtestack 80 | ; and then decode the first. The address of the second is 81 | ; dte_replacements + 2*(code - DTE_MIN_CODEUNIT) + 1 82 | sub DTE_MIN_CODEUNIT 83 | ld hl,dte_replacements + 1 84 | add a ; A = 2*(code - DTE_MIN_CODEUNIT) 85 | add l 86 | ld l,a 87 | adc h 88 | sub l 89 | ld h, a 90 | 91 | ; Get the second code and stack it 92 | ld a,[hl-] 93 | ld [bc],a 94 | inc bc 95 | 96 | ; Now get the first code and print it 97 | ld a,[hl] 98 | pop hl 99 | jr .print_code_a 100 | -------------------------------------------------------------------------------- /nes/src/hanover.s: -------------------------------------------------------------------------------- 1 | .include "nes.inc" 2 | .include "global.inc" 3 | .include "rectfill.inc" 4 | .importzp RF_hanover, helpsect_hanover_bars 5 | 6 | hanover_bgcolor = test_state+0 7 | 8 | .code 9 | .proc do_hanover_bars 10 | lda #0 11 | sta hanover_bgcolor 12 | restart: 13 | lda #$80 14 | sta help_reload 15 | sta PPUCTRL 16 | asl a 17 | sta PPUMASK 18 | lda #RF_hanover 19 | jsr rf_load_layout 20 | lda #$3F 21 | sta PPUADDR 22 | ldx #$01 23 | stx PPUADDR 24 | palloop: 25 | lda hanover_palette-1,x 26 | sta PPUDATA 27 | inx 28 | cpx #hanover_palette_end-hanover_palette+1 29 | bcc palloop 30 | ldx #0 31 | jsr ppu_clear_oam 32 | 33 | ; fill tiles 34 | ldx #0 35 | stx PPUADDR 36 | stx PPUADDR 37 | jsr hanover_gentiles 38 | ldx #0 39 | stx PPUDATA 40 | inx 41 | jsr hanover_gentiles 42 | 43 | ; generate sprite 44 | ldx #0 45 | objloop: 46 | txa 47 | and #%01110000 ; keep row 48 | lsr a 49 | cmp #24 ; move bottom sprites down 1 line 50 | adc #79 51 | sta OAM,x 52 | inx 53 | lda #1 54 | sta OAM,x 55 | inx 56 | lsr a 57 | sta OAM,x 58 | inx 59 | txa 60 | and #%00001100 ; keep column 61 | asl a 62 | adc #48 63 | sta OAM,x 64 | inx 65 | cpx #$60 66 | bcc objloop 67 | 68 | loop: 69 | lda #helpsect_hanover_bars 70 | jsr read_pads_helpcheck 71 | bcs restart 72 | 73 | ldy hanover_bgcolor 74 | lda new_keys 75 | lsr a 76 | bcc not_right 77 | iny 78 | not_right: 79 | lsr a 80 | bcc not_left 81 | dey 82 | not_left: 83 | cpy #13 84 | bcc have_new_bgcolor 85 | bmi wrap_below_0 86 | ldy #0 87 | beq have_new_bgcolor 88 | wrap_below_0: 89 | ldy #13-1 90 | have_new_bgcolor: 91 | sty hanover_bgcolor 92 | 93 | jsr ppu_wait_vblank 94 | 95 | ; update palette 96 | lda #$3F 97 | sta PPUADDR 98 | lda #$00 99 | sta PPUADDR 100 | lda hanover_bgcolor 101 | beq :+ 102 | ora #$10 103 | : 104 | sta PPUDATA 105 | ; update nametable 106 | lda #$20 107 | sta PPUADDR 108 | lda #$CD 109 | sta PPUADDR 110 | lda hanover_bgcolor 111 | beq :+ 112 | lda #2 113 | : 114 | ora #$20 115 | sta PPUDATA 116 | lda hanover_bgcolor 117 | cmp #1 118 | adc #$21 119 | sta PPUDATA 120 | 121 | lda #VBLANK_NMI|BG_0000|OBJ_0000 122 | jsr ppu_oam_dma_screen_on_xy0 123 | bit new_keys+0 124 | bvc loop 125 | rts 126 | .endproc 127 | 128 | .proc hanover_gentiles 129 | xorvalue = $01 130 | txa 131 | and #$01 132 | jsr genplane 133 | txa 134 | and #$02 135 | jsr genplane 136 | inx 137 | cpx #4 138 | bcc hanover_gentiles 139 | rts 140 | genplane: 141 | beq :+ 142 | lda #$FF 143 | : 144 | sta xorvalue 145 | ldy #8 146 | byteloop: 147 | sta PPUDATA 148 | eor xorvalue 149 | dey 150 | bne byteloop 151 | rts 152 | .endproc 153 | 154 | .rodata 155 | ; palette 1: 144,80-240,128 156 | ; palette 2: 48,144-144,192 157 | ; palette 3: 144,144-240-192 158 | 159 | hanover_palette: 160 | .byte $20,$12,$13, $00,$14,$15,$16, $00,$17,$18,$19, $00,$1a,$1b,$1c 161 | .byte $00,$11 162 | hanover_palette_end: 163 | -------------------------------------------------------------------------------- /fc-mdf/docs/sequence.md: -------------------------------------------------------------------------------- 1 | Test sequences 2 | ============== 3 | 4 | Mapper detection 5 | ---------------- 6 | All licensed games on MMC5, VRC6, Namco 163, and Sunsoft 5B use PRG 7 | ROM and CHR ROM. The only licensed game on VRC7 uses PRG ROM and CHR 8 | RAM. All licensed disk games are on a disk. This means this project 9 | will need to ship at least three artifacts: a ROM with PRG ROM and 10 | CHR ROM, a ROM with PRG ROM only, and a disk image. 11 | 12 | As with [Holy Mapperel], I'll distinguish the four mappers used in 13 | Famicom cassettes with CHR ROM and expansion audio through their 14 | nametable mirroring behavior. Then after selecting a background, 15 | a 2-minute test pattern will begin. 16 | 17 | [Holy Mapperel]: https://github.com/pinobatch/holy-mapperel 18 | 19 | VRC6 20 | ---- 21 | Implementation pending 22 | 23 | 1. Sync pulses (20 frames silence, 10 loops of 1 frame 8 kHz V07 24 | pulse and 1 frame silence, 20 frames silence) 25 | 2. 36-note pentatonic scales (C, D#, F, G, A#), beginning at lowest 26 | C (32.7 Hz) and ascending to C 7 octaves up (4186 Hz) at 10 frames 27 | per note, followed by 20 frames silence. Repeat for pulse V00 28 | through V07 and saw at volumes 30 and 62. 29 | 3. 60 frames silence 30 | 4. G (392 Hz) for 20 frames with channel off/on every frame, then 31 | 10 frame silence. Demonstrates how to reset phase 32 | 5. G then pitch bending up 2 period units per frame for 20 frames, 33 | then 10 frame silence. Demonstrates that period high write 34 | doesn't itself reset phase 35 | 6. V00 pulse slide from A (440 Hz) up at 1 period unit per frame 36 | for 270 frames, then 10 frames silence. Tests aliasing and 37 | ultrasound response 38 | 7. Likewise with volume 62 saw from B (494 Hz). 39 | 8. Fades at 5 frames per unit followed by 15 frames silence using 40 | square waves. VRC6 pulse 1 plays 1 kHz fading out five times, 41 | while VRC6 pulse 2 plays nothing, 500 Hz fading out, 1 kHz fading 42 | out, 500 Hz fading in, and 1 kHz fading in. Repeat with 2A03 43 | pulse 1 instead of VRC6 pulse 1. 44 | 9. Fades at 5 frames per unit followed by 15 frames silence using 45 | 1/8 pulse. VRC6 out, 2A03 out, 2A03 out + VRC6 out, 46 | 2A03 out + VRC6 in 47 | 10. Saw fade from 63 to 1 at 2 frames per step (total 126 frames), 48 | 24 frames silence, from 8 to 0 at 10 frames per step, 10 frames 49 | silence. Tests transition at wraparound and LSB loss. 50 | 11. For $4011 in $00, $0F, $1F, $2F, $3F, $4F, $5F, $6F, $7F, $0F: 51 | 10 frames silence, 20 frames 1 kHz pulse, 20 frames 1 kHz saw, 52 | 10 frames silence 53 | 12. Repeat sync pulses 54 | 55 | VRC7 56 | ---- 57 | Pending 58 | 59 | YM2149 60 | ------ 61 | Pending. Will need to touch square, noise, envelope, and 62 | combinations thereof. Be aware that as with DCSG (mdfourier-sms), 63 | YM2149 square has no wave position reset. 64 | 65 | MMC5 66 | ---- 67 | Design pending. Intended to resemble VRC6. Some sort of PCM 68 | response is needed. 69 | 70 | Namco 129/163 71 | ------------- 72 | Pending. If there's a PC Engine tone generator, I might take 73 | inspiration from that. 74 | 75 | Disk System 76 | ----------- 77 | Pending. Will need a whole different link script, as well as a way 78 | to obtain a lawfully made copy of the BIOS with which to test. 79 | -------------------------------------------------------------------------------- /gba/CHANGES.txt: -------------------------------------------------------------------------------- 1 | 0.24 (future) 2 | * Replace Grid and Linearity with Monoscope (thanks Lorenzoone) 3 | * Sound test: add surround option 4 | * Hill zone: scroll the top scanline too (thanks Lorenzoone) 5 | * Shadow sprite: fix sprite when changing background 6 | (thanks Lorenzoone) 7 | * Port from libgba to libtonc 8 | * Reflect rebranding of Twitter to X 9 | 10 | 0.23 (2023-03-03) 11 | * Help: fix blink sprite alignment 12 | * Credits: Display version from Git commit tag and ID 13 | * Credits: Pino has moved to the Fediverse 14 | * Motion blur: add health warning 15 | * Motion blur: increase duration cap to 60 frames 16 | (requested by zeta0134) 17 | * Motion blur: do not edit parameters while running 18 | * Shadow sprite: add Donna 19 | * Rename Manual lag test to Timing and reflex test to reflect 20 | how it is used 21 | * Hill zone scroll test: Switch from laggy libgba IRQs to HDMA 22 | to reduce split artifacts 23 | * Backlight zones: add speed control 24 | * Build in GitHub Actions (thanks Persune) 25 | 26 | 0.22 (2020-11-04) 27 | * Color bars: A to toggle NTSC 7.5% setup 28 | * Color bleed: Rearrange to 10 rectangles 29 | * Add a back story for Gus 30 | * Document deliberately replaced assets 31 | 32 | 0.21 (2020-06-01) 33 | * Help: Make some descriptions more concise (with Artemio's help) 34 | * Compile with -fno-common for GCC 10 compatibility 35 | 36 | 0.20 (2020-01-30) 37 | * Add Convergence pattern 38 | * Vertical scroll test: Display the correct help page 39 | * Help: Blink Gus's eyes 40 | 41 | 0.19 (2019-09-12) 42 | * Stopwatch: Bolder digits 43 | * Color bleed: Fix frame # covering everything 44 | * PLUGE Contrast, Vertical scroll: Center pattern horizontally 45 | * Help: Standardize phrasing: "stuck pixels", "hide or show", 46 | "start or stop" 47 | * Help: List patrons as of release time 48 | * README: Explain rationale behind "160p" title 49 | * Use a more common makefile 50 | * Specify each PNG's conversion settings in a grit file 51 | 52 | 0.18 (2019-05-02) 53 | * PLUGE: Add PLUGE Contrast sub-test with shark graphic 54 | (Genesis 1.16 parity) 55 | * Want your name in the credits? patreon.com/pineight 56 | 57 | 0.17 (2018-06-30) 58 | * Solid screen: Explain what a bad high voltage regulator does to 59 | GB Player border (requested by ISSOtm) 60 | * Sharpness: A to show brick wall pattern (Genesis 1.16 parity) 61 | * Stopwatch: Draw even frame numbers in blue and odd in red 62 | (Genesis 1.16 parity) 63 | * Stopwatch: Add third ruler setting to show in even frames 64 | (Genesis 1.16 parity) 65 | * Help: Align line buffer to prevent corruption of nearby variables 66 | by DMA memset, fixing Down after Scroll test 67 | * Add license headers 68 | * Skip versions 0.03 through 0.16 69 | 70 | 0.02 (2018-06-21) 71 | * Add Shadow sprite, Stopwatch, Hill zone scroll, and Vertical scroll 72 | * Sound test: Add a real time synthesized chord played through PCM 73 | * Audio sync: Move ceiling up to match position in 240p Test Suite 74 | (for Sega Genesis) version 1.16 75 | * Unpacking 2-bit tile data no longer unpacks twice as much data as 76 | required, which exposes a bug at the end of a BitUnPack in mGBA's 77 | HLE BIOS 78 | 79 | 0.01 (2018-06-19) 80 | * initial release, with all tests except Shadow sprite, Stopwatch, 81 | Hill zone scroll, Vertical scroll, and PCM in audio test 82 | -------------------------------------------------------------------------------- /common/tools/parsepages.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Converting lines to documents 4 | """ 5 | import re 6 | 7 | nonalnumRE = re.compile("[^0-9a-zA-Z]+") 8 | 9 | def lines_to_docs(filename, lines, wrap=None, maxpagelen=16): 10 | """Convert a list of lines to a list of documents. 11 | 12 | filename -- source of list of lines for use in error messages 13 | lines -- a list of lines of the following form 14 | "== section title ==": start of document 15 | "----": page separator 16 | Other: text on page. Leading, trailing, and consecutive 17 | blank lines will be removed. 18 | wrap -- a function performing word wrap on long lines (optional) 19 | maxpagelen -- raise an exception if text lines per page exceeds this 20 | """ 21 | # docs[docnum] = (title, labelname, docpages) 22 | # docpages[pagenum][linenum] = line text 23 | docs, secttitles = [], {} 24 | for linenum, line in enumerate(lines): 25 | line = line.rstrip() 26 | if line.startswith('==') and line.endswith('=='): 27 | # New section 28 | secttitle = line.strip('=').strip() 29 | normtitle = nonalnumRE.sub('_', secttitle.lower()).strip('_') 30 | try: 31 | oldsection = secttitles[normtitle] 32 | except KeyError: 33 | pass 34 | else: 35 | oldsecttitle, oldlinenum = oldsection 36 | raise ValueError("%s:%d: %s was already defined on line %d" 37 | % (filename, linenum + 1, 38 | oldsecttitle, oldlinenum)) 39 | secttitles[normtitle] = (secttitle, linenum) 40 | docs.append((secttitle, normtitle, [[]])) 41 | continue 42 | docpages = docs[-1][-1] if docs else None 43 | doclastpage = docpages[-1] if docpages else None 44 | 45 | line_rstrip = line.rstrip() 46 | if line_rstrip == '': 47 | # Blank line; append only if following a nonblank line 48 | if doclastpage and doclastpage[-1]: 49 | doclastpage.append('') 50 | continue 51 | if doclastpage is None: 52 | raise IndexError("%s:%d: nonblank line with no open document" 53 | % (filename, linenum + 1)) 54 | if line.startswith('----') and line.rstrip('-') == '': 55 | # Page break 56 | if doclastpage: 57 | docpages.append([]) 58 | continue 59 | 60 | # Ordinary text 61 | doclastpage.extend(wrap(line_rstrip) if wrap else [line_rstrip]) 62 | if len(doclastpage) > maxpagelen: 63 | raise IndexError("%s:%d: exceeds page length of %d lines" 64 | % (filename, linenum + 1, maxpagelen)) 65 | 66 | for doc in docs: 67 | pages = doc[-1] 68 | 69 | # Remove trailing blank lines 70 | for page in pages: 71 | while page and not page[-1]: del page[-1] 72 | 73 | # Remove blank pages 74 | for i in range(len(pages) - 1, -1, -1): 75 | if not pages[i]: del pages[i] 76 | 77 | # Remove blank docs entirely 78 | for i in range(len(docs) - 1, -1, -1): 79 | if not docs[i][-1]: 80 | del docs[i] 81 | 82 | return docs 83 | -------------------------------------------------------------------------------- /gameboy/tools/romusage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Visualizer of Game Boy 2bpp files (and free space in ROM images) 4 | Copyright 2019, 2022 Damian Yerrick 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | """ 22 | 23 | import sys, argparse 24 | from PIL import Image 25 | 26 | def hexdump(b, w=32): 27 | print("\n".join(b[i:i + w].hex() for i in range(0, len(b), w))) 28 | 29 | def render_to_texels(data, twidth=16): 30 | rowbytes = 16 * twidth 31 | numrows = -(-len(data) // rowbytes) 32 | texels = bytearray() 33 | for i in range(numrows): 34 | rowdata = data[i * rowbytes:i * rowbytes + rowbytes] 35 | if len(rowdata) < rowbytes: 36 | rowdata = rowdata + bytes(rowbytes - len(rowdata)) 37 | plane0 = b''.join(rowdata[i::16] for i in range(0, 16, 2)) 38 | plane1 = b''.join(rowdata[i::16] for i in range(1, 17, 2)) 39 | for p0, p1 in zip(plane0, plane1): 40 | texels.extend( 41 | ((p0 >> x) & 1) | (((p1 >> x) & 1) << 1) 42 | for x in range(7, -1, -1) 43 | ) 44 | im = Image.new('P', (8 * twidth, 8 * numrows)) 45 | im.putdata(texels) 46 | im.putpalette(b'\xC0\xFF\x5F\x80\xBF\x5F\x40\x7F\x5F\x00\x3F\x5F') 47 | return im 48 | 49 | helpText="Visualize space usage in a file by treating it as Game Boy graphics" 50 | 51 | def parse_argv(argv): 52 | p = argparse.ArgumentParser(description=helpText) 53 | p.add_argument("romname", help="name of a Game Boy ROM") 54 | p.add_argument("output", nargs='?', default=None, 55 | help="output file (if omitted, display on screen)") 56 | p.add_argument("-w", "--width", type=int, default=32, 57 | help="number of 16-byte chunks per line (default: 32)") 58 | return p.parse_args(argv[1:]) 59 | 60 | def main(argv=None): 61 | args = parse_argv(argv or sys.argv) 62 | argv = argv or sys.argv 63 | with open(args.romname, "rb") as infp: 64 | romdata = infp.read() 65 | twidth = args.width 66 | tiles = render_to_texels(romdata, twidth) 67 | if args.output: 68 | tiles.save(args.output) 69 | else: 70 | try: 71 | tiles.show() 72 | except Exception: 73 | # Pillow 9.4.0 is giving "conversion not supported" 74 | # if I try to show() an indexed (mode "P") image 75 | tiles.convert("RGB").show() 76 | 77 | if __name__=='__main__': 78 | if 'idlelib' in sys.modules: 79 | main(['./romusage.py', '../gb240p.gb']) 80 | else: 81 | main() 82 | -------------------------------------------------------------------------------- /nes/tools/widesb53.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from __future__ import with_statement, division, print_function 3 | import array, os, sys 4 | from binascii import a2b_hex 5 | from PIL import Image, ImageChops 6 | from pb53 import pb53 7 | 8 | import savtool # also finds common tools 9 | import chnutils, pilbmp2nes 10 | 11 | def bmptowidesb53(infilename, palette, outfilename): 12 | im = Image.open(infilename) 13 | if im.size[0] != 512: 14 | raise ValueError("Image width is %d pixels (expected 512)" 15 | % im.size[0]) 16 | if im.size[1] != 240: 17 | raise ValueError("Image height is %d pixels (expected 240)" 18 | % im.size[0]) 19 | 20 | # Quantize picture to palette 21 | palette = b''.join(palette[0:1] + palette[i + 1:i + 4] 22 | for i in range(0, 16, 4)) 23 | palettes = [[tuple(savtool.bisqwit2012[r * 3:r * 3 + 3]) 24 | for r in palette[i:i + 4]] 25 | for i in range(0, 16, 4)] 26 | imf, attrs = savtool.colorround(im, palettes) 27 | 28 | # Convert to unique tiles 29 | chrdata = pilbmp2nes.pilbmp2chr(imf, 8, 8) 30 | chrdata, linear_namdata = chnutils.dedupe_chr(chrdata) 31 | print("%d distinct tiles" % len(chrdata)) 32 | 33 | # Split into separate 32x32 nametables 34 | nametables = [[linear_namdata[i:i + 32] 35 | for i in range(x, len(linear_namdata), im.size[0] // 8)] 36 | for x in range(0, im.size[0] // 8, 32)] 37 | nametables = [bytes(b for row in nt for b in row) for nt in nametables] 38 | 39 | # Pack attributes into bytes 40 | if len(attrs) % 2: 41 | attrs.append([0] * len(attrs[0])) 42 | attrs = [[lc | (rc << 2) for lc, rc in zip(row[0::2], row[1::2])] 43 | for row in attrs] 44 | attrs = [[tc | (bc << 4) for (tc, bc) in zip(t, b)] 45 | for (t, b) in zip(attrs[0::2], attrs[1::2])] 46 | # Split into separate 32x32 nametables 47 | attrs = [bytes(b for row in attrs for b in row[i:i + 8]) 48 | for i in range(0, len(attrs[0]), 8)] 49 | print([len(x) for x in attrs]) 50 | 51 | outdata = [ 52 | bytearray([len(chrdata) & 0xFF]), 53 | pb53(b''.join(chrdata), copyprev=False)[0] 54 | ] 55 | outdata.extend(pb53(nt + at, copyprev=False)[0] 56 | for nt, at in zip(nametables, attrs)) 57 | outdata.append(palette) 58 | with open(outfilename, 'wb') as outfp: 59 | outfp.writelines(outdata) 60 | 61 | usagemsg = """usage: %s INFILE PALETTE OUTFILE 62 | 63 | INFILE -- path to indexed PNG file 64 | PALETTE -- NES palette string of 32 hex digits 65 | OUTFILE -- path to output c512 file 66 | """ 67 | 68 | def main(argv=None): 69 | argv = argv or sys.argv 70 | if len(argv) != 4: 71 | progname = os.path.basename(argv[0]) 72 | if len(argv) > 1 and argv[1] == '--help': 73 | print(usagemsg % progname) 74 | return 75 | print("%s: wrong number of arguments; try %s --help" 76 | % (progname, progname), file=sys.stderr) 77 | sys.exit(1) 78 | infilename = argv[1] 79 | palette = bytes.fromhex(argv[2]) 80 | outfilename = argv[3] 81 | bmptowidesb53(infilename, palette, outfilename) 82 | 83 | if __name__=='__main__': 84 | main() 85 | ## main(["widesb53.py", "../tilesets/greenhillzone.png", 86 | ## "2232201622081A292208181622161616", "../obj/nes/greenhillzone.sb53"]) 87 | -------------------------------------------------------------------------------- /gameboy/tools/vwfbuild.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Variable-width font builder for GB 4 | Copyright 2018, 2019, 2021 Damian Yerrick 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | """ 22 | assert str is not bytes 23 | import sys 24 | from PIL import Image 25 | from huffnib import huffnibenc 26 | 27 | WIDTHS_4BIT = True 28 | 29 | def rgbasm_bytearray(s): 30 | """Produce db statements for a constant array in rgbasm""" 31 | s = [' db ' + ','.join("%3d" % ch for ch in s[i:i + 16]) 32 | for i in range(0, len(s), 16)] 33 | return '\n'.join(s) 34 | 35 | def vwfcvt(filename, tileHt=8): 36 | im = Image.open(filename) 37 | pixels = im.load() 38 | (w, h) = im.size 39 | (xparentColor, sepColor) = im.getextrema() 40 | widths = bytearray() 41 | tiledata = bytearray() 42 | for yt in range(0, h, tileHt): 43 | for xt in range(0, w, 8): 44 | # step 1: find the glyph width 45 | tilew = 8 46 | for x in range(8): 47 | if pixels[x + xt, yt] == sepColor: 48 | tilew = x 49 | break 50 | # step 2: encode the pixels 51 | widths.append(tilew) 52 | for y in range(tileHt): 53 | rowdata = 0 54 | for x in range(8): 55 | pxhere = pixels[x + xt, y + yt] 56 | pxhere = 0 if pxhere in (xparentColor, sepColor) else 1 57 | rowdata = (rowdata << 1) | pxhere 58 | tiledata.append(rowdata) 59 | return (widths, tiledata) 60 | 61 | def main(argv=None): 62 | if argv is None: 63 | argv = sys.argv 64 | if len(argv) > 1 and argv[1] == '--help': 65 | print("usage: %s font.png font.s" % argv[0]) 66 | return 67 | if len(argv) != 3: 68 | print("wrong number of options; try %s --help" % argv[0], file=sys.stderr) 69 | sys.exit(1) 70 | 71 | (widths, tiledata) = vwfcvt(argv[1]) 72 | 73 | # Compress 74 | if WIDTHS_4BIT: 75 | widths = bytes( 76 | hi << 4 | lo for hi, lo in zip(widths[0::2], widths[1::2]) 77 | ) 78 | cwidths = huffnibenc(widths) 79 | ctiledata = huffnibenc(tiledata) 80 | 81 | out = ["; Generated by vwfbuild", 82 | 'section "vwfChrData", ROM0', 83 | 'vwfChrData_huf::', 84 | rgbasm_bytearray(ctiledata), 85 | "vwfChrWidths_huf::", 86 | rgbasm_bytearray(cwidths), 87 | ''] 88 | with open(argv[2], 'w') as outfp: 89 | outfp.write('\n'.join(out)) 90 | 91 | if __name__ == '__main__': 92 | if 'idlelib' in sys.modules: 93 | main(['vwfbuild', '../../common/tilesets/vwf7_cp144p.png', 94 | '../obj/gb/vwf7.z80']) 95 | else: 96 | main() 97 | -------------------------------------------------------------------------------- /gba/src/posprintf.h: -------------------------------------------------------------------------------- 1 | /* posprintf - a condensed version of sprintf for Thumb, esp. GBA Copyright (C) 2003 Dan Posluns The person or persons who have associated work with this document (the "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of his knowledge, the work of authorship identified is in the public domain of the country from which the work is published, or (b) hereby dedicates whatever copyright the dedicators holds in the work of authorship identified below (the "Work") to the public domain. A certifier, moreover, dedicates any copyright interest he may have in the associated work, and for these purposes, is described as a "dedicator" below. A certifier has taken reasonable steps to verify the copyright status of this work. Certifier recognizes that his good faith efforts may not shield him from liability if in fact the work certified is not in the public domain. Dedicator makes this dedication for the benefit of the public at large and to the detriment of the Dedicator's heirs and successors. Dedicator intends this dedication to be an overt act of relinquishment in perpetuity of all present and future rights under copyright law, whether vested or contingent, in the Work. Dedicator understands that such relinquishment of all rights includes the relinquishment of all rights to enforce (by lawsuit or otherwise) those copyrights in the Work. Dedicator recognizes that, once placed in the public domain, the Work may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived. Author contact e-mail: dan at danposluns dot com INSTRUCTIONS: To call: posprintf(char *dest, const char *src[, param1[, param2[, ... paramN]]]); - src must be a valid zero-terminated C string. - dest must point to a sufficiently large block of memory to contain the result string. The following format specifiers are valid: %% - print a '%' symbol %s - print a string; parameter must point to a valid zero-terminated C string %d - print a 16-bit (short) integer; must be within [-65,535 .. 65,535] %l - print a 29-bit integer; approximate range [-500,000,000 .. 500,000,000] %x - print a hexadecimal number (lowercase digits) %X - print a hexadecimal number (uppercase digits) The specifiers %d, %l, %x and %X may be modified as follows: - Digits 1 through 9 indicate number of leading spaces to print, eg. %5d would print the number 123 as " 123" %5d would print the number 123456 as "123456" (no leading spaces) - When above digit is prefixed with 0, leading zeros are printed instead of spaces %05d would print the number 123 as "00123" %04d would print the number 12345 as "12345" (no leading zeros) - Negative sign consumes a leading space, eg. %05d would print the number -123 as "-0123" (Hexadecimal numbers are considered unsigned) IF YOU WANT MORE FUNCTIONALITY THAN THIS, YOU CAN FEEL FREE TO MODIFY THE CODE, BUT THEN I WOULD SUGGEST MOVING TO A MORE SOPHISTICATED LIBRARY ANYWAY. *** CAUTION IF NOT USED ON GAMEBOY ADVANCE *** Although this is mostly written as general Thumb code, the %l (29-bit print) specifier code currently uses a software interrupt (SWI) specific to the Gameboy Advance to perform a division. If you wish to port this to other ARM machines, you may need to alter this code. I believe that most ARM machines support SWI 7 as an alternative software divide, although you will have to swap the numerator/denominator registers (r0 and r1). *** END CAUTION *** My contact e-mail is: dan at danposluns dot com */ #ifndef _PSPRINTF_HEADER_ #define _PSPRINTF_HEADER_ extern void posprintf(char *, const char *, ...); #endif -------------------------------------------------------------------------------- /gameboy/tools/incruniq.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Compression experiment for GB static scenes 4 | 5 | Copyright 2019 Damian Yerrick 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | """ 23 | """ 24 | The file format: 25 | 1 byte: number of PB8 compressed tiles (or 0 for 256) 26 | PB16 compressed tiles 27 | 1 byte: length of singleton-eliminated map, divided by 2 and rounded up 28 | IUR compressed map 29 | """ 30 | from collections import Counter 31 | import sys 32 | import argparse 33 | from pb16 import pb16 34 | from uniq import uniq 35 | from bitbyte import BitByteInterleave 36 | 37 | def iur_encode_tilemap(tilemap): 38 | 39 | # Type stickiness (brand new uniques vs. horizontal runs) 40 | # was the key to making IUR efficient 41 | lastwasnew, lastbyte, maxsofar = False, 0, 0 42 | out = BitByteInterleave() 43 | for i, t in enumerate(tilemap): 44 | isnew = t == maxsofar + 1 45 | eqlast = t == lastbyte 46 | ismatch = isnew if lastwasnew else eqlast 47 | if ismatch: 48 | # 0: Same run type as last time 49 | out.putbits(0) 50 | elif isnew: 51 | # 10: Switch run type from non-new to new 52 | out.putbits(0b10, 2) 53 | elif eqlast: 54 | # 10: Switch run type from new to non-new 55 | out.putbits(0b10, 2) 56 | else: 57 | # 11: Literal byte follows 58 | out.putbits(0b11, 2) 59 | out.putbyte(t) 60 | lastbyte, lastwasnew = t, isnew 61 | if isnew: maxsofar += 1 62 | return bytes(out) 63 | 64 | def iur_encode(chrdata): 65 | """Encode with incremental uniques and runs 66 | 67 | chrdata -- a list of bytes objects or other hashables 68 | """ 69 | utiles, tilemap = uniq(chrdata) 70 | return utiles, iur_encode_tilemap(tilemap) 71 | 72 | def parse_argv(argv): 73 | p = argparse.ArgumentParser() 74 | p.add_argument("srcfile") 75 | p.add_argument("iufile") 76 | return p.parse_args(argv[1:]) 77 | 78 | def main(argv=None): 79 | args = parse_argv(argv or sys.argv) 80 | with open(args.srcfile, "rb") as infp: 81 | tiles = infp.read() 82 | block_size = 16 83 | tiles = [tiles[i:i + block_size] 84 | for i in range(0, len(tiles), block_size)] 85 | utiles, iurdata = iur_encode(tiles) 86 | 87 | out = bytearray() 88 | out.append(len(utiles)) 89 | out.extend(b''.join(pb16(b''.join(utiles)))) 90 | halfnamsize = -(-len(tiles) // 2) 91 | print("%d tiles" % len(tiles)) 92 | out.append(halfnamsize) 93 | out.extend(iurdata) 94 | with open(args.iufile, "wb") as outfp: 95 | outfp.write(out) 96 | 97 | if __name__=='__main__': 98 | main() 99 | ## main(["incruniq.py", "../obj/gb/Gus_portrait.chrgb", "test.iu"]) 100 | --------------------------------------------------------------------------------