├── examples ├── wifi │ ├── server │ │ ├── Procfile │ │ ├── node_modules │ │ │ ├── ws │ │ │ │ ├── browser.js │ │ │ │ ├── lib │ │ │ │ │ ├── constants.js │ │ │ │ │ └── limiter.js │ │ │ │ ├── index.js │ │ │ │ ├── LICENSE │ │ │ │ └── package.json │ │ │ └── .package-lock.json │ │ ├── package.json │ │ ├── package-lock.json │ │ └── index.js │ ├── bg.png │ └── wifi.cfg ├── fade │ ├── bg.png │ ├── fade.cfg │ └── main.fab ├── hello_world │ ├── hello_world.cfg │ └── main.fab ├── mmc3 │ ├── bg.png │ ├── mmc3.cfg │ └── main.fab ├── mmc5 │ ├── bg.png │ ├── sfx.ftm │ ├── sfx.nsf │ ├── music.ftm │ ├── mmc5.cfg │ └── main.fab ├── pbz │ ├── bg.png │ ├── pbz.cfg │ └── main.fab ├── rope │ ├── bg.png │ └── rope.cfg ├── mmc1 │ ├── chr0.png │ ├── chr1.png │ ├── chr2.png │ ├── chr3.png │ ├── mmc1.cfg │ └── main.fab ├── text │ ├── font.png │ └── text.cfg ├── billiards │ ├── bg.png │ └── billiards.cfg ├── cnrom │ ├── chr0.png │ ├── chr1.png │ ├── chr2.png │ ├── chr3.png │ ├── cnrom.cfg │ └── main.fab ├── counter │ ├── font.png │ ├── counter.cfg │ └── main.fab ├── logging │ ├── font.png │ ├── logging.cfg │ └── main.fab ├── mapfab │ ├── bg1.png │ ├── bg2.png │ ├── collision.png │ ├── project.mapfab │ ├── palette.macrofab │ ├── chr.macrofab │ ├── mapfab.cfg │ ├── level.macrofab │ └── metatiles.macrofab ├── mapper_30 │ ├── bg.png │ ├── bg2.png │ ├── mapper_30.cfg │ └── main.fab ├── maze │ ├── tiles.png │ ├── tutorial_steps │ │ ├── tiles.png │ │ ├── step1.cfg │ │ ├── step2.cfg │ │ ├── step3.cfg │ │ ├── step4.cfg │ │ ├── step5.cfg │ │ ├── step1.fab │ │ └── step2.fab │ ├── README.md │ └── maze.cfg ├── mouse │ ├── sprite.png │ └── mouse.cfg ├── music │ ├── music.ftm │ ├── danger_streets.ftm │ ├── music.cfg │ └── main.fab ├── rainbow │ ├── bg.png │ ├── sfx.ftm │ ├── sfx.nsf │ ├── music.ftm │ ├── rainbow.cfg │ └── main.fab ├── zapper │ ├── sfx.ftm │ ├── sfx.nsf │ ├── zapper.cfg │ └── main.fab ├── mapper_189 │ ├── bg.png │ ├── mapper_189.cfg │ └── main.fab ├── platformer │ ├── bg.png │ ├── collision.png │ ├── project.mapfab │ ├── palette.macrofab │ ├── chr.macrofab │ ├── platformer.cfg │ ├── level.macrofab │ └── metatiles.macrofab ├── 4_player │ ├── sprite.png │ ├── 4_player.cfg │ └── main.fab ├── animation │ ├── sprite.png │ └── animation.cfg ├── flash_save │ ├── font.png │ └── flash_save.cfg ├── hang_glider │ ├── music.ftm │ ├── plane.png │ ├── title.nam │ ├── title.png │ ├── hang_glider.cfg │ ├── resources.fab │ ├── sprites.fab │ ├── main_menu.fab │ └── cliff.fab ├── metasprite │ ├── sprite.png │ ├── metasprite.cfg │ └── main.fab ├── scanline_irq │ ├── bg.png │ ├── scanline_irq.cfg │ └── main.fab ├── sound_effects │ ├── sfx.ftm │ ├── sfx.nsf │ ├── sound_effects.cfg │ └── main.fab ├── meta_meta_tiles │ ├── bg.png │ ├── collision.png │ ├── project.mapfab │ ├── palette.macrofab │ ├── meta_meta_tiles.cfg │ ├── level.macrofab │ └── metatiles.macrofab ├── scrolling_8_way │ ├── bg.png │ ├── collision.png │ ├── project.mapfab │ ├── status_bar.nam │ ├── palette.macrofab │ ├── chr.macrofab │ ├── metatiles.macrofab │ ├── scrolling_8_way.cfg │ └── level.macrofab ├── fn_ptr │ ├── fn_ptr.cfg │ └── main.fab ├── trig │ ├── trig.cfg │ └── main.fab ├── collision │ ├── collision.cfg │ └── main.fab ├── objects │ └── objects.cfg ├── build_all.sh └── LICENSE_1_0.txt ├── src ├── op_name.inc ├── options.cpp ├── cpu_2a03.hpp ├── rom_dummy.hpp ├── rom_prune.hpp ├── mlb.hpp ├── donut.hpp ├── tests.cpp ├── platform.hpp ├── o_motion.hpp ├── o_loop.hpp ├── c_delete.hpp ├── o_locator.hpp ├── ctags.hpp ├── o_type.hpp ├── sizeof_bits.hpp ├── cg_schedule.hpp ├── o_shift.hpp ├── graphviz.hpp ├── o_arg.hpp ├── o_merge_bb.hpp ├── token.cpp ├── convert_map.hpp ├── nes_system.hpp ├── o_id.hpp ├── convert_png.hpp ├── rom_link.hpp ├── byte_block.hpp ├── ram_init.hpp ├── worklist.cpp ├── rom_alloc.hpp ├── unroll_divisor.hpp ├── ram_alloc.hpp ├── o_defork.hpp ├── o_unused.hpp ├── byteify.hpp ├── flat │ ├── static_set.hpp │ ├── small_multiset.hpp │ ├── static_multiset.hpp │ ├── static_map.hpp │ ├── small_set.hpp │ ├── static_multimap.hpp │ ├── small_map.hpp │ ├── small_multimap.hpp │ └── impl │ │ ├── container_traits.hpp │ │ └── class_def.hpp ├── o_phi.hpp ├── carry.cpp ├── o.hpp ├── ssa_op.cpp ├── mods.inc ├── puf.hpp ├── addr_mode.inc ├── console.hpp ├── fixed_tests.cpp ├── parser_decl.hpp ├── carry.hpp ├── ast.hpp ├── rom_dummy.cpp ├── hw_reg.hpp ├── pow2.hpp ├── macro.hpp ├── robin │ ├── apair.hpp │ └── hash.hpp ├── string.hpp ├── ir_util.hpp ├── cg_ptr.hpp ├── stmt.cpp ├── switch.hpp ├── lt.cpp ├── convert_compress.hpp ├── loop_test.hpp ├── xfab.hpp ├── ir_decl.hpp ├── assert.hpp ├── mapfab.hpp ├── debug_print.hpp ├── define.hpp ├── lodepng │ ├── LICENSE │ └── Makefile ├── multi.hpp ├── bitset_tests.cpp ├── cg_liveness.hpp ├── o_type.cpp ├── object_pool.hpp ├── span.cpp ├── span.hpp ├── o_ai.hpp ├── multi.cpp ├── lex_op_name.inc ├── cg_cset.hpp ├── rom_prune.cpp ├── alloca.hpp ├── worklist.hpp ├── o_arg.cpp ├── hex.hpp ├── ctags.cpp ├── ext_lex_tables.hpp ├── fn_def.cpp ├── asm.cpp ├── eternal_new.hpp ├── flags.hpp ├── convert.hpp ├── compiler_error.hpp ├── group.cpp ├── fixed.hpp ├── fnv1a.hpp ├── thread.hpp ├── ident_map.hpp ├── o_locator.cpp ├── token.hpp ├── lt.hpp ├── format.hpp ├── eval.hpp ├── convert_map.cpp ├── ast.cpp └── o_shift.cpp ├── .gitignore ├── Dockerfile ├── graph.sh ├── docker-compose-test.yml ├── syntax_highlighting ├── README.md └── nesfab.vim ├── lib ├── charmap │ ├── numerals.fab │ └── ascii.fab ├── object │ └── object.fab ├── mapper │ ├── gnrom.fab │ ├── colordreams.fab │ ├── anrom.fab │ ├── gtrom.fab │ ├── 30.fab │ └── mmc3.fab ├── math │ ├── constants.fab │ ├── rng.fab │ └── bit.fab ├── step_counter.fab ├── uvec.fab ├── audio │ └── apu.fab ├── LICENSE_1_0.txt ├── zapper.fab └── logging │ └── lua_log.fab └── doc └── LICENSE_1_0.txt /examples/wifi/server/Procfile: -------------------------------------------------------------------------------- 1 | // Procfile 2 | 3 | web: node index.js -------------------------------------------------------------------------------- /src/op_name.inc: -------------------------------------------------------------------------------- 1 | OP_NAME(BAD_OP_NAME) 2 | #include "lex_op_name.inc" 3 | -------------------------------------------------------------------------------- /src/options.cpp: -------------------------------------------------------------------------------- 1 | #include "options.hpp" 2 | 3 | options_t _options; 4 | 5 | -------------------------------------------------------------------------------- /src/cpu_2a03.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/src/cpu_2a03.hpp -------------------------------------------------------------------------------- /examples/fade/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/fade/bg.png -------------------------------------------------------------------------------- /examples/hello_world/hello_world.cfg: -------------------------------------------------------------------------------- 1 | output = hello_world.nes 2 | input = main.fab 3 | -------------------------------------------------------------------------------- /examples/mmc3/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mmc3/bg.png -------------------------------------------------------------------------------- /examples/mmc5/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mmc5/bg.png -------------------------------------------------------------------------------- /examples/pbz/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/pbz/bg.png -------------------------------------------------------------------------------- /examples/rope/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/rope/bg.png -------------------------------------------------------------------------------- /examples/wifi/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/wifi/bg.png -------------------------------------------------------------------------------- /examples/mmc1/chr0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mmc1/chr0.png -------------------------------------------------------------------------------- /examples/mmc1/chr1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mmc1/chr1.png -------------------------------------------------------------------------------- /examples/mmc1/chr2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mmc1/chr2.png -------------------------------------------------------------------------------- /examples/mmc1/chr3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mmc1/chr3.png -------------------------------------------------------------------------------- /examples/mmc5/sfx.ftm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mmc5/sfx.ftm -------------------------------------------------------------------------------- /examples/mmc5/sfx.nsf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mmc5/sfx.nsf -------------------------------------------------------------------------------- /examples/text/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/text/font.png -------------------------------------------------------------------------------- /examples/billiards/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/billiards/bg.png -------------------------------------------------------------------------------- /examples/cnrom/chr0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/cnrom/chr0.png -------------------------------------------------------------------------------- /examples/cnrom/chr1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/cnrom/chr1.png -------------------------------------------------------------------------------- /examples/cnrom/chr2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/cnrom/chr2.png -------------------------------------------------------------------------------- /examples/cnrom/chr3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/cnrom/chr3.png -------------------------------------------------------------------------------- /examples/counter/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/counter/font.png -------------------------------------------------------------------------------- /examples/logging/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/logging/font.png -------------------------------------------------------------------------------- /examples/mapfab/bg1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mapfab/bg1.png -------------------------------------------------------------------------------- /examples/mapfab/bg2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mapfab/bg2.png -------------------------------------------------------------------------------- /examples/mapper_30/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mapper_30/bg.png -------------------------------------------------------------------------------- /examples/maze/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/maze/tiles.png -------------------------------------------------------------------------------- /examples/mmc5/music.ftm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mmc5/music.ftm -------------------------------------------------------------------------------- /examples/mouse/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mouse/sprite.png -------------------------------------------------------------------------------- /examples/music/music.ftm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/music/music.ftm -------------------------------------------------------------------------------- /examples/rainbow/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/rainbow/bg.png -------------------------------------------------------------------------------- /examples/rainbow/sfx.ftm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/rainbow/sfx.ftm -------------------------------------------------------------------------------- /examples/rainbow/sfx.nsf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/rainbow/sfx.nsf -------------------------------------------------------------------------------- /examples/zapper/sfx.ftm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/zapper/sfx.ftm -------------------------------------------------------------------------------- /examples/zapper/sfx.nsf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/zapper/sfx.nsf -------------------------------------------------------------------------------- /examples/mapper_189/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mapper_189/bg.png -------------------------------------------------------------------------------- /examples/mapper_30/bg2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mapper_30/bg2.png -------------------------------------------------------------------------------- /examples/platformer/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/platformer/bg.png -------------------------------------------------------------------------------- /examples/rainbow/music.ftm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/rainbow/music.ftm -------------------------------------------------------------------------------- /examples/4_player/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/4_player/sprite.png -------------------------------------------------------------------------------- /examples/animation/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/animation/sprite.png -------------------------------------------------------------------------------- /examples/flash_save/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/flash_save/font.png -------------------------------------------------------------------------------- /examples/hang_glider/music.ftm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/hang_glider/music.ftm -------------------------------------------------------------------------------- /examples/hang_glider/plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/hang_glider/plane.png -------------------------------------------------------------------------------- /examples/hang_glider/title.nam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/hang_glider/title.nam -------------------------------------------------------------------------------- /examples/hang_glider/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/hang_glider/title.png -------------------------------------------------------------------------------- /examples/mapfab/collision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mapfab/collision.png -------------------------------------------------------------------------------- /examples/mapfab/project.mapfab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/mapfab/project.mapfab -------------------------------------------------------------------------------- /examples/metasprite/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/metasprite/sprite.png -------------------------------------------------------------------------------- /examples/scanline_irq/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/scanline_irq/bg.png -------------------------------------------------------------------------------- /examples/sound_effects/sfx.ftm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/sound_effects/sfx.ftm -------------------------------------------------------------------------------- /examples/sound_effects/sfx.nsf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/sound_effects/sfx.nsf -------------------------------------------------------------------------------- /examples/meta_meta_tiles/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/meta_meta_tiles/bg.png -------------------------------------------------------------------------------- /examples/scrolling_8_way/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/scrolling_8_way/bg.png -------------------------------------------------------------------------------- /examples/music/danger_streets.ftm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/music/danger_streets.ftm -------------------------------------------------------------------------------- /examples/platformer/collision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/platformer/collision.png -------------------------------------------------------------------------------- /examples/platformer/project.mapfab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/platformer/project.mapfab -------------------------------------------------------------------------------- /src/rom_dummy.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ROM_DUMMY_HPP 2 | #define ROM_DUMMY_HPP 3 | 4 | void gen_rom_dummies(); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/rom_prune.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ROM_PRUNE_HPP 2 | #define ROM_PRUNE_HPP 3 | 4 | void prune_rom_data(); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /examples/fn_ptr/fn_ptr.cfg: -------------------------------------------------------------------------------- 1 | output = fn_ptr.nes 2 | nesfab-dir = ../../ 3 | input = lib/nes.fab 4 | input = main.fab 5 | -------------------------------------------------------------------------------- /examples/mapfab/palette.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | 3 | data /palettes 4 | [] pal_#name# 5 | U[25](_palette) 6 | -------------------------------------------------------------------------------- /examples/maze/tutorial_steps/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/maze/tutorial_steps/tiles.png -------------------------------------------------------------------------------- /examples/meta_meta_tiles/collision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/meta_meta_tiles/collision.png -------------------------------------------------------------------------------- /examples/meta_meta_tiles/project.mapfab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/meta_meta_tiles/project.mapfab -------------------------------------------------------------------------------- /examples/platformer/palette.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | 3 | data /palettes 4 | [] pal_#name# 5 | U[25](_palette) 6 | -------------------------------------------------------------------------------- /examples/scrolling_8_way/collision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/scrolling_8_way/collision.png -------------------------------------------------------------------------------- /examples/scrolling_8_way/project.mapfab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/scrolling_8_way/project.mapfab -------------------------------------------------------------------------------- /examples/scrolling_8_way/status_bar.nam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubby/nesfab/HEAD/examples/scrolling_8_way/status_bar.nam -------------------------------------------------------------------------------- /examples/meta_meta_tiles/palette.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | 3 | data /palettes 4 | [] pal_#name# 5 | U[25](_palette) 6 | -------------------------------------------------------------------------------- /examples/scrolling_8_way/palette.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | 3 | data /palettes 4 | [] pal_#name# 5 | U[25](_palette) 6 | -------------------------------------------------------------------------------- /examples/cnrom/cnrom.cfg: -------------------------------------------------------------------------------- 1 | mapper = CNROM 2 | output = cnrom.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = main.fab 6 | -------------------------------------------------------------------------------- /examples/maze/tutorial_steps/step1.cfg: -------------------------------------------------------------------------------- 1 | mapper = NROM 2 | output = step1.nes 3 | input = ../../../lib/nes.fab 4 | input = step1.fab 5 | -------------------------------------------------------------------------------- /examples/maze/tutorial_steps/step2.cfg: -------------------------------------------------------------------------------- 1 | mapper = NROM 2 | output = step2.nes 3 | input = ../../../lib/nes.fab 4 | input = step2.fab 5 | -------------------------------------------------------------------------------- /examples/maze/tutorial_steps/step3.cfg: -------------------------------------------------------------------------------- 1 | mapper = NROM 2 | output = step3.nes 3 | input = ../../../lib/nes.fab 4 | input = step3.fab 5 | -------------------------------------------------------------------------------- /examples/maze/tutorial_steps/step4.cfg: -------------------------------------------------------------------------------- 1 | mapper = NROM 2 | output = step4.nes 3 | input = ../../../lib/nes.fab 4 | input = step4.fab 5 | -------------------------------------------------------------------------------- /examples/maze/tutorial_steps/step5.cfg: -------------------------------------------------------------------------------- 1 | mapper = NROM 2 | output = step5.nes 3 | input = ../../../lib/nes.fab 4 | input = step5.fab 5 | -------------------------------------------------------------------------------- /src/mlb.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MLB_HPP 2 | #define MLB_HPP 3 | 4 | #include 5 | 6 | void print_mlb(std::ostream& os); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /examples/fade/fade.cfg: -------------------------------------------------------------------------------- 1 | output = fade.nes 2 | nesfab-dir = ../../ 3 | input = lib/nes.fab 4 | input = lib/palette.fab 5 | input = main.fab 6 | -------------------------------------------------------------------------------- /examples/maze/README.md: -------------------------------------------------------------------------------- 1 | # Maze Example 2 | 3 | This example is part of the [Maze Tutorial](https://pubby.games/nesfab/maze_tutorial.html). 4 | -------------------------------------------------------------------------------- /examples/trig/trig.cfg: -------------------------------------------------------------------------------- 1 | output = trig.nes 2 | nesfab-dir = ../../ 3 | input = lib/nes.fab 4 | input = lib/math/trig.fab 5 | input = main.fab 6 | -------------------------------------------------------------------------------- /src/donut.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | std::vector donut(std::uint8_t* begin, std::uint8_t* end); 5 | -------------------------------------------------------------------------------- /examples/4_player/4_player.cfg: -------------------------------------------------------------------------------- 1 | output = 4_player.nes 2 | nesfab-dir = ../../ 3 | controllers = 4 4 | input = lib/nes.fab 5 | input = main.fab 6 | -------------------------------------------------------------------------------- /examples/maze/maze.cfg: -------------------------------------------------------------------------------- 1 | mapper = NROM 2 | output = maze.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = tutorial_steps/step5.fab 6 | -------------------------------------------------------------------------------- /src/tests.cpp: -------------------------------------------------------------------------------- 1 | // This tells Catch to provide a main() - only do this in one cpp file 2 | #define CATCH_CONFIG_MAIN 3 | #include "catch/catch.hpp" 4 | 5 | -------------------------------------------------------------------------------- /examples/collision/collision.cfg: -------------------------------------------------------------------------------- 1 | output = collision.nes 2 | nesfab-dir = ../../ 3 | input = lib/nes.fab 4 | input = lib/math/geometry.fab 5 | input = main.fab 6 | -------------------------------------------------------------------------------- /examples/mmc1/mmc1.cfg: -------------------------------------------------------------------------------- 1 | mapper = MMC1 2 | output = mmc1.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/mapper/mmc1.fab 6 | input = main.fab 7 | -------------------------------------------------------------------------------- /examples/mouse/mouse.cfg: -------------------------------------------------------------------------------- 1 | output = mouse.nes 2 | nesfab-dir = ../../ 3 | controllers = 1 4 | input = lib/nes.fab 5 | input = lib/mouse.fab 6 | input = main.fab 7 | -------------------------------------------------------------------------------- /examples/pbz/pbz.cfg: -------------------------------------------------------------------------------- 1 | mapper = unrom 2 | output = pbz.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/decompress/pbz.fab 6 | input = main.fab 7 | -------------------------------------------------------------------------------- /examples/metasprite/metasprite.cfg: -------------------------------------------------------------------------------- 1 | output = metasprite.nes 2 | nesfab-dir = ../../ 3 | input = lib/nes.fab 4 | input = lib/metasprite/metasprite.fab 5 | input = main.fab 6 | -------------------------------------------------------------------------------- /examples/sound_effects/sound_effects.cfg: -------------------------------------------------------------------------------- 1 | output = sound_effects.nes 2 | nesfab-dir = ../../ 3 | input = lib/nes.fab 4 | input = lib/audio/puf1.fab 5 | input = main.fab 6 | -------------------------------------------------------------------------------- /examples/zapper/zapper.cfg: -------------------------------------------------------------------------------- 1 | output = zapper.nes 2 | nesfab-dir = ../../ 3 | input = lib/nes.fab 4 | input = lib/zapper.fab 5 | input = lib/audio/puf1.fab 6 | input = main.fab 7 | -------------------------------------------------------------------------------- /examples/music/music.cfg: -------------------------------------------------------------------------------- 1 | mirroring = H 2 | system = detect 3 | output = music.nes 4 | nesfab-dir = ../../ 5 | input = lib/nes.fab 6 | input = lib/audio/puf1.fab 7 | input = main.fab 8 | -------------------------------------------------------------------------------- /examples/rope/rope.cfg: -------------------------------------------------------------------------------- 1 | mapper = nrom 2 | output = rope.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/math/trig.fab 6 | input = main.fab 7 | input = tables.fab 8 | -------------------------------------------------------------------------------- /src/platform.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_HPP 2 | #define PLATFORM_HPP 3 | 4 | #if defined(unix) || defined(__unix__) || defined(__unix) 5 | #define PLATFORM_UNIX 6 | #endif 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /examples/flash_save/flash_save.cfg: -------------------------------------------------------------------------------- 1 | output = flash_save.nes 2 | mapper = 30 3 | input = lib/nes.fab 4 | input = lib/decompress/pbz.fab 5 | input = lib/math/base_10.fab 6 | input = main.fab 7 | -------------------------------------------------------------------------------- /examples/mmc3/mmc3.cfg: -------------------------------------------------------------------------------- 1 | mapper = mmc3 2 | output = mmc3.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/math/rng.fab 6 | input = lib/mapper/mmc3.fab 7 | input = main.fab 8 | -------------------------------------------------------------------------------- /examples/text/text.cfg: -------------------------------------------------------------------------------- 1 | mirroring = H 2 | system = detect 3 | output = text.nes 4 | nesfab-dir = ../../ 5 | input = lib/nes.fab 6 | input = lib/decompress/string.fab 7 | input = main.fab 8 | -------------------------------------------------------------------------------- /src/o_motion.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_MOTION_HPP 2 | #define O_MOTION_HPP 3 | 4 | #include "debug_print.hpp" 5 | #include "ir_decl.hpp" 6 | 7 | bool o_motion(log_t* log, ir_t& ir); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /examples/animation/animation.cfg: -------------------------------------------------------------------------------- 1 | output = animation.nes 2 | nesfab-dir = ../../ 3 | input = lib/nes.fab 4 | input = lib/metasprite/metasprite.fab 5 | input = lib/metasprite/animation.fab 6 | input = main.fab 7 | -------------------------------------------------------------------------------- /examples/mapper_189/mapper_189.cfg: -------------------------------------------------------------------------------- 1 | mapper = 189 2 | output = mapper_189.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/math/rng.fab 6 | input = lib/mapper/mmc3.fab 7 | input = main.fab 8 | -------------------------------------------------------------------------------- /examples/mapper_30/mapper_30.cfg: -------------------------------------------------------------------------------- 1 | mapper = 30 2 | output = mapper_30.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/decompress/pbz.fab 6 | input = lib/mapper/30.fab 7 | input = main.fab 8 | -------------------------------------------------------------------------------- /examples/billiards/billiards.cfg: -------------------------------------------------------------------------------- 1 | mapper = nrom 2 | output = billiards.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/metasprite/metasprite.fab 6 | input = lib/math/trig.fab 7 | input = main.fab 8 | -------------------------------------------------------------------------------- /examples/scanline_irq/scanline_irq.cfg: -------------------------------------------------------------------------------- 1 | mapper = mmc3 2 | output = scanline_irq.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/math/rng.fab 6 | input = lib/mapper/mmc3.fab 7 | input = main.fab 8 | -------------------------------------------------------------------------------- /examples/counter/counter.cfg: -------------------------------------------------------------------------------- 1 | mirroring = H 2 | system = detect 3 | output = counter.nes 4 | nesfab-dir = ../../ 5 | input = lib/nes.fab 6 | input = lib/math/rng.fab 7 | input = lib/math/base_10.fab 8 | input = main.fab 9 | -------------------------------------------------------------------------------- /src/o_loop.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_LOOP_HPP 2 | #define O_LOOP_HPP 3 | 4 | #include "debug_print.hpp" 5 | #include "ir_decl.hpp" 6 | 7 | bool o_loop(log_t* log, ir_t& ir, bool is_byteified, bool sloppy); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /examples/logging/logging.cfg: -------------------------------------------------------------------------------- 1 | mirroring = H 2 | system = detect 3 | output = logging.nes 4 | nesfab-dir = ../../ 5 | input = lib/nes.fab 6 | input = lib/charmap/ascii.fab 7 | input = lib/logging/lua_log.fab 8 | input = main.fab 9 | -------------------------------------------------------------------------------- /src/c_delete.hpp: -------------------------------------------------------------------------------- 1 | #ifndef C_DELETE_HPP 2 | #define C_DELETE_HPP 3 | 4 | #include 5 | 6 | struct c_delete 7 | { 8 | void operator()(void* ptr) noexcept { std::free(ptr); } 9 | }; 10 | 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /src/o_locator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_LOCATOR_HPP 2 | #define O_LOCATOR_HPP 3 | 4 | #include "debug_print.hpp" 5 | #include "ir_decl.hpp" 6 | 7 | class fn_t; 8 | 9 | bool o_optimize_locators(log_t* log, ir_t& ir); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | **.nes 3 | **.fdb 4 | **.html 5 | **.bak 6 | **.bmp 7 | !/doc/ 8 | !/examples/ 9 | !/lib/ 10 | !/src/ 11 | !/syntax_highlighting/ 12 | !*[Dd]ocker* 13 | examples/tasks.json 14 | examples/hang_glider/tasks.json 15 | -------------------------------------------------------------------------------- /examples/wifi/wifi.cfg: -------------------------------------------------------------------------------- 1 | mapper = rainbow 2 | output = wifi.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/charmap/ascii.fab 6 | input = lib/mapper/rainbow.fab 7 | input = lib/mapper/rainbow_net.fab 8 | input = main.fab 9 | -------------------------------------------------------------------------------- /src/ctags.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CTAGS_HPP 2 | #define CTAGS_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace fs = ::std::filesystem; 8 | 9 | void write_ctags(FILE* fp, fs::path ctags_path); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/o_type.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_TYPE_HPP 2 | #define O_TYPE_HPP 3 | 4 | #include "ir_decl.hpp" 5 | #include "debug_print.hpp" 6 | 7 | // Removes index types from the IR. 8 | bool o_remove_index_types(log_t* log, ir_t& ir); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/sizeof_bits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SIZEOF_BITS_HPP 2 | #define SIZEOF_BITS_HPP 3 | 4 | #include 5 | #include 6 | 7 | template 8 | constexpr std::size_t sizeof_bits = sizeof(T) * CHAR_BIT; 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bookworm-slim 2 | 3 | WORKDIR /app 4 | 5 | COPY . /app 6 | 7 | RUN apt-get update && \ 8 | apt-get install -y \ 9 | gcc \ 10 | g++ \ 11 | make \ 12 | libboost-program-options-dev 13 | 14 | RUN make release -------------------------------------------------------------------------------- /graph.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #rm graphs/*.svg 3 | #rm graphs/cfg/*.svg 4 | 5 | for f in graphs/*/*.gv; 6 | do 7 | echo $f 8 | dot $f -Goverlap=scale -T svg -o ${f%%.*}.svg 9 | done 10 | 11 | #rm graphs/*.gv 12 | #rm graphs/cfg/*.gv 13 | -------------------------------------------------------------------------------- /src/cg_schedule.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CG_SCHEDULE_HPP 2 | #define CG_SCHEDULE_HPP 3 | 4 | #include "ir_decl.hpp" 5 | 6 | void schedule_ir(ir_t& ir); 7 | 8 | // Optimize the IR after scheduling: 9 | void o_schedule(ir_t& ir); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /examples/wifi/server/node_modules/ws/browser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function () { 4 | throw new Error( 5 | 'ws does not work in the browser. Browser clients must use the native ' + 6 | 'WebSocket object' 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /src/o_shift.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_SHIFT_HPP 2 | #define O_SHIFT_HPP 3 | 4 | #include "debug_print.hpp" 5 | #include "ir_decl.hpp" 6 | 7 | // Replaces certain shifts with table lookups. 8 | bool o_shl_tables(log_t* log, ir_t& ir); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/graphviz.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GRAPHVIZ_HPP 2 | #define GRAPHVIZ_HPP 3 | 4 | #include 5 | 6 | class ir_t; 7 | 8 | void graphviz_ssa(std::ostream& o, ir_t const& ir); 9 | void graphviz_cfg(std::ostream& o, ir_t const& ir); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /examples/objects/objects.cfg: -------------------------------------------------------------------------------- 1 | output = objects.nes 2 | nesfab-dir = ../../ 3 | input = lib/nes.fab 4 | input = lib/math/rng.fab 5 | input = lib/math/geometry.fab 6 | input = lib/object/object.fab 7 | input = lib/object/pool_contiguous.macrofab 8 | input = main.fab 9 | -------------------------------------------------------------------------------- /src/o_arg.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_ARG_HPP 2 | #define O_ARG_HPP 3 | 4 | #include "debug_print.hpp" 5 | #include "ir_decl.hpp" 6 | 7 | class fn_t; 8 | 9 | bool o_remove_unused_arguments(log_t* log, ir_t& ir, fn_t const& fn, bool byteified); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/o_merge_bb.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_MERGE_BB_HPP 2 | #define O_MERGE_BB_HPP 3 | 4 | #include "debug_print.hpp" 5 | #include "ir_decl.hpp" 6 | 7 | // Combines chained CFG blocks together. 8 | bool o_merge_basic_blocks(log_t* log, ir_t& ir); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/token.cpp: -------------------------------------------------------------------------------- 1 | #include "token.hpp" 2 | 3 | #include "format.hpp" 4 | 5 | using namespace lex; 6 | 7 | std::string token_t::to_string(char const* source) const 8 | { 9 | return fmt("{ %, %, % }", token_name(type), value, pstring.view(source)); 10 | } 11 | -------------------------------------------------------------------------------- /examples/rainbow/rainbow.cfg: -------------------------------------------------------------------------------- 1 | mapper = rainbow 2 | expansion-audio = true 3 | output = rainbow.nes 4 | nesfab-dir = ../../ 5 | input = lib/nes.fab 6 | input = lib/math/rng.fab 7 | input = lib/mapper/rainbow.fab 8 | input = lib/audio/puf1.fab 9 | input = main.fab 10 | -------------------------------------------------------------------------------- /src/convert_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONVERT_MAP_HPP 2 | #define CONVERT_MAP_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "convert.hpp" 8 | 9 | std::vector map_to_nt(std::uint8_t const* map, std::size_t size); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/nes_system.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NES_SYSTEM_HPP 2 | #define NES_SYSTEM_HPP 3 | 4 | enum nes_system_t 5 | { 6 | NES_SYSTEM_NTSC, 7 | NES_SYSTEM_PAL, 8 | NES_SYSTEM_DENDY, 9 | NES_SYSTEM_UNKNOWN, 10 | NES_SYSTEM_DETECT, 11 | }; 12 | 13 | #endif 14 | 15 | -------------------------------------------------------------------------------- /src/o_id.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_IDENTITY_HPP 2 | #define O_IDENTITY_HPP 3 | 4 | #include "debug_print.hpp" 5 | 6 | class ir_t; 7 | 8 | // Applies various math identities to the code. 9 | bool o_identities(log_t* log, ir_t& ir, bool post_byteified); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/convert_png.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONVERT_PNG_HPP 2 | #define CONVERT_PNG_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "convert.hpp" 8 | 9 | std::vector png_to_chr(std::uint8_t const* png, std::size_t size, bool chr16); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/rom_link.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ROM_LINK_HPP 2 | #define ROM_LINK_HPP 3 | 4 | #include 5 | #include 6 | 7 | class locator_t; 8 | 9 | void link_variables_optimize(); 10 | std::vector write_rom(std::uint8_t default_fill = 0x00); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /examples/mmc5/mmc5.cfg: -------------------------------------------------------------------------------- 1 | mapper = mmc5 2 | unsafe-bank-switch = true 3 | expansion-audio = true 4 | output = mmc5.nes 5 | nesfab-dir = ../../ 6 | input = lib/nes.fab 7 | input = lib/math/rng.fab 8 | input = lib/mapper/mmc5.fab 9 | input = lib/audio/puf1.fab 10 | input = main.fab 11 | -------------------------------------------------------------------------------- /src/byte_block.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BYTE_BLOCK_HPP 2 | #define BYTE_BLOCK_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "locator.hpp" 8 | #include "asm_proc.hpp" 9 | 10 | using byte_block_data_t = std::variant, asm_proc_t>; 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/ram_init.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RAM_INIT_HPP 2 | #define RAM_INIT_HPP 3 | 4 | #include 5 | 6 | struct gvar_ht; 7 | class asm_proc_t; 8 | 9 | bool gen_group_var_inits(std::vector const& gvars, asm_proc_t& proc); 10 | void gen_group_var_inits(); 11 | 12 | #endif 13 | 14 | -------------------------------------------------------------------------------- /src/worklist.cpp: -------------------------------------------------------------------------------- 1 | #include "worklist.hpp" 2 | 3 | TLS worklist_t cfg_worklist; 4 | TLS worklist_t ssa_worklist; 5 | 6 | // These two vectors can also be used by optimization passes, if need be. 7 | TLS std::vector cfg_workvec; 8 | TLS std::vector ssa_workvec; 9 | -------------------------------------------------------------------------------- /docker-compose-test.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | compiler: 5 | image: local/nesfab:dev-intel # 6 | platform: linux/amd64 7 | command: ["./examples/build_all.sh"] 8 | volumes: 9 | - ${PWD}/output:/app/output 10 | - ${PWD}/examples:/app/examples 11 | -------------------------------------------------------------------------------- /src/rom_alloc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ROM_ALLOC_HPP 2 | #define ROM_ALLOC_HPP 3 | 4 | #include 5 | 6 | #include "debug_print.hpp" 7 | #include "span.hpp" 8 | 9 | class span_allocator_t; 10 | 11 | void alloc_rom(log_t* log, span_allocator_t allocator); 12 | 13 | void print_rom(std::ostream& o); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/unroll_divisor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UNROLL_DIVISOR_HPP 2 | #define UNROLL_DIVISOR_HPP 3 | 4 | // Given a loop iteration count 'n', 5 | // and a desired unroll amount of 'd', 6 | // estimates an unroll amount that divides 'n', which is <= 'd'. 7 | unsigned estimate_unroll_divisor(unsigned n, unsigned d); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /examples/scrolling_8_way/chr.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | #:path:# 3 | 4 | // Since this example uses CNROM, we'll store each CHR file at a different offset. 5 | // Note that you don't have to use "path"; you could use "name" to derive a filename instead. 6 | 7 | data /pbz 8 | [] chr_#name# 9 | file(pbz, #"path"#) 10 | -------------------------------------------------------------------------------- /src/ram_alloc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RAM_ALLOC_HPP 2 | #define RAM_ALLOC_HPP 3 | 4 | #include 5 | 6 | #include "debug_print.hpp" 7 | #include "ram.hpp" 8 | 9 | void alloc_ram(log_t* log, ram_bitset_t const& initial); 10 | 11 | void print_ram(std::ostream& o); 12 | void print_mlb_ram(std::ostream& o); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /examples/mapfab/chr.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | #:path:# 3 | 4 | // Since this example uses CNROM, we'll store each CHR file at a different offset. 5 | // Note that you don't have to use "path"; you could use "name" to derive a filename instead. 6 | 7 | ct U chr_#name#_index = _index 8 | 9 | chrrom (_index * $2000) 10 | file(fmt, #"path"#) 11 | -------------------------------------------------------------------------------- /examples/platformer/chr.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | #:path:# 3 | 4 | // Since this example uses CNROM, we'll store each CHR file at a different offset. 5 | // Note that you don't have to use "path"; you could use "name" to derive a filename instead. 6 | 7 | ct U chr_#name#_index = _index 8 | 9 | chrrom (_index * $2000) 10 | file(fmt, #"path"#) 11 | -------------------------------------------------------------------------------- /examples/wifi/server/node_modules/ws/lib/constants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'], 5 | GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 6 | kStatusCode: Symbol('status-code'), 7 | kWebSocket: Symbol('websocket'), 8 | EMPTY_BUFFER: Buffer.alloc(0), 9 | NOOP: () => {} 10 | }; 11 | -------------------------------------------------------------------------------- /examples/hang_glider/hang_glider.cfg: -------------------------------------------------------------------------------- 1 | mirroring = H 2 | system = detect 3 | output = hang_glider.nes 4 | nesfab-dir = ../../ 5 | input = lib/nes.fab 6 | input = lib/math/rng.fab 7 | input = lib/decompress/rlz.fab 8 | input = lib/audio/puf1.fab 9 | input = cliff.fab 10 | input = game.fab 11 | input = main_menu.fab 12 | input = resources.fab 13 | input = sprites.fab 14 | -------------------------------------------------------------------------------- /examples/hang_glider/resources.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | chrrom 8 | file(fmt, "plane.png") 9 | file(fmt, "title.png") 10 | 11 | audio(puf1_music, "music.txt") 12 | audio(puf1_sfx) 13 | -------------------------------------------------------------------------------- /examples/wifi/server/node_modules/ws/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const WebSocket = require('./lib/websocket'); 4 | 5 | WebSocket.createWebSocketStream = require('./lib/stream'); 6 | WebSocket.Server = require('./lib/websocket-server'); 7 | WebSocket.Receiver = require('./lib/receiver'); 8 | WebSocket.Sender = require('./lib/sender'); 9 | 10 | module.exports = WebSocket; 11 | -------------------------------------------------------------------------------- /examples/wifi/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rainbow-chat", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "ws": "^7.4.6" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/o_defork.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_DEFORK_HPP 2 | #define O_DEFORK_HPP 3 | 4 | #include "debug_print.hpp" 5 | #include "ir_decl.hpp" 6 | 7 | // Removes forking control flow, when all outputs lead to the same node. 8 | bool o_defork(log_t* log, ir_t& ir); 9 | 10 | // Turns single 'SSA_if' CFG nodes into many, removing phis. 11 | bool o_fork(log_t* log, ir_t& ir); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/o_unused.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_UNUSED_HPP 2 | #define O_UNUSED_HPP 3 | 4 | #include "ir_decl.hpp" 5 | #include "debug_print.hpp" 6 | 7 | // Removes SSA nodes that have no observable effect. 8 | bool o_remove_no_effect(log_t* log, ir_t& ir); 9 | 10 | bool o_remove_unused_linked(log_t* log, ir_t& ir); 11 | 12 | bool o_remove_unused_ssa(log_t* log, ir_t& ir); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /examples/mapfab/mapfab.cfg: -------------------------------------------------------------------------------- 1 | mapper = cnrom 2 | output = mapfab.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/palette.fab 6 | input = lib/math/rng.fab 7 | input = lib/object/object.fab 8 | input = lib/object/pool_contiguous.macrofab 9 | input = chr.macrofab 10 | input = palette.macrofab 11 | input = metatiles.macrofab 12 | input = level.macrofab 13 | input = main.fab 14 | -------------------------------------------------------------------------------- /examples/meta_meta_tiles/meta_meta_tiles.cfg: -------------------------------------------------------------------------------- 1 | mapper = nrom 2 | output = meta_meta_tiles.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/palette.fab 6 | input = lib/math/rng.fab 7 | input = lib/object/object.fab 8 | input = lib/object/pool_contiguous.macrofab 9 | input = palette.macrofab 10 | input = metatiles.macrofab 11 | input = level.macrofab 12 | input = main.fab 13 | -------------------------------------------------------------------------------- /src/byteify.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CG_BYTEIFY_HPP 2 | #define CG_BYTEIFY_HPP 3 | 4 | // Converts arithmetic types in the IR to all be bytes. 5 | 6 | #include "cg.hpp" 7 | 8 | void byteify(class ir_t& ir, fn_t const& fn); 9 | 10 | void remove_type_tags(ir_t& ir); 11 | bool insert_signed_mul_subtractions(ir_t& ir); 12 | bool shifts_to_rotates(ir_t& ir, bool handle_constant_shifts); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/flat/static_set.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_STATIC_SET_HPP 2 | #define LIB_FLAT_STATIC_SET_HPP 3 | 4 | #include "flat_set.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template 11 | using static_set = flat_set< 12 | ::boost::container::static_vector, Args...>; 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/o_phi.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_PHI_HPP 2 | #define O_PHI_HPP 3 | 4 | #include 5 | 6 | #include "ir_decl.hpp" 7 | #include "debug_print.hpp" 8 | 9 | ssa_value_t get_trivial_phi_value(log_t* log, ssa_node_t const& node); 10 | bool o_remove_trivial_phis(log_t* log, ir_t& ir); 11 | bool o_remove_redundant_phis(log_t* log, ir_t& ir); 12 | bool o_phis(log_t* log, ir_t& ir); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /syntax_highlighting/README.md: -------------------------------------------------------------------------------- 1 | # Syntax Highlighting 2 | 3 | This directory contains syntax highlighting files for different editors. 4 | Additionally, the following editors have files hosted externally: 5 | 6 | ## VSCode / TextMate 7 | 8 | - [Marketplace](https://marketplace.visualstudio.com/items?itemName=RussellSprouts.nesfab-highlighting) / [Github](https://github.com/RussellSprouts/nesfab-highlighting) 9 | -------------------------------------------------------------------------------- /examples/platformer/platformer.cfg: -------------------------------------------------------------------------------- 1 | mapper = cnrom 2 | output = platformer.nes 3 | nesfab-dir = ../../ 4 | input = lib/nes.fab 5 | input = lib/palette.fab 6 | input = lib/metasprite/metasprite.fab 7 | input = lib/math/rng.fab 8 | input = chr.macrofab 9 | input = palette.macrofab 10 | input = metatiles.macrofab 11 | input = level.macrofab 12 | input = main.fab 13 | input = level.fab 14 | input = scroll.fab 15 | -------------------------------------------------------------------------------- /lib/charmap/numerals.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // A charmap for hexadecimal numerals. 8 | // (You can also use this for base 10 or binary!) 9 | charmap numerals("0123456789a\/Ab\/Bc\/Cd\/De\/Ef\/Fg\/G") 10 | : stows /numerals 11 | -------------------------------------------------------------------------------- /src/carry.cpp: -------------------------------------------------------------------------------- 1 | #include "carry.hpp" 2 | 3 | // For debugging mostly 4 | std::string to_string(carry_t carry) 5 | { 6 | switch(carry) 7 | { 8 | case CARRY_BOTTOM: return "CARRY BOTTOM"; 9 | case CARRY_CLEAR: return "CARRY CLEAR"; 10 | case CARRY_SET: return "CARRY SET"; 11 | case CARRY_TOP: return "CARRY TOP"; 12 | default: return "BAD CARRY"; 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/flat/small_multiset.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_SMALL_MULTISET_HPP 2 | #define LIB_FLAT_SMALL_MULTISET_HPP 3 | 4 | #include "flat_multiset.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template 11 | using small_multiset = flat_multiset< 12 | ::boost::container::small_vector, Args...>; 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/o.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_HPP 2 | #define O_HPP 3 | 4 | #include "ir_decl.hpp" 5 | #include "o_ai.hpp" 6 | #include "o_phi.hpp" 7 | #include "o_unused.hpp" 8 | #include "o_merge_bb.hpp" 9 | #include "o_motion.hpp" 10 | #include "o_arg.hpp" 11 | #include "o_id.hpp" 12 | #include "o_loop.hpp" 13 | #include "o_defork.hpp" 14 | #include "o_shift.hpp" 15 | #include "o_locator.hpp" 16 | #include "o_type.hpp" 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/flat/static_multiset.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_STATIC_MULTISET_HPP 2 | #define LIB_FLAT_STATIC_MULTISET_HPP 3 | 4 | #include "flat_multiset.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template 11 | using static_multiset = flat_multiset< 12 | ::boost::container::static_vector, Args...>; 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /examples/meta_meta_tiles/level.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | #:chr_name:# 3 | #:palette_name:# 4 | #:metatiles_name:# 5 | 6 | data /levels 7 | [] lev_#name# 8 | (@pal_#palette_name#) // Pointer to the palette 9 | (@mt_#metatiles_name#) // Pointer to the metatiles 10 | (@mmt_#metatiles_name#) // Pointer to the metametatiles 11 | (_row_major) // The actual level tiles 12 | 13 | -------------------------------------------------------------------------------- /src/flat/static_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_STATIC_MAP_HPP 2 | #define LIB_FLAT_STATIC_MAP_HPP 3 | 4 | #include "flat_map.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template 11 | using static_map = flat_map< 12 | ::boost::container::static_vector, N>, Args...>; 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/ssa_op.cpp: -------------------------------------------------------------------------------- 1 | #include "ssa_op.hpp" 2 | 3 | std::string_view to_string(ssa_op_t op) 4 | { 5 | using namespace std::literals; 6 | switch(op) 7 | { 8 | #define SSA_DEF(x, ...) case SSA_##x: return #x##sv; 9 | #include "ssa_op.inc" 10 | default: return "???"sv; 11 | } 12 | } 13 | 14 | std::ostream& operator<<(std::ostream& o, ssa_op_t node_type) 15 | { 16 | o << to_string(node_type); 17 | return o; 18 | } 19 | -------------------------------------------------------------------------------- /src/flat/small_set.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_SMALL_SET_HPP 2 | #define LIB_FLAT_SMALL_SET_HPP 3 | 4 | #include "flat_set.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template, typename... Args> 12 | using small_set = flat_set< 13 | ::boost::container::small_vector, Compare>; 14 | 15 | } 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/mods.inc: -------------------------------------------------------------------------------- 1 | MOD(0, inline) 2 | MOD(1, align) 3 | MOD(2, zero_page) 4 | MOD(3, spr_8x16) 5 | MOD(4, graphviz) 6 | MOD(5, dpcm) 7 | MOD(6, info) 8 | MOD(7, static) 9 | MOD(8, palette_3) 10 | MOD(9, palette_25) 11 | MOD(10, sram) 12 | MOD(11, sloppy) 13 | MOD(12, fork_scope) 14 | MOD(13, solo_interrupt) 15 | MOD(14, unroll) 16 | MOD(15, unloop) 17 | MOD(16, unused) 18 | MOD(17, sector) 19 | MOD(18, static_fixed) 20 | MOD(19, inherit) 21 | -------------------------------------------------------------------------------- /src/flat/static_multimap.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_STATIC_MULTIMAP_HPP 2 | #define LIB_FLAT_STATIC_MULTIMAP_HPP 3 | 4 | #include "flat_multimap.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template 11 | using static_multimap = flat_map< 12 | ::boost::container::static_vector, N>, Args...>; 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/puf.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PUF_HPP 2 | #define PUF_HPP 3 | 4 | #include 5 | 6 | #include "pstring.hpp" 7 | 8 | // PUF music engine 9 | 10 | void convert_puf_music(char const* const begin, std::size_t size, lpstring_t at); 11 | 12 | void convert_puf_sfx(char const* const txt_data, std::size_t txt_size, 13 | std::uint8_t const* const nsf_data, std::size_t nsf_size, 14 | lpstring_t at); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /examples/platformer/level.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | #:chr_name:# 3 | #:palette_name:# 4 | #:metatiles_name:# 5 | 6 | data /levels 7 | [] lev_#name# 8 | U(chr_#chr_name#_index) // The CHR bank 9 | (@pal_#palette_name#) // Pointer to the palette 10 | (@mt_#metatiles_name#) // Pointer to the metatiles 11 | U(_width) // The width, in tiles. 12 | (_column_major) // The actual level tiles 13 | 14 | -------------------------------------------------------------------------------- /examples/maze/tutorial_steps/step1.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | mode main() 8 | // Set the palette: 9 | palette = example_palette 10 | ppu_upload_palette() 11 | 12 | while true 13 | 14 | // Define the tileset (commonly called CHR): 15 | chrrom 16 | file(fmt, "tiles.png") 17 | -------------------------------------------------------------------------------- /examples/mapfab/level.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | #:chr_name:# 3 | #:palette_name:# 4 | #:metatiles_name:# 5 | 6 | data /levels 7 | [] lev_#name# 8 | U(chr_#chr_name#_index) // The CHR bank 9 | (@pal_#palette_name#) // Pointer to the palette 10 | (@mt_#metatiles_name#) // Pointer to the metatiles 11 | U(_enemies_num) 12 | (enemies_vec(_enemies_x, _enemies_y)) 13 | (_row_major) // The actual level tiles 14 | 15 | -------------------------------------------------------------------------------- /src/addr_mode.inc: -------------------------------------------------------------------------------- 1 | ADDR_MODE(BAD) 2 | 3 | ADDR_MODE(IMPLIED) 4 | ADDR_MODE(IMMEDIATE) 5 | ADDR_MODE(ZERO_PAGE) 6 | ADDR_MODE(ZERO_PAGE_X) 7 | ADDR_MODE(ZERO_PAGE_Y) 8 | ADDR_MODE(ABSOLUTE) 9 | ADDR_MODE(ABSOLUTE_X) 10 | ADDR_MODE(ABSOLUTE_Y) 11 | ADDR_MODE(INDIRECT) 12 | ADDR_MODE(INDIRECT_X) 13 | ADDR_MODE(INDIRECT_Y) 14 | ADDR_MODE(RELATIVE) 15 | 16 | // "fake" addressing modes: 17 | ADDR_MODE(LONG) 18 | ADDR_MODE(MAYBE) 19 | ADDR_MODE(LIKELY) 20 | ADDR_MODE(BUGGY_IMMEDIATE) 21 | -------------------------------------------------------------------------------- /src/console.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONSOLE_HPP 2 | #define CONSOLE_HPP 3 | 4 | // Console colors: 5 | #define CONSOLE_RED "\x1B[31m" 6 | #define CONSOLE_GRN "\x1B[32m" 7 | #define CONSOLE_YEL "\x1B[33m" 8 | #define CONSOLE_BLU "\x1B[34m" 9 | #define CONSOLE_MAG "\x1B[35m" 10 | #define CONSOLE_CYN "\x1B[36m" 11 | #define CONSOLE_WHT "\x1B[37m" 12 | #define CONSOLE_RESET "\x1B[0m" 13 | #define CONSOLE_BOLD "\x1B[1m" 14 | #define CONSOLE_UNDERLINE "\x1B[4m" 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/flat/small_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_SMALL_MAP_HPP 2 | #define LIB_FLAT_SMALL_MAP_HPP 3 | 4 | #include "flat_map.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template,typename... Args> 12 | using small_map = flat_map< 13 | ::boost::container::small_vector, N, Args...>, 14 | Compare>; 15 | 16 | } 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /examples/mapfab/metatiles.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | #:chr_name:# 3 | #:palette_name:# 4 | 5 | omni data /metatiles 6 | [] mt_#name# 7 | U(_num) // The amount of metatiles. 8 | (_nw) // North-west tiles of each metatile 9 | (_ne) // North-east tiles of each metatile 10 | (_sw) // South-west tiles of each metatile 11 | (_se) // South-east tiles of each metatile 12 | (_combined) // The collisions and attributes, OR'd together. 13 | 14 | -------------------------------------------------------------------------------- /examples/platformer/metatiles.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | #:chr_name:# 3 | #:palette_name:# 4 | 5 | omni data /metatiles 6 | [] mt_#name# 7 | U(_num) // The amount of metatiles. 8 | (_nw) // North-west tiles of each metatile 9 | (_ne) // North-east tiles of each metatile 10 | (_sw) // South-west tiles of each metatile 11 | (_se) // South-east tiles of each metatile 12 | (_combined) // The collisions and attributes, OR'd together. 13 | 14 | -------------------------------------------------------------------------------- /examples/scrolling_8_way/metatiles.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | #:chr_name:# 3 | #:palette_name:# 4 | 5 | omni data /metatiles 6 | [] mt_#name# 7 | U(_num) // The amount of metatiles. 8 | (_nw) // North-west tiles of each metatile 9 | (_ne) // North-east tiles of each metatile 10 | (_sw) // South-west tiles of each metatile 11 | (_se) // South-east tiles of each metatile 12 | (_combined) // The collisions and attributes, OR'd together. 13 | 14 | -------------------------------------------------------------------------------- /src/flat/small_multimap.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_SMALL_MULTIMAP_HPP 2 | #define LIB_FLAT_SMALL_MULTIMAP_HPP 3 | 4 | #include "flat_multimap.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template, typename... Args> 12 | using small_multimap = flat_multimap< 13 | ::boost::container::small_vector, N, Args...>, 14 | Compare>; 15 | 16 | } 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /examples/scrolling_8_way/scrolling_8_way.cfg: -------------------------------------------------------------------------------- 1 | mapper = anrom 2 | output = scrolling_8_way.nes 3 | nesfab-dir = ../../ 4 | input = lib/mapper/anrom.fab 5 | input = lib/nes.fab 6 | input = lib/palette.fab 7 | input = lib/math/rng.fab 8 | input = lib/metasprite/metasprite.fab 9 | input = lib/decompress/pbz.fab 10 | input = lib/decompress/rlz.fab 11 | input = chr.macrofab 12 | input = palette.macrofab 13 | input = metatiles.macrofab 14 | input = level.macrofab 15 | input = main.fab 16 | input = level.fab 17 | input = scroll.fab 18 | -------------------------------------------------------------------------------- /examples/scrolling_8_way/level.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | #:chr_name:# 3 | #:palette_name:# 4 | #:metatiles_name:# 5 | 6 | data /levels 7 | [] lev_#name# 8 | (@chr_#chr_name#) // Pointer to the CHR 9 | (@pal_#palette_name#) // Pointer to the palette 10 | (@mt_#metatiles_name#) // Pointer to the metatiles 11 | U(_width) // The width, in tiles. 12 | U(_height) // The height, in tiles. 13 | (_row_major) // The actual level tiles 14 | 15 | -------------------------------------------------------------------------------- /lib/object/object.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // Definitions related to objects. 8 | // You'll need this file when using the object pool macros. 9 | 10 | ct U BAD_OBJECT = $FF 11 | 12 | ct U[8] OBJECT_POOL_MASKS = U[8]( $1, $2, $4, $8, $10, $20, $40, $80) 13 | ct U[8] INVERSE_OBJECT_POOL_MASKS = U[8]($fe, $fd, $fb, $f7, $ef, $df, $bf, $7f) 14 | 15 | -------------------------------------------------------------------------------- /lib/mapper/gnrom.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | /////////////////////////////////////////////////////////////////////////////// 8 | // Helper code for interfacing GNROM ////////////////////////////////////////// 9 | /////////////////////////////////////////////////////////////////////////////// 10 | 11 | // Bits valid to modify using 'state': 12 | ct U STATE_MASK = %00001111 13 | -------------------------------------------------------------------------------- /lib/math/constants.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This file contains common math constants. 8 | 9 | ct Real PI = 3.14159265359 10 | ct Real EULER = 2.71828182846 // 'e' 11 | ct Real PHI = 1.61803398875 // Golden ratio 12 | ct Real GAMMA = 0.57721566490 13 | ct Real SQRT_2 = 1.41421356237 14 | ct Real SQRT_3 = 1.73205080757 15 | ct Real SQRT_PI = 1.77245385091 16 | -------------------------------------------------------------------------------- /src/fixed_tests.cpp: -------------------------------------------------------------------------------- 1 | #include "catch/catch.hpp" 2 | #include "type_mask.hpp" 3 | 4 | TEST_CASE("numeric_bitmask", "[fixed]") 5 | { 6 | REQUIRE(whole_bytes(TYPE_U) == 1); 7 | REQUIRE(frac_bytes(TYPE_U) == 0); 8 | 9 | REQUIRE(numeric_bitmask(TYPE_U) == 0xFFull << 24); 10 | REQUIRE(numeric_bitmask(TYPE_U30) == 0xFFFFFFull << 24); 11 | REQUIRE(numeric_bitmask(type_f(1)) == 0xFFull << 16); 12 | REQUIRE(numeric_bitmask(type_u(1, 1)) == 0xFFFFull << 16); 13 | REQUIRE(numeric_bitmask(type_s(1, 3)) == 0xFFFFFFFFull); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/parser_decl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_DECL_HPP 2 | #define PARSER_DECL_HPP 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "token.hpp" 9 | #include "pstring.hpp" 10 | #include "type.hpp" 11 | 12 | namespace bc = boost::container; 13 | 14 | template 15 | class parser_t; 16 | 17 | struct var_decl_t 18 | { 19 | src_type_t src_type; 20 | pstring_t name; 21 | }; 22 | 23 | struct string_literal_t 24 | { 25 | std::string string; 26 | pstring_t pstring; 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/carry.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CARRY_HPP 2 | #define CARRY_HPP 3 | 4 | #include 5 | 6 | enum carry_t : unsigned char 7 | { 8 | CARRY_BOTTOM = 0b00, 9 | CARRY_CLEAR = 0b01, 10 | CARRY_SET = 0b10, 11 | CARRY_TOP = 0b11, 12 | }; 13 | 14 | std::string to_string(carry_t carry); 15 | 16 | constexpr bool carry_const(carry_t cr) 17 | { 18 | return cr == CARRY_CLEAR || cr == CARRY_SET; 19 | } 20 | 21 | constexpr carry_t carry_intersect(carry_t a, carry_t b) 22 | { 23 | return carry_t(a | b); 24 | } 25 | 26 | #endif 27 | 28 | 29 | -------------------------------------------------------------------------------- /lib/mapper/colordreams.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | /////////////////////////////////////////////////////////////////////////////// 8 | // Helper code for interfacing COLORDREAMS //////////////////////////////////// 9 | /////////////////////////////////////////////////////////////////////////////// 10 | 11 | // Bits valid to modify using 'state': 12 | ct U STATE_MASK = %11110000 13 | -------------------------------------------------------------------------------- /src/ast.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AST_HPP 2 | #define AST_HPP 3 | 4 | #include 5 | 6 | #include "token.hpp" 7 | 8 | struct mods_t; 9 | class global_t; 10 | 11 | struct ast_node_t 12 | { 13 | using int_type = std::uint64_t; // Should match fixed_uint_t 14 | 15 | token_t token = {}; 16 | union 17 | { 18 | ast_node_t* children = nullptr; 19 | mods_t* mods; 20 | global_t const* charmap; 21 | int_type uint; 22 | }; 23 | 24 | unsigned num_children() const; 25 | void weaken_idents(); 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/rom_dummy.cpp: -------------------------------------------------------------------------------- 1 | #include "rom_dummy.hpp" 2 | 3 | #include 4 | 5 | #include "rom.hpp" 6 | #include "locator.hpp" 7 | #include "group.hpp" 8 | 9 | void gen_rom_dummies() 10 | { 11 | for(group_ht g : group_ht::handles()) 12 | { 13 | if(!g->dummy_required()) 14 | continue; 15 | 16 | loc_vec_t vec = { locator_t::data_bank(g) }; 17 | rom_array_ht a = rom_array_t::make(std::move(vec), false, false, ROMR_NORMAL, g->data_handle()); 18 | a->mark_emits(); 19 | g->set_dummy(a); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/step_counter.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // Implements a simple counter that gets incremented when waiting for 'nmi'. 8 | // This can be used to count the iterations of your game loop. 9 | // Unlike 'nmi_counter', it does not get incremented on lag frames. 10 | 11 | vars 12 | U step_counter = 0 13 | 14 | fn nmi_step() 15 | : -inline 16 | fence 17 | step_counter += 1 18 | nmi 19 | -------------------------------------------------------------------------------- /lib/mapper/anrom.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | /////////////////////////////////////////////////////////////////////////////// 8 | // Helper code for interfacing ANROM ////////////////////////////////////////// 9 | /////////////////////////////////////////////////////////////////////////////// 10 | 11 | ct U ANROM_NT = %10000 12 | 13 | // Bits valid to modify using 'state': 14 | ct U STATE_MASK = %00010000 15 | -------------------------------------------------------------------------------- /src/hw_reg.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HW_REG_HPP 2 | #define HW_REG_HPP 3 | 4 | #include 5 | 6 | constexpr std::uint16_t PPUCTRL = 0x2000; 7 | constexpr std::uint16_t PPUMASK = 0x2001; 8 | constexpr std::uint16_t PPUSTATUS = 0x2002; 9 | constexpr std::uint16_t OAMADDR = 0x2003; 10 | constexpr std::uint16_t OAMDATA = 0x2004; 11 | constexpr std::uint16_t PPUSCROLL = 0x2005; 12 | constexpr std::uint16_t PPUADDR = 0x2006; 13 | constexpr std::uint16_t PPUDATA = 0x2007; 14 | constexpr std::uint16_t OAMDMA = 0x4014; 15 | constexpr std::uint16_t SNDCHN = 0x4015; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/pow2.hpp: -------------------------------------------------------------------------------- 1 | #ifndef POW2_HPP 2 | #define POW2_HPP 3 | 4 | #include 5 | #include 6 | 7 | constexpr std::uint64_t is_pow2(std::uint64_t v) 8 | { 9 | return (v & (v - 1)) == 0ull; 10 | } 11 | 12 | constexpr std::uint64_t next_pow2(std::uint64_t v) 13 | { 14 | std::uint64_t r = v; 15 | --r; 16 | r |= r >> 1ull; 17 | r |= r >> 2ull; 18 | r |= r >> 4ull; 19 | r |= r >> 8ull; 20 | r |= r >> 16ull; 21 | r |= r >> 32ull; 22 | ++r; 23 | 24 | assert(r >= v); 25 | assert(is_pow2(r)); 26 | 27 | return r; 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/macro.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MACRO_HPP 2 | #define MACRO_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "pstring.hpp" 9 | 10 | struct macro_error_t : public std::exception 11 | { 12 | explicit macro_error_t(std::string const& msg, pstring_t pstring) 13 | : msg(msg) 14 | , pstring(pstring) 15 | {} 16 | 17 | virtual const char* what() const noexcept { return msg.c_str(); } 18 | std::string msg; 19 | pstring_t pstring; 20 | }; 21 | 22 | std::string invoke_macro(unsigned file_i, std::vector const& args); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/robin/apair.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ROBIN_HOOD_APAIR_HPP 2 | #define ROBIN_HOOD_APAIR_HPP 3 | 4 | // A POD alternative to std::pair. 5 | // (the 'a' in 'apair' stands for aggregate) 6 | 7 | #include 8 | 9 | namespace rh 10 | { 11 | 12 | template 13 | struct apair 14 | { 15 | using first_type = A; 16 | using second_type = B; 17 | A first; 18 | B second; 19 | }; 20 | 21 | template 22 | constexpr apair make_apair(A&& a, B&& b) 23 | { 24 | return { std::forward(a), std::forward(b) }; 25 | } 26 | 27 | } // namespace 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/string.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STRING_HPP 2 | #define STRING_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // Utility functions for working with strings. 9 | 10 | inline std::string to_lower(std::string str) 11 | { 12 | std::transform(str.begin(), str.end(), str.begin(), 13 | [](unsigned char c){ return std::tolower(c); }); 14 | return str; 15 | } 16 | 17 | inline std::string to_upper(std::string str) 18 | { 19 | std::transform(str.begin(), str.end(), str.begin(), 20 | [](unsigned char c){ return std::toupper(c); }); 21 | return str; 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/ir_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef IR_UTIL_HPP 2 | #define IR_UTIL_HPP 3 | 4 | #include "ir.hpp" 5 | 6 | class fn_t; 7 | 8 | bool io_pure(ssa_node_t const& ssa_node); 9 | bool pure(ssa_node_t const& ssa_node); 10 | bool ct_pure(ssa_node_t const& ssa_node); 11 | 12 | // If 'ssa_node' changes the bank to an unspecified value. 13 | bool clobbers_unknown_bank(fn_t const& fn, ssa_node_t const& ssa_node); 14 | 15 | // Loosely approximates the cost of each ssa node, proportional to (but not equal to) cycles. 16 | unsigned estimate_cost(ssa_node_t const& ssa_node); 17 | 18 | void steal_ssa_after(ssa_ht ssa, cfg_ht steal_dest); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/cg_ptr.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CG_PTR_HPP 2 | #define CG_PTR_HPP 3 | 4 | #include "ir_decl.hpp" 5 | #include "decl.hpp" 6 | 7 | class locator_t; 8 | 9 | // Moves bank switches out of loops, when possible. 10 | // NOTE: Requires loop information to be built before calling. 11 | bool cg_hoist_bank_switches(fn_t const& fn, ir_t& ir); 12 | 13 | // Call after scheduling. 14 | // Sets FLAG_BANK_PRELOADED on SSA nodes that don't need to bankswitch, 15 | // and returns the dominating bankswitch in the IR. 16 | // A.K.A. the optimal bank the function should be in when called. 17 | locator_t cg_calc_bank_switches(fn_ht fn, ir_t& ir); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/stmt.cpp: -------------------------------------------------------------------------------- 1 | #include "stmt.hpp" 2 | 3 | bool has_expression(stmt_name_t stmt_name) 4 | { 5 | switch(stmt_name) 6 | { 7 | default: return false; 8 | #define X(x, e) case x: return e; 9 | STMT_XENUM 10 | #undef X 11 | } 12 | } 13 | 14 | std::string to_string(stmt_name_t stmt_name) 15 | { 16 | switch(stmt_name) 17 | { 18 | default: 19 | if(is_var_init(stmt_name)) 20 | return ("STMT_VAR_INIT " + std::to_string(get_local_i(stmt_name))); 21 | else 22 | return "bad stmt_name_t"; 23 | 24 | #define X(x, e) case x: return #x; 25 | STMT_XENUM 26 | #undef X 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/hello_world/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This small program plays a sound effect. 8 | 9 | // Variables: 10 | vars /sound 11 | UU pitch = 1000 12 | 13 | // Sends 'pitch' variable to the APU, emitting sound: 14 | fn play_sound() 15 | {$4015}(%100) 16 | {$4008}($FF) 17 | {$400A}(pitch.a) 18 | {$400B}(pitch.b & %111) 19 | 20 | mode main() 21 | {$2000}(%10000000) 22 | while true 23 | pitch *= 1.01 24 | play_sound() 25 | nmi 26 | -------------------------------------------------------------------------------- /src/switch.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SWITCH_HPP 2 | #define SWITCH_HPP 3 | 4 | #include 5 | 6 | #include "robin/map.hpp" 7 | 8 | #include "ir_decl.hpp" 9 | #include "locator.hpp" 10 | 11 | class ir_t; 12 | class ssa_node_t; 13 | 14 | // Converts a SSA_switch_partial to a SSA_switch_full, 15 | // also updating the CFG by removing the default case. 16 | // Return 'true' on success. 17 | bool switch_partial_to_full(ssa_node_t& switch_node); 18 | 19 | // Converts every SSA_switch_partial node to SSA_switch_full. 20 | // Return 'true' if any node updated. 21 | bool switch_partial_to_full(ir_t& ir); 22 | 23 | using switch_table_t = std::vector; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /lib/mapper/gtrom.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | /////////////////////////////////////////////////////////////////////////////// 8 | // Helper code for interfacing GTROM ////////////////////////////////////////// 9 | /////////////////////////////////////////////////////////////////////////////// 10 | 11 | // Flags: 12 | ct U GTROM_CHR_BANK = %00010000 13 | ct U GTROM_NT_BANK = %00100000 14 | ct U GTROM_RED_LED = %01000000 15 | ct U GTROM_GREEN_LED = %10000000 16 | 17 | // Bits valid to modify using 'state': 18 | ct U STATE_MASK = %11110000 19 | -------------------------------------------------------------------------------- /src/robin/hash.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ROBIN_HOOD_HASH_HPP 2 | #define ROBIN_HOOD_HASH_HPP 3 | 4 | // Functions for defining / improving hashes. 5 | 6 | #include 7 | 8 | namespace rh 9 | { 10 | [[gnu::always_inline]] 11 | constexpr std::size_t hash_combine(std::size_t a, std::size_t b) 12 | { 13 | return a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2)); 14 | } 15 | 16 | // Just randomizes an integer hash a bit: 17 | [[gnu::always_inline]] 18 | constexpr std::size_t hash_finalize(std::size_t h) 19 | { 20 | h = (h ^ (h >> 30ull)) * 0xbf58476d1ce4e5b9ull; 21 | h = (h ^ (h >> 27ull)) * 0x94d049bb133111ebull; 22 | h = h ^ (h >> 31ull); 23 | return h; 24 | } 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /examples/sound_effects/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This small program plays PUF sound effects. 8 | 9 | audio(puf1_sfx, "sfx.txt", "sfx.nsf") 10 | audio(puf1_music) 11 | 12 | mode main() 13 | : nmi game_nmi 14 | puf.init(system) 15 | {PPUCTRL}(PPUCTRL_NMI_ON) 16 | while true 17 | nmi 18 | update_pads() 19 | if pads[0].pressed & BUTTON_UP 20 | puf.play_sfx(0) 21 | if pads[0].pressed & BUTTON_DOWN 22 | puf.play_sfx(1) 23 | 24 | nmi game_nmi() 25 | ppu_upload_oam_poll_pads(0) 26 | puf.process(PUF_DEFAULT) 27 | -------------------------------------------------------------------------------- /src/lt.cpp: -------------------------------------------------------------------------------- 1 | #include "lt.hpp" 2 | 3 | #include "eval.hpp" 4 | 5 | lt_ht alloc_lt_value(type_t type, ast_node_t const& expr) 6 | { 7 | lt_value_t* ptr; 8 | return lt_ht::pool_emplace(ptr, lt_value_t{ type, expr }); 9 | } 10 | 11 | void lt_value_t::resolve(romv_t romv) 12 | { 13 | auto rpair = interpret_lt(romv, ast, type); 14 | 15 | assert(rpair.type == type); 16 | passert(rpair.value.size(), lex::token_name(ast.token.type)); 17 | passert(rpair.value.size() == num_members(type), rpair.value.size(), type); 18 | 19 | results[romv].bytes.clear(); 20 | append_locator_bytes(true, results[romv].bytes, rpair.value, type, ast.token.pstring); 21 | assert(resolved(romv)); 22 | 23 | results[romv].rval = std::move(rpair.value); 24 | } 25 | -------------------------------------------------------------------------------- /examples/counter/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // Prints a constantly increasing number on the screen. 8 | 9 | chrrom 10 | file(fmt, "font.png") 11 | 12 | mode main() 13 | palette = example_palette 14 | ppu_upload_palette() 15 | 16 | {PPUCTRL}(PPUCTRL_NMI_ON) 17 | {PPUMASK}(PPUMASK_ON | PPUMASK_NO_CLIP) 18 | for UU i = 0; true; i += randb(10) + 1 19 | nmi 20 | ppu_reset_addr($218E) 21 | U[5] array = uu_to_ddddd(i) 22 | do for U i = 4; i != $FF; i -= 1 23 | {PPUDATA}(array[i] + $10) 24 | ppu_reset_scroll(0, 0) 25 | -------------------------------------------------------------------------------- /lib/mapper/30.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | /////////////////////////////////////////////////////////////////////////////// 8 | // Helper code for interfacing MAPPER 30 ////////////////////////////////////// 9 | /////////////////////////////////////////////////////////////////////////////// 10 | 11 | // Bits valid to modify using 'state': 12 | ct U STATE_MASK = %11100000 13 | 14 | ct U M30_MIRRORING = %10000000 15 | 16 | // Returns a value which can be passed to 'state()' to set the CHR bank. 17 | // 'bank' should be in the range [0, 3] 18 | fn M30_CHR(U bank) U 19 | : +inline 20 | return bank << 5 21 | -------------------------------------------------------------------------------- /src/convert_compress.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONVERT_COMPRESS_HPP 2 | #define CONVERT_COMPRESS_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "convert.hpp" 8 | 9 | std::vector compress_pbz(std::uint8_t* begin, std::uint8_t* end); 10 | conversion_t convert_pbz(std::uint8_t* begin, std::uint8_t* end); 11 | 12 | // GBA RLUnComp 13 | // See: https://www.nesdev.org/wiki/Tile_compression#GBA_RLUnComp 14 | std::vector compress_rlz(std::uint8_t* begin, std::uint8_t* end, bool terminate); 15 | conversion_t convert_rlz(std::uint8_t* begin, std::uint8_t* end, bool terminate); 16 | 17 | std::vector compress_donut(std::uint8_t* begin, std::uint8_t* end); 18 | conversion_t convert_donut(std::uint8_t* begin, std::uint8_t* end); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/loop_test.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LOOP_TEST_HPP 2 | #define LOOP_TEST_HPP 3 | 4 | #include 5 | 6 | // This is used inside "for_each" style functions. 7 | // It converts functions returning void into functions returning 'true'. 8 | 9 | template 10 | struct loop_test_t 11 | { 12 | Fn const& fn; 13 | 14 | template 15 | bool call(T&&... t) { return fn(std::forward(t)...); } 16 | }; 17 | 18 | 19 | template 20 | struct loop_test_t 21 | { 22 | Fn const& fn; 23 | 24 | template 25 | bool call(T&&... t) { fn(std::forward(t)...); return true; } 26 | }; 27 | 28 | #define LOOP_TEST(FN, ...) (::loop_test_t{FN}.call(__VA_ARGS__)) 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/xfab.hpp: -------------------------------------------------------------------------------- 1 | #ifndef XFAB_HPP 2 | #define XFAB_HPP 3 | 4 | #include 5 | 6 | #include "pstring.hpp" 7 | 8 | // 8x8Fab level files 9 | 10 | struct xfab_macros_t 11 | { 12 | std::string chr; 13 | std::string palette; 14 | std::string level; 15 | }; 16 | 17 | enum xfab_convert_type_t 18 | { 19 | XFAB_INVALID, 20 | XFAB_RAW, 21 | XFAB_RLZ, 22 | XFAB_PBZ, 23 | }; 24 | 25 | template 26 | class ident_map_t; 27 | struct global_ht; 28 | struct group_ht; 29 | 30 | void convert_xfab(xfab_convert_type_t ct, std::uint8_t const* const begin, std::size_t size, 31 | lpstring_t at, fs::path xfab_path, xfab_macros_t const& macros, 32 | ident_map_t* private_globals, 33 | ident_map_t* private_groups); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /examples/maze/tutorial_steps/step2.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | fn load_level() 8 | // Tell the NES which VRAM address we want to upload to: 9 | ppu_reset_addr($2000) 10 | 11 | // Upload 1024 bytes; all ones. 12 | for UU i = 0; i < 1024; i += 1 13 | {PPUDATA}(1) 14 | 15 | mode main() 16 | // Set the palette: 17 | palette = example_palette 18 | ppu_upload_palette() 19 | 20 | // Load the background: 21 | load_level() 22 | 23 | // Turn on rendering: 24 | {PPUMASK}(PPUMASK_ON | PPUMASK_NO_CLIP) 25 | 26 | while true 27 | 28 | // Define the tileset (commonly called CHR): 29 | chrrom 30 | file(fmt, "tiles.png") 31 | -------------------------------------------------------------------------------- /examples/wifi/server/node_modules/.package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rainbow-chat", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "node_modules/ws": { 8 | "version": "7.4.6", 9 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", 10 | "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", 11 | "engines": { 12 | "node": ">=8.3.0" 13 | }, 14 | "peerDependencies": { 15 | "bufferutil": "^4.0.1", 16 | "utf-8-validate": "^5.0.2" 17 | }, 18 | "peerDependenciesMeta": { 19 | "bufferutil": { 20 | "optional": true 21 | }, 22 | "utf-8-validate": { 23 | "optional": true 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/ir_decl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef IR_DECL_HPP 2 | #define IR_DECL_HPP 3 | 4 | #include 5 | 6 | #include "static_pool.hpp" 7 | 8 | //////////////////////////////////////// 9 | // Main types 10 | //////////////////////////////////////// 11 | 12 | class ir_t; 13 | class cfg_node_t; 14 | class ssa_node_t; 15 | class ssa_fwd_edge_t; 16 | class ssa_bck_edge_t; 17 | class cfg_fwd_edge_t; 18 | class cfg_bck_edge_t; 19 | class ssa_value_t; 20 | 21 | using ssa_data_pool = static_any_pool_t; 22 | using cfg_data_pool = static_any_pool_t; 23 | 24 | using ssa_pool = static_intrusive_pool_t; 25 | using cfg_pool = static_intrusive_pool_t; 26 | 27 | using ssa_ht = ssa_pool::handle_t; 28 | using cfg_ht = cfg_pool::handle_t; 29 | 30 | DEF_HANDLE_HASH(ssa_ht) 31 | DEF_HANDLE_HASH(cfg_ht) 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/assert.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ASSERT_HPP 2 | #define ASSERT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "format.hpp" 9 | 10 | // passert is like assert, but lets you print more information on failure. 11 | #ifdef NDEBUG 12 | #define passert(C, ...) ((void) 0) 13 | #else 14 | #define passert(C, ...) (void)((C) || (_passert_impl(#C, __FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__), 0)) 15 | #endif 16 | 17 | template 18 | void _passert_impl(char const* expr, char const* file, long long line, char const* fn, Args const&... args) 19 | { 20 | std::fflush(stdout); 21 | std::fprintf(stderr, "%s\n", 22 | ezcat(" ", "assert: ", file, ':', line, ':', fn, ": Assertion `", expr, 23 | "' failed. Additional info below.\n", args...).c_str()); 24 | std::abort(); 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /examples/meta_meta_tiles/metatiles.macrofab: -------------------------------------------------------------------------------- 1 | #:name:# 2 | #:chr_name:# 3 | #:palette_name:# 4 | 5 | omni data /mt 6 | [] mt_#name# 7 | U(_num) // The amount of metatiles. 8 | (_nw) // North-west tiles of each metatile 9 | (_ne) // North-east tiles of each metatile 10 | (_sw) // South-west tiles of each metatile 11 | (_se) // South-east tiles of each metatile 12 | (_collisions) // The collisions 13 | 14 | omni data /mmt 15 | [] mmt_#name# 16 | U(_mmt_num) 17 | (_mmt_nw) // North-west metatiles of each metametatile 18 | (_mmt_ne) // North-east metatiles of each metametatile 19 | (_mmt_sw) // South-west metatiles of each metametatile 20 | (_mmt_se) // South-east metatiles of each metametatile 21 | (_mmt_attributes) // All the attributes 22 | 23 | -------------------------------------------------------------------------------- /src/mapfab.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAPFAB_HPP 2 | #define MAPFAB_HPP 3 | 4 | #include 5 | 6 | #include "pstring.hpp" 7 | 8 | // MapFab level files 9 | 10 | struct mapfab_macros_t 11 | { 12 | std::string chr; 13 | std::string palette; 14 | std::string metatiles; 15 | std::string level; 16 | }; 17 | 18 | enum mapfab_convert_type_t 19 | { 20 | MAPFAB_INVALID, 21 | MAPFAB_RAW, 22 | MAPFAB_RLZ, 23 | MAPFAB_PBZ, 24 | MAPFAB_MMT_32, 25 | }; 26 | 27 | template 28 | class ident_map_t; 29 | struct global_ht; 30 | struct group_ht; 31 | 32 | void convert_mapfab(mapfab_convert_type_t ct, std::uint8_t const* const begin, std::size_t size, 33 | lpstring_t at, fs::path mapfab_path, mapfab_macros_t const& macros, 34 | ident_map_t* private_globals, 35 | ident_map_t* private_groups); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /examples/music/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This example plays a song, using the left and right buttons to change its speed. 8 | 9 | audio(puf1_music, "danger_streets.txt") 10 | audio(puf1_sfx) 11 | 12 | nmi main_nmi() 13 | ppu_upload_oam_poll_pads(0) 14 | puf.process(PUF_DEFAULT) 15 | 16 | mode main() 17 | : nmi main_nmi 18 | puf.init(system) 19 | U speed = puf.play_track(0) 20 | 21 | {PPUCTRL}(PPUCTRL_NMI_ON) 22 | while true 23 | nmi 24 | 25 | update_pads() 26 | 27 | if pads[0].pressed & BUTTON_RIGHT && speed > 1 28 | speed -= 1 29 | else if pads[0].pressed & BUTTON_LEFT && speed < 30 30 | speed += 1 31 | 32 | puf.set_speed(speed) 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/hang_glider/sprites.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | fn prepare_sprites() 8 | ct U[9] plane_patterns = U[]($40, $44, $48, $4C, $1C, $7C, $78, $74, $70) 9 | 10 | U i = 0 11 | do for U x = 0; x != 4; x += 1 12 | do for U y = 0; y != 3; y += 1 13 | i = push_oam(i, 14 | px.a - 16 + (x << 3), 15 | PLAYER_Y - 23 + (y << 3), 16 | plane_patterns[pdir] + x + (y << 4), 17 | 0) 18 | 19 | do for U j = 0; j != len(score); j += 1 20 | i = push_oam(i, 21 | 152 - (j << 3), 22 | 16, 23 | score[j] | $10, 24 | 1) 25 | 26 | hide_oam(i) 27 | 28 | -------------------------------------------------------------------------------- /src/debug_print.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_PRINT_HPP 2 | #define DEBUG_PRINT_HPP 3 | 4 | // Logging, used for debugging purposes. 5 | 6 | #include 7 | #include 8 | 9 | #include "format.hpp" 10 | 11 | struct log_t 12 | { 13 | FILE* stream; 14 | std::mutex mutex; 15 | 16 | void write(std::string const& msg) 17 | { 18 | std::lock_guard lock(mutex); 19 | std::fputs(msg.c_str(), stream); 20 | std::fputc('\n', stream); 21 | std::fflush(stream); 22 | } 23 | }; 24 | 25 | inline log_t stdout_log = { stdout }; 26 | inline log_t stderr_log = { stderr }; 27 | 28 | #define DEBUG_PRINT 29 | 30 | #ifdef DEBUG_PRINT 31 | #define debug_printf(...) std::printf(__VA_ARGS__) 32 | #define dprint(stream, ...) ((void)((stream) ? ((stream)->write(::ezcat(" ",__VA_ARGS__)), 0) : 0)) 33 | #else 34 | #define debug_printf(...) ((void)0) 35 | #define dprint(...) ((void)0) 36 | #endif 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /lib/uvec.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This file contains functions for manipulating values of type U{}. 8 | 9 | // Inserts the size of 'vec' at the beginning of 'vec', 10 | // represented as a value of type U. 11 | ct fn uvec_prefix_size(U{} vec) U{} 12 | push(vec, len(vec), 0) 13 | return vec 14 | 15 | // Inserts the size of 'vec' at the beginning of 'vec', 16 | // represented as a value of type UU. 17 | ct fn uvec_prefix_size_uu(U{} vec) U{} 18 | UU size = UU(len(vec)) 19 | push(vec, size.a, 0) 20 | push(vec, size.b, 1) 21 | return vec 22 | 23 | // Reverses the contents of 'vec': 24 | ct fn uvec_reverse(U{} vec) 25 | U{} reversed 26 | for Int i = 0; i < len(vec); i += 1 27 | push(reversed, pop(vec)) 28 | return reversed 29 | 30 | -------------------------------------------------------------------------------- /src/define.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DEFINE_HPP 2 | #define DEFINE_HPP 3 | 4 | #include 5 | 6 | #include "decl.hpp" 7 | #include "pstring.hpp" 8 | #include "asm_proc.hpp" 9 | #include "mods.hpp" 10 | 11 | const_ht define_const(lpstring_t at, std::string_view name, asm_proc_t&& proc, 12 | defined_group_data_t const& d, bool omni, mod_flags_t flags); 13 | 14 | const_ht define_ct(lpstring_t at, std::string_view name, std::uint8_t value); 15 | const_ht define_ct(global_t& global, lpstring_t at, std::uint8_t value); 16 | const_ht define_ct_int(global_t& global, lpstring_t at, type_t const& type, unsigned value); 17 | 18 | const_ht define_ct(global_t& global, lpstring_t at, std::uint8_t const* data, std::size_t length); 19 | const_ht define_ct(global_t& global, lpstring_t at, std::uint16_t const* data, std::size_t length); 20 | const_ht define_ct(global_t& global, lpstring_t at, std::int16_t const* data, std::size_t length); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /examples/pbz/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This examples loads CHRRAM using PBZ. 8 | 9 | data /pbz 10 | [] chr 11 | file(pbz, "bg.png") 12 | 13 | mode main() 14 | palette = example_palette 15 | ppu_upload_palette() 16 | 17 | // Setup the nametable: 18 | ppu_reset_addr($2000) 19 | for U y = 0; y < 30; y += 1 20 | for U x = 0; x < 32; x += 1 21 | {PPUDATA}((x & %1111) + ((y & %1111) << 4)) 22 | for U a = 0; a < 64; a += 1 23 | {PPUDATA}(0) 24 | 25 | // Load PBZ: 26 | ppu_set_addr($0000) 27 | ppu_upload_pbz.chunks(ppu_upload_pbz.chunks(@chr, 0), 0) 28 | 29 | {PPUCTRL}(PPUCTRL_NMI_ON | PPUCTRL_SPR_PT_1000) 30 | {PPUMASK}(PPUMASK_ON) 31 | while true 32 | ppu_reset_scroll(0, 0) 33 | nmi 34 | -------------------------------------------------------------------------------- /src/lodepng/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2018 Lode Vandevenne 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | 22 | -------------------------------------------------------------------------------- /src/multi.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MULTI_HPP 2 | #define MULTI_HPP 3 | 4 | #include "type_name.hpp" 5 | #include "ir_decl.hpp" 6 | 7 | struct multi_lt_info_t 8 | { 9 | explicit multi_lt_info_t(ssa_ht h, bool flip = false); 10 | 11 | type_name_t lt; 12 | type_name_t rt; 13 | 14 | int lwhole; 15 | int rwhole; 16 | int minwhole; 17 | int maxwhole; 18 | int sbcwhole; // This is used to implement the multi-byte subtraction. 19 | 20 | int lfrac; 21 | int rfrac; 22 | int maxfrac; 23 | 24 | int lsize; 25 | int rsize; 26 | 27 | // Offsets into the node's input array. 28 | int lstart; 29 | int rstart; 30 | 31 | int loffset() const { return lstart + lfrac; } 32 | int roffset() const { return rstart + rfrac; } 33 | 34 | bool lsigned; 35 | bool rsigned; 36 | 37 | bool validl(int i) const; 38 | bool validr(int i) const; 39 | 40 | bool signedl(int i) const; 41 | bool signedr(int i) const; 42 | }; 43 | 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /lib/charmap/ascii.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // A charmap for ASCII text 8 | charmap ascii( 9 | "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" 10 | "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" 11 | " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f", 12 | '\x00') 13 | : stows /ascii 14 | 15 | // Returns the length of a null-terminated ascii string, 16 | // not counting the null terminator. 17 | fn ascii_strlen(PPP/ascii str) U 18 | do for U i = 0; i; i += 1 19 | if str[i] == ascii.sentinel 20 | return i 21 | return $FF 22 | 23 | // Same as above, but returns a UU. 24 | fn ascii_strlen_uu(PPP/ascii str) UU 25 | do for UU i = 0; i; i += 1 26 | if str[0] == ascii.sentinel 27 | return i 28 | str += 1 29 | return $FFFF 30 | -------------------------------------------------------------------------------- /src/flat/impl/container_traits.hpp: -------------------------------------------------------------------------------- 1 | // Copyright Pubby 2016 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // http://www.boost.org/LICENSE_1_0.txt 4 | 5 | using container_type = Container; 6 | using key_type = Key; 7 | using size_type = typename container_type::size_type; 8 | using difference_type = typename container_type::difference_type; 9 | using value_type = typename container_type::value_type; 10 | using iterator 11 | = impl::flat_iterator< 12 | typename container_type::iterator, 13 | impl::dummy_iterator>; 14 | using const_iterator 15 | = impl::flat_iterator< 16 | typename container_type::const_iterator, 17 | iterator>; 18 | using reverse_iterator 19 | = impl::flat_iterator< 20 | typename container_type::reverse_iterator, 21 | impl::dummy_iterator>; 22 | using const_reverse_iterator 23 | = impl::flat_iterator< 24 | typename container_type::const_reverse_iterator, 25 | reverse_iterator>; 26 | -------------------------------------------------------------------------------- /lib/audio/apu.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // Code for interfacing with the NES's APU. 8 | 9 | /////////////////////////////////////////////////////////////////////////////// 10 | // REGISTERS ////////////////////////////////////////////////////////////////// 11 | /////////////////////////////////////////////////////////////////////////////// 12 | 13 | ct AA SQ1_VOL = $4000 14 | ct AA SQ1_SWEEP = $4001 15 | ct AA SQ1_LO = $4002 16 | ct AA SQ1_HI = $4003 17 | ct AA SQ2_VOL = $4004 18 | ct AA SQ2_SWEEP = $4005 19 | ct AA SQ2_LO = $4006 20 | ct AA SQ2_HI = $4007 21 | ct AA TRI_LINEAR = $4008 22 | ct AA TRI_LO = $400A 23 | ct AA TRI_HI = $400B 24 | ct AA NOISE_VOL = $400C 25 | ct AA NOISE_LO = $400E 26 | ct AA NOISE_HI = $400F 27 | ct AA DMC_FREQ = $4010 28 | ct AA DMC_RAW = $4011 29 | ct AA DMC_START = $4012 30 | ct AA DMC_LEN = $4013 31 | ct AA SND_CHN = $4015 32 | 33 | -------------------------------------------------------------------------------- /src/bitset_tests.cpp: -------------------------------------------------------------------------------- 1 | #include "catch/catch.hpp" 2 | #include "bitset.hpp" 3 | 4 | #include 5 | #include 6 | 7 | void test_fill(bitset_t& bs, unsigned start, unsigned size) 8 | { 9 | bs.clear_all(); 10 | REQUIRE(bs.all_clear()); 11 | bitset_set_n(bs.size(), bs.data(), start, size); 12 | 13 | for(unsigned bit = 0; bit < bs.size_in_bits(); ++bit) 14 | { 15 | INFO("bit = " << bit); 16 | REQUIRE(bs.test(bit) == (bit >= start && bit < start + size)); 17 | } 18 | } 19 | 20 | TEST_CASE("bitset_set_n", "[bitset]") 21 | { 22 | bitset_t bs(8); 23 | REQUIRE(bs.all_clear()); 24 | 25 | test_fill(bs, 0, 64); 26 | test_fill(bs, 1, 64); 27 | test_fill(bs, 110, 64); 28 | test_fill(bs, 200, 64); 29 | 30 | test_fill(bs, 0, 7); 31 | test_fill(bs, 1, 7); 32 | test_fill(bs, 110, 7); 33 | test_fill(bs, 200, 7); 34 | 35 | test_fill(bs, 0, 300); 36 | test_fill(bs, 1, 300); 37 | test_fill(bs, 110, 300); 38 | test_fill(bs, 200, 300); 39 | 40 | test_fill(bs, 0, 0); 41 | test_fill(bs, 200, 0); 42 | } 43 | 44 | -------------------------------------------------------------------------------- /examples/logging/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This shows how to use 'lua_log.fab' to log files. 8 | // To test this, you'll have to import 'lua_log.lua' into FCEUX or Mesen. 9 | 10 | nmi main_nmi() 11 | ppu_upload_oam_poll_pads(0) 12 | {PPUMASK}(PPUMASK_BG_ON) 13 | 14 | mode main() 15 | : nmi main_nmi 16 | palette = example_palette 17 | ppu_upload_palette() 18 | 19 | // Clear the screen 20 | {PPUSTATUS}() 21 | {PPUADDR}($20) 22 | {PPUADDR}($00) 23 | for UU i = 0; i != 1024; i += 1 24 | {PPUDATA}(0) 25 | 26 | // We'll keep track of how many times we pressed a button: 27 | UU pressed = 0 28 | 29 | {PPUCTRL}(PPUCTRL_NMI_ON) 30 | while true 31 | nmi 32 | update_pads() 33 | if pads[0].pressed 34 | // Send a log message whenever a button is pressed: 35 | pressed += 1 36 | log_2(@"Buttons pressed: %d, Total: %d\n"ascii, UU(pads[0].pressed), pressed) 37 | 38 | -------------------------------------------------------------------------------- /examples/build_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x #echo on 3 | 4 | ../nesfab cnrom/cnrom.cfg 5 | ../nesfab hang_glider/hang_glider.cfg 6 | ../nesfab hello_world/hello_world.cfg 7 | ../nesfab maze/maze.cfg 8 | ../nesfab mmc1/mmc1.cfg 9 | ../nesfab mmc3/mmc3.cfg 10 | ../nesfab scanline_irq/scanline_irq.cfg 11 | ../nesfab text/text.cfg 12 | ../nesfab objects/objects.cfg 13 | ../nesfab zapper/zapper.cfg 14 | ../nesfab counter/counter.cfg 15 | ../nesfab fade/fade.cfg 16 | ../nesfab sound_effects/sound_effects.cfg 17 | ../nesfab trig/trig.cfg 18 | ../nesfab pbz/pbz.cfg 19 | ../nesfab mapfab/mapfab.cfg 20 | ../nesfab platformer/platformer.cfg 21 | ../nesfab scrolling_8_way/scrolling_8_way.cfg 22 | ../nesfab rope/rope.cfg 23 | ../nesfab billiards/billiards.cfg 24 | ../nesfab meta_meta_tiles/meta_meta_tiles.cfg 25 | ../nesfab fn_ptr/fn_ptr.cfg 26 | ../nesfab animation/animation.cfg 27 | ../nesfab music/music.cfg 28 | ../nesfab mouse/mouse.cfg 29 | ../nesfab 4_player/4_player.cfg 30 | ../nesfab mmc5/mmc5.cfg 31 | ../nesfab rainbow/rainbow.cfg 32 | ../nesfab wifi/wifi.cfg 33 | ../nesfab logging/logging.cfg 34 | ../nesfab flash_save/flash_save.cfg 35 | ../nesfab collision/collision.cfg 36 | -------------------------------------------------------------------------------- /examples/fn_ptr/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // Shows how to use fn pointers. 8 | 9 | vars 10 | UU pitch = 1000 11 | 12 | // Our fn pointer: 13 | Fn.update update_fn = Fn.update() // Initialize to null. 14 | 15 | nmi main_nmi() 16 | if ready 17 | if update_fn 18 | update_fn() // Call the function pointer 19 | 20 | // Sends 'pitch' variable to the APU, emitting sound: 21 | {$4015}(%100) 22 | {$4008}($FF) 23 | {$400A}(pitch.a) 24 | {$400B}(pitch.b & %111) 25 | poll_pads() 26 | 27 | mode main() 28 | : nmi main_nmi 29 | {PPUCTRL}(%10000000) 30 | while true 31 | update_pads() 32 | if pads[0].pressed & BUTTON_A 33 | update_fn = @(update.rise) // Assign function pointer. 34 | else if pads[0].pressed & BUTTON_B 35 | update_fn = @(update.fall) // Assign function pointer. 36 | nmi 37 | 38 | fn update.rise() 39 | pitch += 10 40 | 41 | fn update.fall() 42 | pitch -= 10 43 | -------------------------------------------------------------------------------- /src/cg_liveness.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CG_LIVENESS_HPP 2 | #define CG_LIVENESS_HPP 3 | 4 | // A self-contained implementation of live variable analysis. 5 | 6 | #include "array_pool.hpp" 7 | #include "bitset.hpp" 8 | #include "ir_decl.hpp" 9 | #include "thread" 10 | 11 | class fn_t; 12 | 13 | namespace liveness_impl 14 | { 15 | extern TLS array_pool_t bitset_pool; 16 | extern TLS unsigned set_size; 17 | extern TLS unsigned reserved_size; 18 | } 19 | 20 | inline unsigned live_set_size() { return liveness_impl::set_size; } 21 | 22 | void calc_ssa_liveness(ssa_ht node); // only does a single node 23 | unsigned calc_ssa_liveness(ir_t const& ir); 24 | unsigned calc_ssa_liveness(ir_t const& ir, unsigned pool_size); 25 | 26 | void clear_liveness_for(ir_t const& ir, ssa_ht node); 27 | 28 | // If 'range' intersects 'def'. 29 | bool live_at_def(ssa_ht range, ssa_ht def); 30 | 31 | bool live_at_any_def(ssa_ht range, ssa_ht const* defs_begin, 32 | ssa_ht const* defs_end); 33 | 34 | bool live_range_overlap(ssa_ht a, ssa_ht b); 35 | 36 | // A rough approximation of how much a live range overlaps with all others. 37 | std::size_t live_range_busyness(ir_t& ir, ssa_ht h); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /examples/wifi/server/node_modules/ws/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011 Einar Otto Stangvik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/mapper_30/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This examples loads CHRRAM using PBZ. 8 | 9 | data /pbz 10 | [] chr_0 11 | file(pbz, "bg.png") 12 | [] chr_1 13 | file(pbz, "bg2.png") 14 | 15 | mode main() 16 | palette = example_palette 17 | ppu_upload_palette() 18 | 19 | // Setup the nametable: 20 | ppu_reset_addr($2000) 21 | for U y = 0; y < 30; y += 1 22 | for U x = 0; x < 32; x += 1 23 | {PPUDATA}((x & %1111) + ((y & %1111) << 4)) 24 | for U a = 0; a < 64; a += 1 25 | {PPUDATA}(0) 26 | 27 | // Load PBZ: 28 | state(M30_CHR(0)) 29 | ppu_set_addr($0000) 30 | ppu_upload_pbz.chunks(ppu_upload_pbz.chunks(@chr_0, 0), 0) 31 | state(M30_CHR(1)) 32 | ppu_set_addr($0000) 33 | ppu_upload_pbz.chunks(ppu_upload_pbz.chunks(@chr_1, 0), 0) 34 | 35 | {PPUCTRL}(PPUCTRL_NMI_ON | PPUCTRL_SPR_PT_1000) 36 | {PPUMASK}(PPUMASK_ON) 37 | while true 38 | state(M30_CHR((nmi_counter >> 4) & %1)) 39 | ppu_reset_scroll(0, 0) 40 | nmi 41 | -------------------------------------------------------------------------------- /src/o_type.cpp: -------------------------------------------------------------------------------- 1 | #include "o_type.hpp" 2 | 3 | #include "globals.hpp" 4 | #include "ir.hpp" 5 | #include "ir_util.hpp" 6 | 7 | bool o_remove_index_types(log_t* log, ir_t& ir) 8 | { 9 | bool updated = false; 10 | 11 | for(cfg_node_t& cfg_node : ir) 12 | for(ssa_ht ssa_it = cfg_node.ssa_begin(); ssa_it; ++ssa_it) 13 | { 14 | ssa_node_t& ssa_node = *ssa_it; 15 | 16 | if(is_index(ssa_node.type().name())) 17 | { 18 | ssa_it->set_type(to_u(ssa_node.type().name())); 19 | updated = true; 20 | } 21 | } 22 | 23 | for(cfg_node_t& cfg_node : ir) 24 | for(ssa_ht ssa_it = cfg_node.ssa_begin(); ssa_it; ++ssa_it) 25 | { 26 | unsigned const size = ssa_it->input_size(); 27 | for(unsigned i = 0; i < size; i += 1) 28 | { 29 | ssa_value_t input = ssa_it->input(i); 30 | type_name_t const tn = input.type().name(); 31 | 32 | if(is_index(tn) && input.is_num()) 33 | { 34 | input.set_num_type_name(to_u(tn)); 35 | ssa_it->link_change_input(i, input); 36 | updated = true; 37 | } 38 | } 39 | } 40 | 41 | return updated; 42 | } 43 | -------------------------------------------------------------------------------- /src/object_pool.hpp: -------------------------------------------------------------------------------- 1 | #ifndef OBJECT_POOL_HPP 2 | #define OBJECT_POOL_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace bc = ::boost::container; 10 | 11 | // A basic memory pool that supports alloc and free. 12 | // NOTE: Objects are not destructed when 'free' is called! 13 | template 14 | class object_pool_t 15 | { 16 | public: 17 | object_pool_t() = default; 18 | object_pool_t(object_pool_t const&) = delete; 19 | object_pool_t& operator=(object_pool_t const&) = delete; 20 | 21 | using int_type = Int; 22 | private: 23 | bc::deque storage; 24 | std::vector free_list; 25 | public: 26 | T& alloc() 27 | { 28 | if(free_list.empty()) 29 | return storage.emplace_back(); 30 | else 31 | { 32 | T& ret = *free_list.back(); 33 | free_list.pop_back(); 34 | return ret; 35 | } 36 | } 37 | 38 | void free(T& t) 39 | { 40 | free_list.push_back(&t); 41 | } 42 | 43 | void clear() noexcept 44 | { 45 | storage.clear(); 46 | free_list.clear(); 47 | } 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /examples/cnrom/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // Plays an animation using CNROM 8 | 9 | mode main() 10 | // Set the palette: 11 | palette = example_palette 12 | ppu_upload_palette() 13 | 14 | // Setup the nametable: 15 | ppu_reset_addr($2000) 16 | for U y = 0; y < 30; y += 1 17 | for U x = 0; x < 32; x += 1 18 | {PPUDATA}((x & %1111) + ((y & %1111) << 4)) 19 | for U a = 0; a < 64; a += 1 20 | {PPUDATA}(0) 21 | ppu_set_scroll(0, 0) 22 | 23 | // Animate the CHRROM: 24 | U frame = 0 25 | state(frame) 26 | {PPUCTRL}(PPUCTRL_NMI_ON) 27 | {PPUMASK}(PPUMASK_ON) 28 | while true 29 | state(frame) 30 | frame += 1 31 | frame &= %11 32 | wait(8) 33 | 34 | // Define the tileset (commonly called CHR): 35 | chrrom 36 | file(fmt, "chr0.png") 37 | file(fmt, "chr0.png") 38 | file(fmt, "chr1.png") 39 | file(fmt, "chr1.png") 40 | file(fmt, "chr2.png") 41 | file(fmt, "chr2.png") 42 | file(fmt, "chr3.png") 43 | file(fmt, "chr3.png") 44 | -------------------------------------------------------------------------------- /examples/mmc1/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | mode main() 8 | // Set the palette: 9 | palette = example_palette 10 | ppu_upload_palette() 11 | 12 | // Set the mapper to a known state: 13 | state(%10000) 14 | mmc1_set_chr_0(0) 15 | mmc1_set_chr_1(0) 16 | 17 | // Setup the nametable: 18 | ppu_reset_addr($2000) 19 | for U y = 0; y < 30; y += 1 20 | for U x = 0; x < 32; x += 1 21 | {PPUDATA}((x & %1111) + ((y & %1111) << 4)) 22 | for U a = 0; a < 64; a += 1 23 | {PPUDATA}(0) 24 | ppu_set_scroll(0, 0) 25 | 26 | {PPUCTRL}(PPUCTRL_NMI_ON) 27 | {PPUMASK}(PPUMASK_BG_ON) 28 | U counter = 0 29 | while true 30 | mmc1_set_chr_0(counter) 31 | counter += 2 32 | counter &= %110 33 | wait(8) 34 | 35 | // Define the tileset (commonly called CHR): 36 | chrrom 37 | file(fmt, "chr0.png") 38 | file(fmt, "chr0.png") 39 | file(fmt, "chr1.png") 40 | file(fmt, "chr1.png") 41 | file(fmt, "chr2.png") 42 | file(fmt, "chr2.png") 43 | file(fmt, "chr3.png") 44 | file(fmt, "chr3.png") 45 | -------------------------------------------------------------------------------- /src/lodepng/Makefile: -------------------------------------------------------------------------------- 1 | # This makefile only makes the unit test, benchmark and pngdetail and showpng 2 | # utilities. It does not make the PNG codec itself as shared or static library. 3 | # That is because: 4 | # LodePNG itself has only 1 source file (lodepng.cpp, can be renamed to 5 | # lodepng.c) and is intended to be included as source file in other projects and 6 | # their build system directly. 7 | 8 | 9 | CC ?= gcc 10 | CXX ?= g++ 11 | 12 | override CFLAGS := -W -Wall -Wextra -ansi -pedantic -O3 -Wno-unused-function $(CFLAGS) 13 | override CXXFLAGS := -W -Wall -Wextra -ansi -pedantic -O3 $(CXXFLAGS) 14 | 15 | all: unittest benchmark pngdetail showpng 16 | 17 | %.o: %.cpp 18 | @mkdir -p `dirname $@` 19 | $(CXX) -I ./ $(CXXFLAGS) -c $< -o $@ 20 | 21 | unittest: lodepng.o lodepng_util.o lodepng_unittest.o 22 | $(CXX) $^ $(CXXFLAGS) -o $@ 23 | 24 | benchmark: lodepng.o lodepng_benchmark.o 25 | $(CXX) $^ $(CXXFLAGS) -lSDL2 -o $@ 26 | 27 | pngdetail: lodepng.o lodepng_util.o pngdetail.o 28 | $(CXX) $^ $(CXXFLAGS) -o $@ 29 | 30 | showpng: lodepng.o examples/example_sdl.o 31 | $(CXX) -I ./ $^ $(CXXFLAGS) -lSDL2 -o $@ 32 | 33 | clean: 34 | rm -f unittest benchmark pngdetail showpng lodepng_unittest.o lodepng_benchmark.o lodepng.o lodepng_util.o pngdetail.o examples/example_sdl.o 35 | -------------------------------------------------------------------------------- /examples/mmc3/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This examples uses scanline IRQs to alter the scroll mid-frame. 8 | 9 | fn split() 10 | {PPUSCROLL}(rand() & %11) 11 | {PPUSCROLL}(0) 12 | mmc3_timer(10) 13 | 14 | irq main_irq() 15 | split() 16 | 17 | mode main() 18 | : irq main_irq 19 | palette = example_palette 20 | ppu_upload_palette() 21 | 22 | // Setup the nametable: 23 | ppu_reset_addr($2000) 24 | for U y = 0; y < 30; y += 1 25 | for U x = 0; x < 32; x += 1 26 | {PPUDATA}((x & %1111) + ((y & %1111) << 4)) 27 | for U a = 0; a < 64; a += 1 28 | {PPUDATA}(0) 29 | 30 | // Setup MMC3: 31 | {MMC3_MIRRORING}(1) 32 | {MMC3_BANK_SELECT, MMC3_BANK_DATA}(0, %00) 33 | {MMC3_BANK_SELECT, MMC3_BANK_DATA}(1, %10) 34 | 35 | // Enable interrupts: 36 | {MMC3_IRQ_DISABLE}(0) 37 | irq true 38 | 39 | {PPUCTRL}(PPUCTRL_NMI_ON | PPUCTRL_SPR_PT_1000) 40 | while true 41 | nmi 42 | ppu_reset_scroll(0, 0) 43 | split() 44 | {PPUMASK}(PPUMASK_BG_ON | PPUMASK_NO_CLIP) 45 | 46 | chrrom 47 | file(fmt, "bg.png") 48 | -------------------------------------------------------------------------------- /src/span.cpp: -------------------------------------------------------------------------------- 1 | #include "span.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "format.hpp" 8 | 9 | std::ostream& operator<<(std::ostream& o, span_t span) 10 | { 11 | o << "span_t{ $" << to_hex_string(span.addr) << ", " << span.size << " }"; 12 | return o; 13 | } 14 | 15 | span_t aligned(span_t span, std::uint16_t size, std::uint16_t alignment) 16 | { 17 | if(!alignment) 18 | alignment = 1; 19 | // Must be power of 2. 20 | assert((alignment & (alignment - 1)) == 0); 21 | 22 | std::uint16_t const add = (alignment - span.addr) & (alignment - 1); 23 | span_t const ret = { .addr = span.addr + add, .size = size }; 24 | assert(ret.addr % alignment == 0); 25 | return span.contains(ret) ? ret : span_t{}; 26 | } 27 | 28 | span_t aligned_reverse(span_t span, std::uint16_t size, std::uint16_t alignment) 29 | { 30 | if(!alignment) 31 | alignment = 1; 32 | // Must be power of 2. 33 | assert((alignment & (alignment - 1)) == 0); 34 | 35 | std::uint16_t const addr = span.end() - size; 36 | std::uint16_t const sub = addr & (alignment - 1); 37 | span_t const ret = { .addr = addr - sub, .size = size }; 38 | assert(ret.addr % alignment == 0); 39 | return span.contains(ret) ? ret : span_t{}; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/span.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SPAN_HPP 2 | #define SPAN_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // Spans are intrevals representing memory regions. 9 | 10 | template 11 | struct generic_span_t 12 | { 13 | T addr; 14 | T size; 15 | 16 | using value_type = T; 17 | 18 | constexpr std::uint64_t end() const { return addr + size; } 19 | constexpr explicit operator bool() const { return size; } 20 | constexpr auto operator<=>(generic_span_t const&) const = default; 21 | 22 | constexpr bool contains(T addr) const { return addr >= this->addr && end() > addr; } 23 | constexpr bool contains(generic_span_t const& o) const { return o.addr >= addr && o.end() <= end(); } 24 | constexpr bool intersects(generic_span_t const& o) const { return o.end() > addr && end() > o.addr; } 25 | }; 26 | 27 | using span_t = generic_span_t; 28 | 29 | template 30 | constexpr generic_span_t offset_span(generic_span_t span, int amount) { span.addr += amount; return span; } 31 | 32 | std::ostream& operator<<(std::ostream& o, span_t span); 33 | 34 | span_t aligned(span_t span, std::uint16_t size, std::uint16_t alignment); 35 | span_t aligned_reverse(span_t span, std::uint16_t size, std::uint16_t alignment); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /examples/mapper_189/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This examples uses scanline IRQs to alter the scroll mid-frame. 8 | 9 | fn split() 10 | {PPUSCROLL}(rand() & %11) 11 | {PPUSCROLL}(0) 12 | mmc3_timer(10) 13 | 14 | irq main_irq() 15 | split() 16 | 17 | mode main() 18 | : irq main_irq 19 | palette = example_palette 20 | ppu_upload_palette() 21 | 22 | // Setup the nametable: 23 | ppu_reset_addr($2000) 24 | for U y = 0; y < 30; y += 1 25 | for U x = 0; x < 32; x += 1 26 | {PPUDATA}((x & %1111) + ((y & %1111) << 4)) 27 | for U a = 0; a < 64; a += 1 28 | {PPUDATA}(0) 29 | 30 | // Setup MMC3: 31 | {MMC3_MIRRORING}(1) 32 | {MMC3_BANK_SELECT, MMC3_BANK_DATA}(0, %00) 33 | {MMC3_BANK_SELECT, MMC3_BANK_DATA}(1, %10) 34 | 35 | // Enable interrupts: 36 | {MMC3_IRQ_DISABLE}(0) 37 | irq true 38 | 39 | {PPUCTRL}(PPUCTRL_NMI_ON | PPUCTRL_SPR_PT_1000) 40 | while true 41 | nmi 42 | ppu_reset_scroll(0, 0) 43 | split() 44 | {PPUMASK}(PPUMASK_BG_ON | PPUMASK_NO_CLIP) 45 | 46 | chrrom 47 | file(fmt, "bg.png") 48 | -------------------------------------------------------------------------------- /examples/scanline_irq/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This examples uses scanline IRQs to alter the scroll mid-frame. 8 | 9 | fn split() 10 | {PPUSCROLL}(rand() & %11) 11 | {PPUSCROLL}(0) 12 | mmc3_timer(10) 13 | 14 | irq main_irq() 15 | split() 16 | 17 | mode main() 18 | : irq main_irq 19 | palette = example_palette 20 | ppu_upload_palette() 21 | 22 | // Setup the nametable: 23 | ppu_reset_addr($2000) 24 | for U y = 0; y < 30; y += 1 25 | for U x = 0; x < 32; x += 1 26 | {PPUDATA}((x & %1111) + ((y & %1111) << 4)) 27 | for U a = 0; a < 64; a += 1 28 | {PPUDATA}(0) 29 | 30 | // Setup MMC3: 31 | {MMC3_MIRRORING}(1) 32 | {MMC3_BANK_SELECT, MMC3_BANK_DATA}(0, %00) 33 | {MMC3_BANK_SELECT, MMC3_BANK_DATA}(1, %10) 34 | 35 | // Enable interrupts: 36 | {MMC3_IRQ_DISABLE}(0) 37 | irq true 38 | 39 | {PPUCTRL}(PPUCTRL_NMI_ON | PPUCTRL_SPR_PT_1000) 40 | while true 41 | nmi 42 | ppu_reset_scroll(0, 0) 43 | split() 44 | {PPUMASK}(PPUMASK_BG_ON | PPUMASK_NO_CLIP) 45 | 46 | chrrom 47 | file(fmt, "bg.png") 48 | -------------------------------------------------------------------------------- /examples/wifi/server/node_modules/ws/lib/limiter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const kDone = Symbol('kDone'); 4 | const kRun = Symbol('kRun'); 5 | 6 | /** 7 | * A very simple job queue with adjustable concurrency. Adapted from 8 | * https://github.com/STRML/async-limiter 9 | */ 10 | class Limiter { 11 | /** 12 | * Creates a new `Limiter`. 13 | * 14 | * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed 15 | * to run concurrently 16 | */ 17 | constructor(concurrency) { 18 | this[kDone] = () => { 19 | this.pending--; 20 | this[kRun](); 21 | }; 22 | this.concurrency = concurrency || Infinity; 23 | this.jobs = []; 24 | this.pending = 0; 25 | } 26 | 27 | /** 28 | * Adds a job to the queue. 29 | * 30 | * @param {Function} job The job to run 31 | * @public 32 | */ 33 | add(job) { 34 | this.jobs.push(job); 35 | this[kRun](); 36 | } 37 | 38 | /** 39 | * Removes a job from the queue and runs it if possible. 40 | * 41 | * @private 42 | */ 43 | [kRun]() { 44 | if (this.pending === this.concurrency) return; 45 | 46 | if (this.jobs.length) { 47 | const job = this.jobs.shift(); 48 | 49 | this.pending++; 50 | job(this[kDone]); 51 | } 52 | } 53 | } 54 | 55 | module.exports = Limiter; 56 | -------------------------------------------------------------------------------- /examples/wifi/server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rainbow-chat", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "rainbow-chat", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "ws": "^7.4.6" 13 | } 14 | }, 15 | "node_modules/ws": { 16 | "version": "7.4.6", 17 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", 18 | "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", 19 | "engines": { 20 | "node": ">=8.3.0" 21 | }, 22 | "peerDependencies": { 23 | "bufferutil": "^4.0.1", 24 | "utf-8-validate": "^5.0.2" 25 | }, 26 | "peerDependenciesMeta": { 27 | "bufferutil": { 28 | "optional": true 29 | }, 30 | "utf-8-validate": { 31 | "optional": true 32 | } 33 | } 34 | } 35 | }, 36 | "dependencies": { 37 | "ws": { 38 | "version": "7.4.6", 39 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", 40 | "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", 41 | "requires": {} 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/hang_glider/main_menu.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | ct U[25] main_menu_palette = U[25]( 8 | $11, $26, $39, 9 | $13, $21, $3B, 10 | $15, $23, $31, 11 | $17, $25, $33, 12 | 13 | $02, $14, $26, 14 | $04, $16, $28, 15 | $06, $18, $2A, 16 | $08, $1A, $2C, 17 | 18 | $0F) 19 | 20 | data /rlz 21 | [] main_menu_nt 22 | file(rlz, "title.nam") 23 | 24 | nmi main_menu_nmi() 25 | {PPUSCROLL}(0) 26 | {PPUSCROLL}(0) 27 | {PPUMASK}(PPUMASK_BG_ON) 28 | ppu_upload_oam_poll_pads(0) 29 | puf.process(PUF_DEFAULT) 30 | 31 | mode main() 32 | : nmi main_menu_nmi 33 | {PPUCTRL}(0) 34 | {PPUMASK}(0) 35 | 36 | puf.init(system) 37 | puf.play_track(puf_track_title) 38 | 39 | palette = main_menu_palette 40 | ppu_upload_palette() 41 | 42 | {PPUSTATUS}() 43 | {PPUADDR}($20) 44 | {PPUADDR}($00) 45 | ppu_upload_rlz(@main_menu_nt) 46 | 47 | {PPUCTRL}(PPUCTRL_NMI_ON | PPUCTRL_BG_PT_1000) 48 | 49 | while true 50 | update_pads() 51 | rand() 52 | if pads[0].pressed & BUTTON_START 53 | goto mode game() 54 | : preserves 55 | nmi 56 | 57 | -------------------------------------------------------------------------------- /src/o_ai.hpp: -------------------------------------------------------------------------------- 1 | #ifndef O_ABSTRACT_INTERPRET_HPP 2 | #define O_ABSTRACT_INTERPRET_HPP 3 | 4 | // This pass implements abstract interpretation to fold constants, 5 | // thread jumps, and simplify conditional expressions. 6 | 7 | // https://en.wikipedia.org/wiki/Abstract_interpretation 8 | 9 | // The model is contained in 'constraints.hpp', 10 | // and is defined as the intersection of an interval and a bitset. 11 | // Widening is used to ensure termination. 12 | 13 | // Conditionals are handled with something I call "tracing". 14 | // The pass tries to prove which values are known inside specific branches, 15 | // temporarily adding SSA nodes to track this. 16 | // At the end of the pass, these temporary nodes are deleted. 17 | 18 | #include 19 | 20 | #include "debug_print.hpp" 21 | #include "ir_decl.hpp" 22 | #include "constraints.hpp" 23 | #include "thread.hpp" 24 | 25 | struct ai_prep_t 26 | { 27 | std::unique_ptr constraints; 28 | }; 29 | 30 | extern TLS std::vector ai_prep_vec; 31 | 32 | inline ai_prep_t& ai_prep(ssa_ht ssa) { assert(ssa.id < ai_prep_vec.size()); return ai_prep_vec[ssa.id]; } 33 | inline void reset_ai_prep() { ai_prep_vec.clear(); } 34 | inline void resize_ai_prep() { ai_prep_vec.resize(ssa_pool::array_size()); } 35 | 36 | bool o_abstract_interpret(log_t* os, ir_t& ir, bool byteified); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/multi.cpp: -------------------------------------------------------------------------------- 1 | #include "multi.hpp" 2 | 3 | #include "ir.hpp" 4 | 5 | multi_lt_info_t::multi_lt_info_t(ssa_ht h, bool flip) 6 | { 7 | lt = type_name_t(h->input(flip).whole()); 8 | rt = type_name_t(h->input(!flip).whole()); 9 | 10 | lwhole = whole_bytes(lt); 11 | rwhole = whole_bytes(rt); 12 | minwhole = std::min(lwhole, rwhole); 13 | maxwhole = std::max(lwhole, rwhole); 14 | sbcwhole = minwhole; 15 | 16 | lfrac = frac_bytes(lt); 17 | rfrac = frac_bytes(rt); 18 | maxfrac = std::max(lfrac, rfrac); 19 | 20 | lsize = lwhole + lfrac; 21 | rsize = rwhole + rfrac; 22 | 23 | // Offsets into the node's input array. 24 | lstart = 2 + (flip ? rsize : 0); 25 | rstart = 2 + (flip ? 0 : lsize); 26 | assert(lstart != rstart); 27 | 28 | lsigned = is_signed(lt); 29 | rsigned = is_signed(rt); 30 | }; 31 | 32 | bool multi_lt_info_t::validl(int i) const 33 | { 34 | return i >= lstart && i < lstart + lsize; 35 | }; 36 | 37 | bool multi_lt_info_t::validr(int i) const 38 | { 39 | return i >= rstart && i < rstart + rsize; 40 | }; 41 | 42 | bool multi_lt_info_t::signedl(int i) const 43 | { 44 | assert(validl(i)); 45 | return lsigned && i == lstart + lsize - 1; 46 | } 47 | 48 | bool multi_lt_info_t::signedr(int i) const 49 | { 50 | assert(validr(i)); 51 | return rsigned && i == rstart + rsize - 1; 52 | } 53 | -------------------------------------------------------------------------------- /examples/fade/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This examples shows the palette fading in and out. 8 | 9 | data /palettes 10 | [] my_palette 11 | (example_palette) 12 | 13 | nmi main_nmi() 14 | ppu_upload_palette() 15 | ppu_set_scroll(0, 0) 16 | {PPUMASK}(PPUMASK_BG_ON | PPUMASK_NO_CLIP) 17 | {PPUCTRL}(PPUCTRL_NMI_ON) 18 | 19 | mode main() 20 | : nmi main_nmi 21 | load_palette(@my_palette) 22 | 23 | // Setup the nametable: 24 | ppu_reset_addr($2000) 25 | for U y = 0; y < 30; y += 1 26 | for U x = 0; x < 32; x += 1 27 | {PPUDATA}((x & %1111) + ((y & %1111) << 4)) 28 | 29 | {PPUCTRL}(PPUCTRL_NMI_ON) 30 | 31 | ct U delay = 20 32 | ct U period = 8 33 | 34 | while true 35 | wait(delay) 36 | fade_out_black(period) 37 | wait(delay) 38 | fade_in_black(period, @my_palette) 39 | wait(delay) 40 | fade_out_white(period) 41 | wait(delay) 42 | fade_in_white(period, @my_palette) 43 | wait(delay) 44 | fade_out_custom(period, -1, 0) 45 | wait(delay) 46 | fade_in_custom(period, -1, 0, @my_palette) 47 | 48 | chrrom 49 | file(fmt, "bg.png") 50 | -------------------------------------------------------------------------------- /src/lex_op_name.inc: -------------------------------------------------------------------------------- 1 | // This file has all the ops lexed by the assembler. 2 | 3 | OP_NAME(ADC) 4 | OP_NAME(AND) 5 | OP_NAME(ASL) 6 | OP_NAME(BCC) 7 | OP_NAME(BCS) 8 | OP_NAME(BEQ) 9 | OP_NAME(BIT) 10 | OP_NAME(BMI) 11 | OP_NAME(BNE) 12 | OP_NAME(BPL) 13 | OP_NAME(BRK) 14 | OP_NAME(BVC) 15 | OP_NAME(BVS) 16 | OP_NAME(CLC) 17 | OP_NAME(CLD) 18 | OP_NAME(CLI) 19 | OP_NAME(CLV) 20 | OP_NAME(CMP) 21 | OP_NAME(CPX) 22 | OP_NAME(CPY) 23 | OP_NAME(DEC) 24 | OP_NAME(DEX) 25 | OP_NAME(DEY) 26 | OP_NAME(EOR) 27 | OP_NAME(INC) 28 | OP_NAME(INX) 29 | OP_NAME(INY) 30 | OP_NAME(JMP) 31 | OP_NAME(JSR) 32 | OP_NAME(LDA) 33 | OP_NAME(LDX) 34 | OP_NAME(LDY) 35 | OP_NAME(LSR) 36 | OP_NAME(NOP) 37 | OP_NAME(ORA) 38 | OP_NAME(PHA) 39 | OP_NAME(PHP) 40 | OP_NAME(PLA) 41 | OP_NAME(PLP) 42 | OP_NAME(ROL) 43 | OP_NAME(ROR) 44 | OP_NAME(RTI) 45 | OP_NAME(RTS) 46 | OP_NAME(SBC) 47 | OP_NAME(SEC) 48 | OP_NAME(SED) 49 | OP_NAME(SEI) 50 | OP_NAME(STA) 51 | OP_NAME(STX) 52 | OP_NAME(STY) 53 | OP_NAME(TAX) 54 | OP_NAME(TAY) 55 | OP_NAME(TSX) 56 | OP_NAME(TXA) 57 | OP_NAME(TXS) 58 | OP_NAME(TYA) 59 | 60 | // Illegal: 61 | #ifndef DEBUG_LEGAL 62 | OP_NAME(LAX) 63 | OP_NAME(AXS) 64 | OP_NAME(ANC) 65 | OP_NAME(ALR) 66 | OP_NAME(ARR) 67 | OP_NAME(SAX) 68 | OP_NAME(SKB) 69 | OP_NAME(IGN) 70 | 71 | // Illegal RMW: 72 | OP_NAME(DCP) 73 | OP_NAME(ISC) 74 | OP_NAME(RLA) 75 | OP_NAME(RRA) 76 | OP_NAME(SLO) 77 | OP_NAME(SRE) 78 | #endif 79 | -------------------------------------------------------------------------------- /examples/zapper/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This small program plays sound effects when the zapper is fired 8 | // at the center of the screen. 9 | 10 | audio(puf1_sfx, "sfx.txt", "sfx.nsf") 11 | audio(puf1_music) 12 | 13 | mode main() 14 | : nmi game_nmi 15 | puf.init(system) 16 | 17 | ppu_reset_addr($2000) 18 | do for U i = 0; i; i += 1 19 | {PPUDATA}(0) 20 | do for U i = 0; i; i += 1 21 | {PPUDATA}((i & %1000) >> 3) 22 | do for U i = 0; i; i += 1 23 | {PPUDATA}(0) 24 | do for U i = 0; i; i += 1 25 | {PPUDATA}(0) 26 | 27 | palette = U[25]($24) 28 | {PPUCTRL}(PPUCTRL_NMI_ON) 29 | 30 | while true 31 | if zapper_pressed() 32 | palette = U[25]($20) 33 | palette[PALETTE_UBC] = $0F 34 | nmi 35 | if zapper_lit() 36 | puf.play_sfx(0) 37 | else 38 | puf.play_sfx(1) 39 | palette = U[25]($24) 40 | nmi 41 | 42 | nmi game_nmi() 43 | ppu_upload_palette() 44 | puf.process(PUF_DEFAULT) 45 | ppu_reset_scroll(0, 0) 46 | {PPUMASK}(PPUMASK_BG_ON) 47 | {PPUCTRL}(PPUCTRL_NMI_ON) 48 | 49 | chrrom 50 | U[16]($00) 51 | U[16]($FF) 52 | -------------------------------------------------------------------------------- /doc/LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /lib/LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /examples/LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/cg_cset.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CG_CSET_HPP 2 | #define CG_CSET_HPP 3 | 4 | #include 5 | 6 | #include "decl.hpp" 7 | #include "ir_decl.hpp" 8 | #include "cg.hpp" 9 | 10 | // Nodes will be partitioned into congruence classes for coalescing 11 | // purposes, dubbed "cset" for brevity. 12 | // These are implemented as union-find on top of a singly-linked list. 13 | // Below are some helper functions. 14 | 15 | locator_t asm_arg(ssa_value_t v); 16 | 17 | // Caches the locations of specific SSA ops to avoid constantly looping through the SSA. 18 | struct cset_ir_cache_t 19 | { 20 | std::vector special; 21 | }; 22 | 23 | cset_ir_cache_t cset_build_cache(ir_t const& ir); 24 | 25 | bool cset_is_head(ssa_ht h); 26 | bool cset_is_last(ssa_ht h); 27 | ssa_ht cset_next(ssa_ht h); 28 | 29 | ssa_ht cset_head(ssa_ht h); 30 | ssa_ht cset_last(ssa_ht h); 31 | 32 | locator_t cset_locator(ssa_ht h, bool convert_ssa = false); 33 | 34 | bool cset_locators_mergable(locator_t a, locator_t b); 35 | 36 | ssa_ht cset_remove(ssa_ht h); 37 | 38 | ssa_ht cset_append(ssa_value_t last, ssa_ht h); 39 | 40 | bool special_interferes(fn_ht fn, ir_t const& ir, ssa_ht h, locator_t loc, ssa_ht node); 41 | 42 | ssa_ht csets_dont_interfere(fn_ht fn, ir_t const& ir, ssa_ht a, ssa_ht b, cset_ir_cache_t const& cache); 43 | ssa_ht csets_appendable(fn_ht fn, ir_t const& ir, ssa_ht a, ssa_ht b, cset_ir_cache_t const& cache); 44 | 45 | bool cset_live_at_any_def(ssa_ht a, ssa_ht const* b_begin, ssa_ht const* b_end); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/rom_prune.cpp: -------------------------------------------------------------------------------- 1 | #include "rom_prune.hpp" 2 | 3 | #ifndef NDEBUG 4 | #include 5 | #endif 6 | 7 | #include "rom.hpp" 8 | #include "runtime.hpp" 9 | #include "lt.hpp" 10 | #include "globals.hpp" 11 | #include "group.hpp" 12 | 13 | static void rom_mark_emits(rom_data_ht data); 14 | 15 | static void locator_mark_emits(locator_t loc) 16 | { 17 | if(rom_data_ht h = loc.rom_data()) 18 | ::rom_mark_emits(h); // Recurse 19 | 20 | if(has_fn_set(loc.lclass())) 21 | { 22 | fn_set_ht fn_set = loc.fn_set(); 23 | for(fn_ht fn : *fn_set) 24 | rom_mark_emits(fn->rom_proc()); 25 | } 26 | 27 | if(loc.lclass() == LOC_LT_EXPR) 28 | { 29 | lt_value_t& value = *loc.lt(); 30 | 31 | if(!value.prune_processed) 32 | { 33 | value.prune_processed = true; 34 | value.for_each_locator(locator_mark_emits); 35 | } 36 | } 37 | } 38 | 39 | static void rom_mark_emits(rom_data_ht data) 40 | { 41 | if(data.get()->emits()) 42 | return; 43 | 44 | data.get()->mark_emits(); 45 | assert(data.get()->emits()); 46 | 47 | data.for_each_locator(locator_mark_emits); 48 | } 49 | 50 | void prune_rom_data() 51 | { 52 | // Recursively mark rom_data as being emitted, starting from the runtime rom. 53 | for(runtime_rom_name_t rtrom = {}; rtrom < NUM_RTROM; rtrom = runtime_rom_name_t(rtrom + 1)) 54 | if(rom_data_ht data = runtime_data(rtrom)) 55 | rom_mark_emits(data); 56 | } 57 | -------------------------------------------------------------------------------- /src/alloca.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PUBBY_ALLOCA_HPP 2 | #define PUBBY_ALLOCA_HPP 3 | 4 | // alloca can be platform specific. 5 | // This header exists for portability reasons. 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) 12 | // Note that MSVC's alloca can throw something called a structured exception. 13 | // This is NOT a standard C++ exception. It's something weird and fucky. 14 | // It's probably easiest to just ignore this fucky exception and let 15 | // everything crash and burn on error. 16 | # include 17 | # if defined(_MSC_VER) 18 | # define alloca _alloca // have to use define because alloca is special. 19 | # endif 20 | #else 21 | # include 22 | #endif 23 | 24 | // Have to wrap the assert in a function call to use it in a comma expression. 25 | template 26 | void allocaT_trait_check() 27 | { 28 | static_assert(std::is_trivially_destructible::value); 29 | } 30 | 31 | template 32 | T* callocaT_impl(T* ptr, std::size_t n, T const& fill) { std::fill(ptr, ptr+n, fill); return ptr; } 33 | 34 | // It's the sequel to alloca. 35 | // Expects "number of elements" rather than "total size of elements". 36 | #define ALLOCA_T(t, n) \ 37 | (allocaT_trait_check(), static_cast(alloca((n) * sizeof(t)))) 38 | #define CALLOCA_T(t, n) \ 39 | (allocaT_trait_check(), callocaT_impl(static_cast(alloca((n) * sizeof(t))), (n), (t()))) 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/worklist.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WORKLIST_HPP 2 | #define WORKLIST_HPP 3 | 4 | #include 5 | 6 | #include "flags.hpp" 7 | #include "ir_decl.hpp" 8 | #include "thread.hpp" 9 | 10 | namespace bc = ::boost::container; 11 | 12 | // Worklists used by several optimization passes. 13 | template 14 | class worklist_t 15 | { 16 | public: 17 | bc::deque container; 18 | 19 | void push(H h) 20 | { 21 | if(h->test_flags(FLAG_IN_WORKLIST)) 22 | return; 23 | h->set_flags(FLAG_IN_WORKLIST); 24 | container.push_back(h); 25 | } 26 | 27 | void queue(H h) 28 | { 29 | if(h->test_flags(FLAG_IN_WORKLIST)) 30 | return; 31 | h->set_flags(FLAG_IN_WORKLIST); 32 | container.push_front(h); 33 | } 34 | 35 | H top() { return container.back(); } 36 | 37 | H pop() 38 | { 39 | H ret = top(); 40 | ret->clear_flags(FLAG_IN_WORKLIST); 41 | container.pop_back(); 42 | return ret; 43 | } 44 | 45 | void clear() 46 | { 47 | for(H handle : container) 48 | handle->clear_flags(FLAG_IN_WORKLIST); 49 | container.clear(); 50 | } 51 | 52 | bool empty() { return container.empty(); } 53 | }; 54 | 55 | extern TLS worklist_t cfg_worklist; 56 | extern TLS worklist_t ssa_worklist; 57 | 58 | // These two vectors can also be used by optimization passes, if need be. 59 | extern TLS std::vector cfg_workvec; 60 | extern TLS std::vector ssa_workvec; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/o_arg.cpp: -------------------------------------------------------------------------------- 1 | #include "o_arg.hpp" 2 | 3 | #include "globals.hpp" 4 | #include "ir.hpp" 5 | 6 | bool o_remove_unused_arguments(log_t* log, ir_t& ir, fn_t const& fn, bool byteified) 7 | { 8 | bool changed = false; 9 | 10 | for(cfg_node_t const& cfg : ir) 11 | for(ssa_ht ssa_it = cfg.ssa_begin(); ssa_it; ++ssa_it) 12 | { 13 | if(!direct_fn(ssa_it->op())) 14 | continue; 15 | 16 | fn_ht const called_h = get_fn(*ssa_it); 17 | if(!called_h) 18 | continue; 19 | fn_t const& called = *called_h; 20 | 21 | // If the called fn is a mode, it may not have been compiled yet. 22 | // Thus, we can't check if it uses the argument. 23 | if(called.fclass == FN_MODE) 24 | continue; 25 | 26 | passert(called.global.compiled(), fn.global.name, called.global.name); 27 | 28 | for(unsigned i = write_globals_begin(ssa_it->op()); i < ssa_it->input_size();) 29 | { 30 | locator_t const loc = ssa_it->input(i+1).locator(); 31 | if(loc.lclass() == LOC_ARG && loc.fn() == called_h 32 | && (!called.lvars().seen_arg(loc.arg()) 33 | || (byteified && called.lvars().index(loc) < 0))) 34 | { 35 | dprint(log, "REMOVE_UNUSED_ARGUMENTS_PRUNE", loc); 36 | 37 | // Prune this arg: 38 | ssa_it->link_remove_input(i+1); 39 | ssa_it->link_remove_input(i); 40 | changed = true; 41 | } 42 | else 43 | i += 2; 44 | } 45 | } 46 | 47 | return changed; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /examples/rainbow/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This demo showcases Rainbow extended attributes and expansion audio. 8 | 9 | audio(puf1_music, "music.txt") 10 | audio(puf1_sfx, "sfx.txt", "sfx.nsf") 11 | 12 | nmi main_nmi() 13 | ppu_upload_oam_poll_pads(0) 14 | ppu_reset_scroll(0, 0) 15 | {PPUMASK}(PPUMASK_BG_ON | PPUMASK_NO_CLIP) 16 | puf.process(PUF_DEFAULT) 17 | 18 | mode main() 19 | : nmi main_nmi 20 | // Init the music: 21 | puf.init(system) 22 | puf.play_track(0) 23 | 24 | // Load the palette: 25 | palette = example_palette 26 | ppu_upload_palette() 27 | 28 | // Setup the nametable: 29 | ppu_reset_addr($2000) 30 | for U y = 0; y < 30; y += 1 31 | for U x = 0; x < 32; x += 1 32 | {PPUDATA}((x & %1111) + ((y & %1111) << 4)) 33 | for U a = 0; a < 64; a += 1 34 | {PPUDATA}($FF) 35 | 36 | // Setup extended attributes: 37 | rnbw_exram_auto($0000, 1) 38 | do for UU i = 0; i < 1024; i += 1 39 | {EXDATA}(rand() & %11000000) 40 | 41 | // Set Rainbow registers: 42 | {RNBW_NT_2000_CTRL}(RNBW_NT_CTRL_ATTR_EXT) 43 | 44 | // Loop forever: 45 | {PPUCTRL}(PPUCTRL_NMI_ON | PPUCTRL_SPR_PT_1000) 46 | while true 47 | nmi 48 | update_pads() 49 | if pads[0].pressed & BUTTON_UP 50 | puf.play_sfx(0) 51 | if pads[0].pressed & BUTTON_DOWN 52 | puf.play_sfx(1) 53 | 54 | chrrom 55 | file(fmt, "bg.png") 56 | -------------------------------------------------------------------------------- /src/hex.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HEX_HPP 2 | #define HEX_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | inline int char_to_int(char ch) 10 | { 11 | constexpr auto lookup_table = [] 12 | { 13 | std::array table; 14 | 15 | table.fill(-1); 16 | 17 | for(unsigned i = 0; i < 10; ++i) 18 | table['0'+i] = i; 19 | 20 | table['a'] = table['A'] = 10; 21 | table['b'] = table['B'] = 11; 22 | table['c'] = table['C'] = 12; 23 | table['d'] = table['D'] = 13; 24 | table['e'] = table['E'] = 14; 25 | table['f'] = table['F'] = 15; 26 | 27 | return table; 28 | }(); 29 | 30 | return lookup_table[ch]; 31 | } 32 | 33 | inline char int_to_char(int i) 34 | { 35 | switch(i) 36 | { 37 | default: 38 | case 0: return '0'; 39 | case 1: return '1'; 40 | case 2: return '2'; 41 | case 3: return '3'; 42 | case 4: return '4'; 43 | case 5: return '5'; 44 | case 6: return '6'; 45 | case 7: return '7'; 46 | case 8: return '8'; 47 | case 9: return '9'; 48 | case 10: return 'A'; 49 | case 11: return 'B'; 50 | case 12: return 'C'; 51 | case 13: return 'D'; 52 | case 14: return 'E'; 53 | case 15: return 'F'; 54 | } 55 | } 56 | 57 | inline std::string hex_string(unsigned v, unsigned digits) 58 | { 59 | std::string ret; 60 | ret.resize(digits); 61 | for(unsigned i = 0; i < digits; ++i) 62 | { 63 | ret.rbegin()[i] = int_to_char(v & 0xF); 64 | v >>= 4; 65 | } 66 | return ret; 67 | } 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /src/ctags.cpp: -------------------------------------------------------------------------------- 1 | #include "ctags.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "file.hpp" 8 | #include "globals.hpp" 9 | #include "pstring.hpp" 10 | 11 | void write_ctags(FILE* fp, fs::path ctags_path) 12 | { 13 | struct ctag_t 14 | { 15 | std::string ident; 16 | lpstring_t lpstring; 17 | }; 18 | 19 | ctags_path = fs::absolute(ctags_path); 20 | ctags_path.remove_filename(); 21 | 22 | std::vector file_paths(compiler_options().source_names.size()); 23 | for(unsigned i = 0; i < file_paths.size(); ++i) 24 | file_paths[i] = fs::proximate(source_path(i), ctags_path).string(); 25 | 26 | std::vector ctags; 27 | ctags.reserve(global_ht::pool().size()); 28 | 29 | for(global_t const& g : global_ht::values()) 30 | { 31 | if(!g.name.empty() && g.name[0] == '_') 32 | continue; 33 | if(g.lpstring().file_i >= file_paths.size()) 34 | continue; 35 | if(g.lpstring().line == BAD_LINE_NUMBER) 36 | continue; 37 | if(global_t::lookup_sourceless(g.name) != &g) 38 | continue; 39 | ctags.push_back({ g.name, g.lpstring() }); 40 | } 41 | 42 | std::sort(ctags.begin(), ctags.end(), [&](ctag_t const& a, ctag_t const& b) 43 | { 44 | return a.ident < b.ident; 45 | }); 46 | 47 | for(ctag_t const& ctag : ctags) 48 | { 49 | std::fprintf( 50 | fp, "%s\t%s\t%i\n", 51 | ctag.ident.c_str(), 52 | file_paths[ctag.lpstring.file_i].c_str(), 53 | ctag.lpstring.line - 1); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/ext_lex_tables.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | namespace ext_lex 4 | { 5 | using token_type_t = std::uint16_t; 6 | constexpr token_type_t TOK_ERROR = 0; 7 | constexpr token_type_t TOK_bin = 1; 8 | constexpr token_type_t TOK_chr = 2; 9 | constexpr token_type_t TOK_nam = 3; 10 | constexpr token_type_t TOK_png = 4; 11 | constexpr token_type_t TOK_txt = 5; 12 | constexpr token_type_t TOK_pal = 6; 13 | constexpr token_type_t TOK_map = 7; 14 | constexpr token_type_t TOK_END = 8; 15 | inline std::string_view token_name(token_type_t type) 16 | { 17 | using namespace std::literals; 18 | switch(type) 19 | { 20 | default: return "?BAD?"sv; 21 | case TOK_bin: return "bin"sv; 22 | case TOK_chr: return "chr"sv; 23 | case TOK_nam: return "nam"sv; 24 | case TOK_png: return "png"sv; 25 | case TOK_txt: return "txt"sv; 26 | case TOK_pal: return "pal"sv; 27 | case TOK_map: return "map"sv; 28 | } 29 | } 30 | inline std::string_view token_string(token_type_t type) 31 | { 32 | using namespace std::literals; 33 | switch(type) 34 | { 35 | default: return "?BAD?"sv; 36 | case TOK_bin: return "bin"sv; 37 | case TOK_chr: return "chr"sv; 38 | case TOK_nam: return "nam"sv; 39 | case TOK_png: return "png"sv; 40 | case TOK_txt: return "txt"sv; 41 | case TOK_pal: return "pal"sv; 42 | case TOK_map: return "map"sv; 43 | } 44 | } 45 | #define ext_lex_TOK_KEY_CASES \ 46 | 47 | constexpr token_type_t TOK_LAST_STATE = 7; 48 | constexpr token_type_t TOK_START = 8; 49 | extern unsigned const lexer_ec_table[256]; 50 | extern token_type_t const lexer_transition_table[1134]; 51 | } // namespace ext_lex 52 | -------------------------------------------------------------------------------- /examples/4_player/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This example displays 4 movable characters, each controlled by their own controller. 8 | 9 | struct Player 10 | U x 11 | U y 12 | 13 | vars 14 | // Our players: 15 | Player[4] pl = Player[4](Player(128-4, 120-4)) 16 | U num_players = 4 17 | 18 | nmi game_nmi() 19 | // Update OAM and poll the pads: 20 | ppu_upload_oam_poll_pads(0) 21 | 22 | // Turn on rendering: 23 | {PPUMASK}(PPUMASK_SPR_ON | PPUMASK_NO_CLIP) 24 | 25 | // Reset the scroll 26 | ppu_reset_scroll(0, 0) 27 | 28 | mode main() 29 | : nmi game_nmi 30 | palette = example_palette 31 | ppu_upload_palette() 32 | {PPUCTRL}(PPUCTRL_NMI_ON) 33 | 34 | while true 35 | nmi 36 | update_pads() 37 | move_players() 38 | update_sprites() 39 | 40 | fn move_players() 41 | for U i = 0; i < num_players; i += 1 42 | if pads[i].held & BUTTON_LEFT 43 | pl[i].x -= 1 44 | else if pads[i].held & BUTTON_RIGHT 45 | pl[i].x += 1 46 | 47 | if pads[i].held & BUTTON_UP 48 | pl[i].y -= 1 49 | else if pads[i].held & BUTTON_DOWN 50 | pl[i].y += 1 51 | 52 | fn update_sprites() 53 | // Our stack index into OAM: 54 | U o = 0 55 | 56 | for U i = 0; i < num_players; i += 1 57 | o = push_oam(o, pl[i].x, pl[i].y, i, i) 58 | 59 | // Clear the remainder of OAM 60 | hide_oam(o) 61 | 62 | chrrom 63 | file(fmt, "sprite.png") 64 | -------------------------------------------------------------------------------- /src/fn_def.cpp: -------------------------------------------------------------------------------- 1 | #include "fn_def.hpp" 2 | 3 | using namespace lex; 4 | 5 | static pstring_t _find_global(ast_node_t const& ast, global_t const* global) 6 | { 7 | if(ast.token.type == TOK_global_ident && ast.token.ptr() == global) 8 | return ast.token.pstring; 9 | unsigned const n = ast.num_children(); 10 | for(unsigned i = 0; i != n; ++i) 11 | if(pstring_t pstring = _find_global(ast.children[i], global)) 12 | return pstring; 13 | return {}; 14 | } 15 | 16 | pstring_t fn_def_t::find_global(global_t const* global) const 17 | { 18 | for(stmt_t const& stmt : stmts) 19 | { 20 | if(has_expression(stmt.name)) 21 | { 22 | passert(stmt.expr, to_string(stmt.name)); 23 | if(pstring_t pstring = _find_global(*stmt.expr, global)) 24 | return pstring; 25 | } 26 | } 27 | return {}; 28 | } 29 | 30 | 31 | stmt_ht fn_def_t::push_stmt(stmt_t stmt) 32 | { 33 | stmt_ht const handle = next_stmt(); 34 | stmts.push_back(stmt); 35 | return handle; 36 | } 37 | 38 | stmt_ht fn_def_t::push_var_init(unsigned name, ast_node_t const* expr, pstring_t pstring) 39 | { 40 | return push_stmt({ static_cast(~name), {}, {}, pstring, expr }); 41 | } 42 | 43 | stmt_mods_ht fn_def_t::push_mods(std::unique_ptr m) 44 | { 45 | if(!m) 46 | return {}; 47 | stmt_mods_ht const handle = { mods.size() }; 48 | mods.push_back(std::move(*m)); 49 | return handle; 50 | } 51 | 52 | mods_t const* fn_def_t::mods_of(stmt_ht h) const 53 | { 54 | if(stmt_mods_ht m = operator[](h).mods) 55 | return &operator[](m); 56 | return nullptr; 57 | } 58 | -------------------------------------------------------------------------------- /examples/wifi/server/node_modules/ws/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ws", 3 | "version": "7.4.6", 4 | "description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js", 5 | "keywords": [ 6 | "HyBi", 7 | "Push", 8 | "RFC-6455", 9 | "WebSocket", 10 | "WebSockets", 11 | "real-time" 12 | ], 13 | "homepage": "https://github.com/websockets/ws", 14 | "bugs": "https://github.com/websockets/ws/issues", 15 | "repository": "websockets/ws", 16 | "author": "Einar Otto Stangvik (http://2x.io)", 17 | "license": "MIT", 18 | "main": "index.js", 19 | "browser": "browser.js", 20 | "engines": { 21 | "node": ">=8.3.0" 22 | }, 23 | "files": [ 24 | "browser.js", 25 | "index.js", 26 | "lib/*.js" 27 | ], 28 | "scripts": { 29 | "test": "nyc --reporter=lcov --reporter=text mocha --throw-deprecation test/*.test.js", 30 | "integration": "mocha --throw-deprecation test/*.integration.js", 31 | "lint": "eslint --ignore-path .gitignore . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yaml,yml}\"" 32 | }, 33 | "peerDependencies": { 34 | "bufferutil": "^4.0.1", 35 | "utf-8-validate": "^5.0.2" 36 | }, 37 | "peerDependenciesMeta": { 38 | "bufferutil": { 39 | "optional": true 40 | }, 41 | "utf-8-validate": { 42 | "optional": true 43 | } 44 | }, 45 | "devDependencies": { 46 | "benchmark": "^2.1.4", 47 | "bufferutil": "^4.0.1", 48 | "eslint": "^7.2.0", 49 | "eslint-config-prettier": "^8.1.0", 50 | "eslint-plugin-prettier": "^3.0.1", 51 | "mocha": "^7.0.0", 52 | "nyc": "^15.0.0", 53 | "prettier": "^2.0.5", 54 | "utf-8-validate": "^5.0.2" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/asm.cpp: -------------------------------------------------------------------------------- 1 | #include "asm.hpp" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | std::string to_string(addr_mode_t addr_mode) 9 | { 10 | using namespace std::string_literals; 11 | switch(addr_mode) 12 | { 13 | #define ADDR_MODE(name) case MODE_##name: \ 14 | return BOOST_PP_CAT(BOOST_PP_STRINGIZE(name),s); 15 | #include "addr_mode.inc" 16 | #undef ADDR_MODE 17 | default: return ""s; 18 | } 19 | } 20 | 21 | std::string to_string(op_name_t name) 22 | { 23 | using namespace std::string_literals; 24 | switch(name) 25 | { 26 | #define OP_NAME(name) case name: \ 27 | return BOOST_PP_CAT(BOOST_PP_STRINGIZE(name),s); 28 | #include "op_name.inc" 29 | #undef OP_NAME 30 | default: return ""s; 31 | } 32 | } 33 | 34 | std::string to_string(op_t op) 35 | { 36 | using namespace std::string_literals; 37 | switch(op) 38 | { 39 | #define OP(name) case name: \ 40 | return BOOST_PP_CAT(BOOST_PP_STRINGIZE(name),s); 41 | #include "op.inc" 42 | #undef OP 43 | default: 44 | if(op >= BEGIN_REG_READ_OP && op < END_REG_READ_OP) 45 | return "REG_READ"s; 46 | if(op >= BEGIN_REG_WRITE_OP && op < END_REG_WRITE_OP) 47 | return "REG_WRITE"s; 48 | return ""s; 49 | } 50 | } 51 | 52 | std::ostream& operator<<(std::ostream& os, addr_mode_t addr_mode) 53 | { 54 | os << to_string(addr_mode); 55 | return os; 56 | } 57 | 58 | std::ostream& operator<<(std::ostream& os, op_name_t name) 59 | { 60 | os << to_string(name); 61 | return os; 62 | } 63 | 64 | std::ostream& operator<<(std::ostream& os, op_t op) 65 | { 66 | os << to_string(op); 67 | return os; 68 | } 69 | -------------------------------------------------------------------------------- /src/eternal_new.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ETERNAL_NEW_HPP 2 | #define ETERNAL_NEW_HPP 3 | 4 | // Let's you allocate data that persists until program termination. 5 | // (This could simply be 'return new T', but that triggers leak detectors.) 6 | 7 | #include 8 | 9 | #include "array_pool.hpp" 10 | #include "thread.hpp" 11 | 12 | template 13 | class eternal_new_pool_t : public array_pool_t 14 | { 15 | public: 16 | using array_pool_t::array_pool_t; 17 | 18 | ~eternal_new_pool_t() 19 | { 20 | std::lock_guard lock(mutex); 21 | eternal.splice(*this); 22 | } 23 | 24 | static void free_parent() 25 | { 26 | std::lock_guard lock(mutex); 27 | eternal.clear(); 28 | eternal.shrink_to_fit(); 29 | } 30 | 31 | private: 32 | inline static std::mutex mutex; 33 | inline static array_pool_t eternal; 34 | }; 35 | 36 | // This has to be a function to get around a GCC bug 37 | // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81880 38 | template 39 | inline eternal_new_pool_t& eternal_new_pool() 40 | { 41 | static TLS eternal_new_pool_t pool; 42 | return pool; 43 | } 44 | 45 | template 46 | T* eternal_new(std::size_t size) 47 | { 48 | return eternal_new_pool().alloc(size); 49 | } 50 | 51 | template 52 | T* eternal_new(T const* begin, T const* end) 53 | { 54 | return eternal_new_pool().insert(begin, end); 55 | } 56 | 57 | template 58 | T* eternal_emplace(Args&&... args) 59 | { 60 | return &eternal_new_pool().emplace(std::forward(args)...); 61 | } 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/flags.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FLAGS_HPP 2 | #define FLAGS_HPP 3 | 4 | #include 5 | 6 | // Flags that are useful among different passes. 7 | 8 | enum mark_t : std::uint16_t 9 | { 10 | MARK_NONE = 0, 11 | MARK_TEMPORARY = 1, 12 | MARK_PERMANENT = 2, 13 | MARK_DELAYED = 3, 14 | }; 15 | 16 | constexpr std::uint16_t MARK_OFFSET = 0; 17 | constexpr std::uint16_t MARK_MASK = 0b11; 18 | 19 | constexpr std::uint16_t FLAG_PRUNED = 1ull << 2; 20 | constexpr std::uint16_t FLAG_DAISY = 1ull << 3; 21 | 22 | constexpr std::uint16_t FLAG_IN_WORKLIST = 1ull << 4; 23 | constexpr std::uint16_t FLAG_PROCESSED = 1ull << 5; 24 | constexpr std::uint16_t FLAG_ARRAY = 1ull << 6; 25 | constexpr std::uint16_t FLAG_BANK_PRELOADED = 1ull << 7; 26 | constexpr std::uint16_t FLAG_TO_PRUNE = 1ull << 8; 27 | 28 | // For CFG: 29 | constexpr std::uint16_t FLAG_NO_UNROLL = 1ull << 9; 30 | constexpr std::uint16_t FLAG_UNROLL = 1ull << 10; 31 | constexpr std::uint16_t FLAG_UNLOOP = 1ull << 11; 32 | 33 | // Flags that should propagate: 34 | constexpr std::uint16_t FLAGS_PROP = FLAG_NO_UNROLL | FLAG_UNROLL | FLAG_UNLOOP; 35 | 36 | class flag_owner_t 37 | { 38 | public: 39 | void set_flags(std::uint16_t f) { m_flags |= f; } 40 | void clear_flags(std::uint16_t f) { m_flags &= ~f; } 41 | bool test_flags(std::uint16_t f) const { return (m_flags & f) == f; } 42 | 43 | void set_mark(mark_t mark) { m_flags &= ~MARK_MASK; m_flags |= mark; } 44 | void clear_mark() { m_flags &= ~MARK_MASK; } 45 | mark_t get_mark() const { return (mark_t)(m_flags & MARK_MASK); } 46 | 47 | std::uint16_t prop_flags() const { return m_flags & FLAGS_PROP; } 48 | protected: 49 | std::uint16_t m_flags = 0; 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /examples/collision/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This example uses trigonometry to have an object follow the player. 8 | 9 | vars 10 | // Player variables: 11 | Coord[2] p = Coord[2](Coord(64, 64), Coord(128, 128)) 12 | 13 | mode main() 14 | : nmi game_nmi 15 | palette = example_palette 16 | ppu_upload_palette() 17 | {PPUCTRL}(PPUCTRL_NMI_ON) 18 | 19 | while true 20 | nmi 21 | update_pads() 22 | move_player() 23 | update_sprites() 24 | 25 | fn move_player() 26 | ct Int SPEED = 1 27 | 28 | if nmi_counter & 1 29 | return 30 | 31 | for U i = 0; i < 2; i += 1 32 | if pads[i].held & BUTTON_LEFT 33 | p[i].x -= SPEED 34 | else if pads[i].held & BUTTON_RIGHT 35 | p[i].x += SPEED 36 | 37 | if pads[i].held & BUTTON_UP 38 | p[i].y -= SPEED 39 | else if pads[i].held & BUTTON_DOWN 40 | p[i].y += SPEED 41 | 42 | fn update_sprites() 43 | // Our stack index into OAM: 44 | U o = 0 45 | 46 | for U i = 0; i < 2; i += 1 47 | set_oam(o, p[i].x, p[i].y - 1, $00, 0) 48 | o += 4 49 | 50 | // Clear the remainder of OAM 51 | hide_oam(o) 52 | 53 | nmi game_nmi() 54 | // Update OAM and poll the pads: 55 | ppu_upload_oam_poll_pads(0) 56 | 57 | // Reset the scroll 58 | ppu_reset_scroll(0, 0) 59 | 60 | // Turn on rendering: 61 | if overlap(Rect(p[0], Dimen(8, 8)), Rect(p[1], Dimen(8, 8))) 62 | {PPUMASK}(PPUMASK_SPR_ON | PPUMASK_NO_CLIP | PPUMASK_GRAYSCALE) 63 | else 64 | {PPUMASK}(PPUMASK_SPR_ON | PPUMASK_NO_CLIP) 65 | 66 | chrrom 67 | U[16]($FF) 68 | -------------------------------------------------------------------------------- /src/convert.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONVERT_HPP 2 | #define CONVERT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "pstring.hpp" 10 | #include "locator.hpp" 11 | #include "parser_decl.hpp" 12 | #include "ir_edge.hpp" 13 | #include "asm_proc.hpp" 14 | 15 | struct mods_t; 16 | 17 | struct convert_arg_t 18 | { 19 | using variant_t = std::variant; 20 | 21 | variant_t value; 22 | pstring_t pstring; 23 | 24 | string_literal_t filename() const; 25 | }; 26 | 27 | class convert_error_t : public std::runtime_error 28 | { 29 | public: 30 | explicit convert_error_t(char const* what) : std::runtime_error(what) {} 31 | explicit convert_error_t(std::string const& what) : std::runtime_error(what) {} 32 | }; 33 | 34 | struct conversion_named_values_t 35 | { 36 | char const* name; 37 | ssa_value_t value; 38 | }; 39 | 40 | struct conversion_t 41 | { 42 | std::variant, std::vector, asm_proc_t> data; 43 | bc::small_vector named_values; 44 | }; 45 | 46 | conversion_t convert_file(char const* source, pstring_t script, fs::path preferred_dir, 47 | string_literal_t const& filename, mods_t const* mods, 48 | convert_arg_t* args, std::size_t argn); 49 | 50 | template 51 | struct convert_u8_impl_t 52 | { static T call(std::uint8_t u) { return T(u); } }; 53 | 54 | template<> 55 | struct convert_u8_impl_t 56 | { static locator_t call(std::uint8_t u) { return locator_t::const_byte(u); } }; 57 | 58 | // Allows writing conversion functions in a slightly generic way. 59 | template 60 | T convert_u8(std::uint8_t u) { return convert_u8_impl_t::call(u); } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /syntax_highlighting/nesfab.vim: -------------------------------------------------------------------------------- 1 | " Vim syntax file 2 | " Language: NESFab 3 | " Maintainer: Pubby 4 | 5 | set expandtab 6 | 7 | if exists("b:current_syntax") 8 | finish 9 | endif 10 | 11 | syn keyword nesfabKeyword if else for while do break continue return fn 12 | \ ct nmi mode goto label file struct vars data omni ready fence irq 13 | \ default switch case asm charmap chrrom true false audio system stows 14 | \ employs preserves state read write sizeof len push pop mapfab min max 15 | \ abs macro nmi_counter swap 16 | 17 | syntax match nesfabId "_\{0,1}\l\k*" 18 | syntax match nesfabType "_\{0,1}\u\k*" 19 | syntax match nesfabTypeFn "Fn\._\{0,1}\l\k*" 20 | syntax match nesfabTypeI "I\._\{0,1}\l\k*" 21 | syntax match nesfabTypeII "II\._\{0,1}\l\k*" 22 | 23 | syntax match nesfabGroup "/\k\+" 24 | 25 | " Integer with - + or nothing in front 26 | syn match nesfabNumber '\d\+\(\.\d*\|\)' 27 | syn match nesfabNumberHex '\$\x\+\(\.\x*\|\)' 28 | syn match nesfabNumberBin '%[01]\+\(\.[01]*\|\)' 29 | 30 | " Comment 31 | syn match nesfabCommentL "//.*$" 32 | syntax region nesfabComment start=/\/\*/ end=/\*\// 33 | 34 | " String 35 | syn region nesfabString start="\"" skip=+\\\\\|\\"+ end="\"" 36 | syn region nesfabStringC start="`" skip=+\\\\\|\\"+ end="`" 37 | 38 | let b:current_syntax = "nesfab" 39 | 40 | hi def link nesfabNumber Constant 41 | hi def link nesfabNumberHex Constant 42 | hi def link nesfabNumberBin Constant 43 | hi def link nesfabCommentL Comment 44 | hi def link nesfabComment Comment 45 | hi def link nesfabString String 46 | hi def link nesfabStringC String 47 | hi def link nesfabKeyword Statement 48 | hi def link nesfabGroup Identifier 49 | hi def link nesfabType Type 50 | hi def link nesfabTypeFn Type 51 | hi def link nesfabTypeI Type 52 | hi def link nesfabTypeII Type 53 | -------------------------------------------------------------------------------- /src/compiler_error.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMPILER_ERROR_HPP 2 | #define COMPILER_ERROR_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "format.hpp" 8 | #include "pstring.hpp" 9 | #include "console.hpp" 10 | 11 | class compiler_error_t : public std::runtime_error 12 | { 13 | public: 14 | explicit compiler_error_t(char const* what, bool warning = false) 15 | : std::runtime_error(what) 16 | , warning(warning) 17 | {} 18 | 19 | explicit compiler_error_t(std::string const& what, bool warning = false) 20 | : std::runtime_error(what) 21 | , warning(warning) 22 | {} 23 | 24 | bool warning = false; 25 | }; 26 | 27 | std::string fmt_source_pos(file_contents_t const& file, pstring_t pstring); 28 | std::string fmt_source_pos(std::string const& filename, char const* source, pstring_t pstring); 29 | 30 | std::string fmt_error(std::string const& what); 31 | std::string fmt_error(pstring_t pstring, std::string const& what, 32 | file_contents_t const* file = nullptr, 33 | char const* color = CONSOLE_RED CONSOLE_BOLD, char const* prefix = "error"); 34 | 35 | std::string fmt_note(std::string const& what); 36 | std::string fmt_note(pstring_t pstring, std::string const& what, 37 | file_contents_t const* file = nullptr); 38 | 39 | std::string fmt_warning(pstring_t pstring, std::string const& what, 40 | file_contents_t const* file = nullptr); 41 | 42 | [[gnu::noreturn]] 43 | void compiler_error(pstring_t pstring, std::string const& what, 44 | file_contents_t const* file = nullptr); 45 | 46 | void compiler_warning(pstring_t pstring, std::string const& what, 47 | file_contents_t const* file = nullptr); 48 | 49 | void compiler_warning(std::string const& what, bool formatted = false); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/group.cpp: -------------------------------------------------------------------------------- 1 | #include "group.hpp" 2 | 3 | #include "fnv1a.hpp" 4 | #include "format.hpp" 5 | #include "compiler_error.hpp" 6 | #include "globals.hpp" 7 | 8 | defined_group_vars_t group_t::define_vars(pstring_t pstring) 9 | { 10 | std::lock_guard lock(m_define_mutex); 11 | if(!m_pstring) 12 | m_pstring = pstring; 13 | if(!m_vars) 14 | m_vars.reset(new group_vars_t()); 15 | if(!m_vars_h) 16 | m_vars_h = group_vars_ht::pool_make(this); 17 | return { this, m_vars.get(), m_vars_h }; 18 | } 19 | 20 | defined_group_data_t group_t::define_data(pstring_t pstring, bool omni) 21 | { 22 | std::lock_guard lock(m_define_mutex); 23 | if(!m_pstring) 24 | m_pstring = pstring; 25 | auto& ptr = omni ? m_omni : m_data; 26 | if(!ptr) 27 | ptr.reset(new group_data_t()); 28 | if(!m_data_h) 29 | m_data_h = group_data_ht::pool_make(this); 30 | return { this, ptr.get(), m_data_h }; 31 | } 32 | 33 | void group_t::group_members() 34 | { 35 | assert(compiler_phase() == PHASE_GROUP_MEMBERS); 36 | 37 | for(group_t* group : group_vars_ht::values()) 38 | group->vars()->group_members(); 39 | } 40 | 41 | /////////////////// 42 | // group_gvars_t // 43 | /////////////////// 44 | 45 | void group_vars_t::group_members() 46 | { 47 | assert(compiler_phase() == PHASE_GROUP_MEMBERS); 48 | 49 | // Init the gmembers bitset 50 | m_gmembers.alloc(); 51 | for(gvar_ht gv : gvars()) 52 | m_gmembers.set_n(gv->begin().id, gv->num_members()); 53 | } 54 | 55 | void group_vars_t::determine_has_init() 56 | { 57 | assert(compiler_phase() == PHASE_PARSE_CLEANUP); 58 | m_has_init = false; 59 | for(gvar_ht gvar : gvars()) 60 | { 61 | if(gvar->init_expr) 62 | { 63 | m_has_init = true; 64 | break; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/zapper.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // The controller port to use: 8 | ct AA ZAPPER_ADDR = $4017 9 | 10 | // Flags for reading the Zapper input. 11 | ct U ZAPPER_TRIGGER = %00010000 12 | ct U ZAPPER_LIGHT = %00001000 13 | 14 | vars 15 | // Holds the last polled zapper value: 16 | U zapper = 0 17 | 18 | // Reads the current zapper state and stores it into 'zapper'. 19 | // Returns the prior value of 'zapper'. 20 | fn poll_zapper() U 21 | : +inline 22 | U prev = zapper 23 | zapper = {ZAPPER_ADDR}() 24 | return prev 25 | 26 | // If the zapper button was pressed: 27 | fn zapper_pressed() Bool 28 | : +inline 29 | U prev = poll_zapper() 30 | return zapper & ~prev & ZAPPER_TRIGGER 31 | 32 | // If the zapper button was released: 33 | fn zapper_released() Bool 34 | : +inline 35 | U prev = poll_zapper() 36 | return ~zapper & prev & ZAPPER_TRIGGER 37 | 38 | // If the zapper button was pressed OR released: 39 | fn zapper_triggered() Bool 40 | : +inline 41 | U prev = poll_zapper() 42 | return (zapper ^ prev) & ZAPPER_TRIGGER 43 | 44 | // Spends the rest of the frame (until NMI) reading the zapper. 45 | // Returns true if the zapper detects light and false otherwise. 46 | // Note: This function returns *after* the next NMI handler finishes. 47 | asm fn zapper_lit() Bool 48 | : employs 49 | default 50 | ldx #1 51 | stx &ready 52 | dex 53 | ldy &nmi_counter 54 | label read_loop 55 | stx &return 56 | lda ZAPPER_ADDR 57 | and #ZAPPER_LIGHT 58 | bne done_load 59 | ldx #1 60 | label done_load 61 | cpy &nmi_counter 62 | beq read_loop 63 | lda #0 64 | sta &ready 65 | rts 66 | -------------------------------------------------------------------------------- /examples/trig/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This example uses trigonometry to have an object follow the player. 8 | 9 | vars 10 | // Player variables: 11 | SS px = 128 12 | SS py = 120 13 | 14 | // Follower variables: 15 | SSF fx = 128.0 16 | SSF fy = 120.0 17 | 18 | mode main() 19 | : nmi game_nmi 20 | palette = example_palette 21 | ppu_upload_palette() 22 | {PPUCTRL}(PPUCTRL_NMI_ON) 23 | 24 | while true 25 | nmi 26 | update_pads() 27 | move_player() 28 | move_follower() 29 | update_sprites() 30 | 31 | fn move_player() 32 | ct Int SPEED = 2 33 | 34 | if pads[0].held & BUTTON_LEFT 35 | px -= SPEED 36 | else if pads[0].held & BUTTON_RIGHT 37 | px += SPEED 38 | 39 | if pads[0].held & BUTTON_UP 40 | py -= SPEED 41 | else if pads[0].held & BUTTON_DOWN 42 | py += SPEED 43 | 44 | fn move_follower() 45 | U dir = point_dir(SS(fx), SS(fy), px, py) 46 | fx += cos(dir) 47 | fy += sin(dir) 48 | 49 | fn update_sprites() 50 | // Our stack index into OAM: 51 | U o = 0 52 | 53 | // Player: 54 | if px.b == 0 && py.b == 0 55 | set_oam(o, px.a, py.a - 1, $00, 0) 56 | o += 4 57 | 58 | // Follower: 59 | if fx.b == 0 && fy.b == 0 60 | set_oam(o, fx.a, fy.a - 1, $00, 1) 61 | o += 4 62 | 63 | // Clear the remainder of OAM 64 | hide_oam(o) 65 | 66 | nmi game_nmi() 67 | // Update OAM and poll the pads: 68 | ppu_upload_oam_poll_pads(0) 69 | 70 | // Turn on rendering: 71 | {PPUMASK}(PPUMASK_SPR_ON | PPUMASK_NO_CLIP) 72 | 73 | // Reset the scroll 74 | ppu_reset_scroll(0, 0) 75 | 76 | chrrom 77 | U[16]($FF) 78 | -------------------------------------------------------------------------------- /src/fixed.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FIXED_HPP 2 | #define FIXED_HPP 3 | 4 | #include 5 | 6 | using fixed_uint_t = std::uint64_t; 7 | using fixed_sint_t = std::int64_t; 8 | static_assert(sizeof(fixed_uint_t) >= sizeof(std::uintptr_t)); 9 | 10 | constexpr fixed_uint_t MAX_FIXED_MASK = (1ull << 56) - 1ull; 11 | 12 | struct fixed_t 13 | { 14 | fixed_uint_t value; 15 | 16 | static constexpr fixed_uint_t shift = 24; // Matches types 17 | 18 | constexpr explicit operator bool() const { return value; } 19 | constexpr auto operator<=>(fixed_t const& o) const = default; 20 | constexpr bool operator!() const { return !value; } 21 | 22 | constexpr fixed_sint_t signed_() const { return static_cast(value); } 23 | 24 | static constexpr fixed_t whole(fixed_uint_t i) { return { i << shift }; } 25 | constexpr fixed_uint_t whole() const { return value >> shift; } 26 | constexpr fixed_sint_t swhole() const { return signed_() >> shift; } 27 | }; 28 | 29 | constexpr fixed_t operator""_f(unsigned long long int i) { return { i }; } 30 | 31 | constexpr double to_double(fixed_t f) 32 | { 33 | fixed_sint_t value = static_cast(f.value); 34 | return (double)value / (double)(1ull << fixed_t::shift); 35 | } 36 | 37 | constexpr bool is_byte(fixed_t fixed) 38 | { 39 | return (fixed.value & (0xFFull << fixed_t::shift)) == fixed.value; 40 | } 41 | 42 | inline fixed_sint_t fixed_mul(fixed_sint_t lhs, fixed_sint_t rhs) 43 | { 44 | __int128 lhs128 = lhs; 45 | __int128 rhs128 = rhs; 46 | return static_cast(fixed_uint_t((lhs128 * rhs128) >> (fixed_t::shift))); 47 | } 48 | 49 | inline fixed_sint_t fixed_div(fixed_sint_t lhs, fixed_sint_t rhs) 50 | { 51 | __int128 lhs128 = lhs; 52 | __int128 rhs128 = rhs; 53 | lhs128 <<= fixed_t::shift; 54 | return static_cast(fixed_uint_t(lhs128 / rhs128)); 55 | } 56 | 57 | #endif 58 | 59 | -------------------------------------------------------------------------------- /examples/mmc5/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This demo showcases MMC5 extended attributes and expansion audio. 8 | 9 | audio(puf1_music, "music.txt") 10 | audio(puf1_sfx, "sfx.txt", "sfx.nsf") 11 | 12 | nmi main_nmi() 13 | ppu_upload_oam_poll_pads(0) 14 | ppu_reset_scroll(0, 0) 15 | {PPUMASK}(PPUMASK_BG_ON | PPUMASK_NO_CLIP) 16 | puf.process(PUF_DEFAULT) 17 | 18 | mode main() 19 | : nmi main_nmi 20 | // Init the music: 21 | puf.init(system) 22 | puf.play_track(0) 23 | 24 | // Load the palette: 25 | palette = example_palette 26 | ppu_upload_palette() 27 | 28 | mmc5_mirror_1() // Use 1-screen mirroring. 29 | 30 | // Setup the nametable: 31 | ppu_reset_addr($2000) 32 | for U y = 0; y < 30; y += 1 33 | for U x = 0; x < 32; x += 1 34 | {PPUDATA}((x & %1111) + ((y & %1111) << 4)) 35 | for U a = 0; a < 64; a += 1 36 | {PPUDATA}($FF) 37 | 38 | // Setup extended attributes: 39 | {MMC5_EXRAM_MODE}(%10) // Use an EXRAM mode that allows writing. 40 | MM ptr = mmc5_exram 41 | do for U i = 0; i; i += 1 42 | do for U j = 0; j < 4; j += 1 43 | ptr[j] = rand() & %11000000 44 | ptr += 4 45 | 46 | // Set MMC5 registers: 47 | {MMC5_EXRAM_MODE}(%01) // Use extended attribute mode. 48 | {MMC5_CHR_MODE}(0) // Use chr mode 0. 49 | mmc5_chr_select_8k(0) // Select the first bank. 50 | 51 | // Loop forever: 52 | {PPUCTRL}(PPUCTRL_NMI_ON) 53 | while true 54 | nmi 55 | update_pads() 56 | if pads[0].pressed & BUTTON_UP 57 | puf.play_sfx(0) 58 | if pads[0].pressed & BUTTON_DOWN 59 | puf.play_sfx(1) 60 | 61 | chrrom 62 | file(fmt, "bg.png") 63 | -------------------------------------------------------------------------------- /lib/logging/lua_log.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This code can be used with the 'lua_log.lua' script to write logs 8 | // that can be seen inside emulators. 9 | 10 | // Original code by Julius Riecke (Miau), original copyright him. 11 | 12 | ct AA LOG_WRITE_HOOK_ADDR = $00 13 | ct AA LOG_ARG_STORAGE_ADDR = $100 14 | 15 | // Copies a value to arg storage, so that it's visible to the lua logger. 16 | fn log_arg(U i, UU arg) 17 | : +inline 18 | UU addr = UU(LOG_ARG_STORAGE_ADDR) + UU(i << 1) 19 | {addr}(arg.a) 20 | {addr+1}(arg.b) 21 | 22 | // Logs a string 23 | fn log(PPP/ascii str) 24 | : -inline 25 | U i = 0 26 | while true 27 | {LOG_WRITE_HOOK_ADDR}(str[i]) 28 | if str[i] == ascii.sentinel 29 | break 30 | i += 1 31 | 32 | // Logs an integer as a decimal 33 | fn log_dec(UU n) 34 | log_arg(0, n) 35 | log(@"%d"ascii) 36 | 37 | // Logs an integer as hex 38 | fn log_hex(UU n) 39 | log_arg(0, n) 40 | log(@"$%04X"ascii) 41 | 42 | // 43 | // The 'logf' functions print strings with up to 4 arguments. 44 | // 'str' supports printf-style format. See Lua's string.format. 45 | // e.g. logf2("here's some variables: %u hex:%04X", var1, var2) 46 | // 47 | 48 | // 2 arguments 49 | fn log_1(PPP/ascii str, UU a) 50 | log_arg(0, a) 51 | log(str) 52 | 53 | // 2 arguments 54 | fn log_2(PPP/ascii str, UU a, UU b) 55 | log_arg(0, a) 56 | log_arg(1, b) 57 | log(str) 58 | 59 | // 3 arguments 60 | fn log_3(PPP/ascii str, UU a, UU b, UU c) 61 | log_arg(0, a) 62 | log_arg(1, b) 63 | log_arg(2, c) 64 | log(str) 65 | 66 | // 4 arguments 67 | fn log_4(PPP/ascii str, UU a, UU b, UU c, UU d) 68 | log_arg(0, a) 69 | log_arg(1, b) 70 | log_arg(2, c) 71 | log_arg(3, d) 72 | log(str) 73 | -------------------------------------------------------------------------------- /src/fnv1a.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FNV1A_HPP 2 | #define FNV1A_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // A decently-fast, endian-agnostic, non-cryptographic hash function 9 | // that is good for small strings. For big strings, check out smhasher. 10 | // Hyperlinks: 11 | // http://create.stephan-brumme.com/fnv-hash/ 12 | // http://isthe.com/chongo/tech/comp/fnv/ 13 | 14 | template 15 | struct fnv1a_constants {}; 16 | 17 | template<> 18 | struct fnv1a_constants 19 | { 20 | static constexpr std::uint32_t prime = 16777619ul; 21 | static constexpr std::uint32_t seed = 2166136261ul; 22 | }; 23 | 24 | template<> 25 | struct fnv1a_constants 26 | { 27 | static constexpr std::uint64_t prime = 1099511628211ull; 28 | static constexpr std::uint64_t seed = 14695981039346656037ull; 29 | }; 30 | 31 | template 32 | struct fnv1a 33 | { 34 | static constexpr T prime = fnv1a_constants::prime; 35 | static constexpr T seed = fnv1a_constants::seed; 36 | 37 | [[gnu::always_inline]] 38 | static constexpr inline T hash(unsigned char byte, T hashval = seed) 39 | { 40 | return (byte ^ hashval) * prime; 41 | } 42 | 43 | static constexpr T hash(char const* data, std::size_t size, T hashval = seed) 44 | { 45 | assert(data); 46 | while(size--) 47 | hashval = hash(*data++, hashval); 48 | return hashval; 49 | } 50 | 51 | static constexpr T hash(char const* begin, char const* end, T hashval = seed) 52 | { 53 | assert(begin && end); 54 | while(begin < end) 55 | hashval = hash(*begin++, hashval); 56 | return hashval; 57 | } 58 | 59 | static constexpr T hash(std::string_view view, T hashval = seed) 60 | { 61 | return hash(view.data(), view.size(), hashval); 62 | } 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/thread.hpp: -------------------------------------------------------------------------------- 1 | #ifndef THREAD_HPP 2 | #define THREAD_HPP 3 | 4 | #if defined(__MINGW32__) || defined(__MINGW64__) 5 | #define NO_THREAD 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #ifndef NO_THREAD 12 | #include 13 | #endif 14 | 15 | // MinGW has a buggy thread_local implementation. 16 | #ifdef NO_THREAD 17 | #define TLS 18 | #else 19 | #define TLS thread_local 20 | #endif 21 | 22 | // Launches a bunch of threads and waits until they finish. 23 | template 24 | void parallelize(unsigned const num_threads, Fn const& fn, OnError const& on_error) 25 | { 26 | std::atomic exception_thrown = false; 27 | 28 | #ifdef NO_THREAD 29 | fn(exception_thrown); 30 | return; 31 | #else 32 | if(num_threads == 1) 33 | { 34 | fn(exception_thrown); 35 | return; 36 | } 37 | 38 | std::vector threads; 39 | threads.reserve(num_threads); 40 | 41 | std::vector exception_ptrs; 42 | exception_ptrs.resize(num_threads, nullptr); 43 | 44 | for(unsigned i = 0; i < num_threads; ++i) 45 | { 46 | threads.emplace_back( 47 | [&fn, &exception_thrown, &on_error](std::exception_ptr& exception_ptr) 48 | { 49 | try 50 | { 51 | fn(exception_thrown); 52 | } 53 | catch(...) 54 | { 55 | exception_ptr = std::current_exception(); 56 | exception_thrown = true; 57 | on_error(); 58 | } 59 | }, std::ref(exception_ptrs[i])); 60 | } 61 | 62 | for(unsigned i = 0; i < threads.size(); ++i) 63 | threads[i].join(); 64 | 65 | threads.clear(); 66 | 67 | for(unsigned i = 0; i < num_threads; ++i) 68 | if(exception_ptrs[i]) 69 | std::rethrow_exception(exception_ptrs[i]); 70 | #endif 71 | } 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /examples/metasprite/main.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // This example displays a metasprite. 8 | 9 | data /sprites 10 | // Here's where we define our metasprite, comprised of four 8x8 tiles: 11 | [] my_metasprite 12 | (make_metasprite(0, Ms{}( 13 | Ms(-8, -8, $00, $00), 14 | Ms( 0, -8, $01, $00), 15 | Ms(-8, 0, $02, $00), 16 | Ms( 0, 0, $03, $00)))) 17 | 18 | vars 19 | // Player variables: 20 | SS px = 128 21 | SS py = 120 22 | U pflip = 0 23 | 24 | nmi game_nmi() 25 | // Update OAM and poll the pads: 26 | ppu_upload_oam_poll_pads(0) 27 | 28 | // Turn on rendering: 29 | {PPUMASK}(PPUMASK_SPR_ON | PPUMASK_NO_CLIP) 30 | 31 | // Reset the scroll 32 | ppu_reset_scroll(0, 0) 33 | 34 | mode main() 35 | : nmi game_nmi 36 | palette = example_palette 37 | ppu_upload_palette() 38 | {PPUCTRL}(PPUCTRL_NMI_ON) 39 | 40 | while true 41 | nmi 42 | update_pads() 43 | move_player() 44 | update_sprites() 45 | 46 | fn move_player() 47 | ct Int SPEED = 2 48 | 49 | if pads[0].held & BUTTON_LEFT 50 | px -= SPEED 51 | pflip |= ATTR_H_FLIP 52 | else if pads[0].held & BUTTON_RIGHT 53 | px += SPEED 54 | pflip &= ~ATTR_H_FLIP 55 | 56 | if pads[0].held & BUTTON_UP 57 | py -= SPEED 58 | pflip &= ~ATTR_V_FLIP 59 | else if pads[0].held & BUTTON_DOWN 60 | py += SPEED 61 | pflip |= ATTR_V_FLIP 62 | 63 | fn update_sprites() 64 | // Our stack index into OAM: 65 | U o = 0 66 | 67 | o = push_oam_metasprite_a(o, px, py, @my_metasprite, pflip) 68 | 69 | // Clear the remainder of OAM 70 | hide_oam(o) 71 | 72 | chrrom 73 | file(fmt, "sprite.png") 74 | -------------------------------------------------------------------------------- /lib/math/rng.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // RNG means random number generator. 8 | // This file contains code for generating random numbers. 9 | 10 | vars 11 | U[2] rng_state = U[2](1, 1) 12 | 13 | // Helper function for setting 'rng_state'. 14 | fn seed(UU value) 15 | : +inline 16 | if value == 0 17 | value = $BEEF 18 | rng_state[0] = value.a 19 | rng_state[1] = value.b 20 | 21 | // Returns a pseudo-random 8-bit value. 22 | asm fn rand() U 23 | : employs 24 | : +static_fixed 25 | default 26 | lda &rng_state+0 27 | asl 28 | rol &rng_state+1 29 | bcc done_xor_0 30 | eor #$2D // apply XOR feedback whenever a 1 bit is shifted out 31 | label done_xor_0 32 | asl 33 | rol &rng_state+1 34 | bcc done_xor_1 35 | eor #$2D 36 | label done_xor_1 37 | sta &rng_state+0 38 | sta &return 39 | rts 40 | 41 | // Returns a pseudo-random 16-bit value. 42 | fn rand_uu() UU 43 | : +inline 44 | UU result 45 | result.a = rand() 46 | result.b = rand() 47 | return result 48 | 49 | // Returns a pseudo-random 24-bit value. 50 | fn rand_uuu() UUU 51 | : +inline 52 | UUU result 53 | result.a = rand() 54 | result.b = rand() 55 | result.c = rand() 56 | return result 57 | 58 | // Returns a random value less than 'bound', or 0 if 'bound' is 0. 59 | fn randb(U bound) U 60 | if bound == 0 61 | return 0 62 | 63 | ct U[8] table = U[8](%1, %11, %111, %1111, %11111, %111111, %1111111, %11111111) 64 | 65 | U i = $FF 66 | do for U v = bound - 1; v; v >>= 1 67 | i += 1 68 | U mask = table[i] 69 | 70 | U result 71 | do while result >= bound 72 | result = rand() & mask 73 | 74 | return result 75 | -------------------------------------------------------------------------------- /src/ident_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef IDENT_MAP_HPP 2 | #define IDENT_MAP_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "robin/collection.hpp" 8 | #include "robin/table.hpp" 9 | 10 | #include "fnv1a.hpp" 11 | #include "handle.hpp" 12 | #include "pstring.hpp" 13 | 14 | // Maps identif 15 | template 16 | class ident_map_t 17 | { 18 | public: 19 | using handle_type = Handle; 20 | using value_type = typename Handle::value_type; 21 | 22 | template 23 | value_type& lookup(PString name, std::string_view key) 24 | { 25 | std::uint64_t const hash = fnv1a::hash(key.data(), key.size()); 26 | 27 | return *Handle::with_pool([&, hash, key](auto& pool) 28 | { 29 | rh::apair result = map.emplace(hash, 30 | [key](value_type* ptr) -> bool 31 | { 32 | return std::equal(key.begin(), key.end(), ptr->name.begin(), ptr->name.end()); 33 | }, 34 | [&pool, name, key]() -> value_type* 35 | { 36 | return &pool.emplace_back(name, key, pool.size()); 37 | }); 38 | 39 | return *result.first; 40 | }); 41 | } 42 | 43 | value_type* lookup(std::string_view view) 44 | { 45 | std::uint64_t const hash = fnv1a::hash(view.data(), view.size()); 46 | 47 | return Handle::with_const_pool([&, hash, view](auto const&) 48 | { 49 | auto result = map.lookup(hash, 50 | [view](value_type* ptr) -> bool 51 | { 52 | return std::equal(view.begin(), view.end(), ptr->name.begin(), ptr->name.end()); 53 | }); 54 | 55 | return result.second ? *result.second : nullptr; 56 | }); 57 | } 58 | private: 59 | rh::robin_auto_table map; 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /lib/math/bit.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | // Shifts 1 to the left by 'i' places. 8 | // 'i' must be in the range [0, 7] 9 | fn flag_bit(U i) U 10 | ct U[8] table = U[8](1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7) 11 | return table[i] 12 | 13 | // Shifts 0 to the left by 'i' places. 14 | // 'i' must be in the range [0, 7] 15 | fn flag_mask(U i) U 16 | ct U[8] table = U[8](~U(1<<0), ~U(1<<1), ~U(1<<2), ~U(1<<3), ~U(1<<4), ~U(1<<5), ~U(1<<6), ~U(1<<7)) 17 | return table[i] 18 | 19 | // Shifts 1 to the left by 'i' places. 20 | // 'i' must be in the range [0, 15] 21 | fn flag_bit_uu(U i) UU 22 | ct UU[16] table = UU[16](1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 23 | 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15) 24 | return table[i] 25 | 26 | // Shifts 0 to the left by 'i' places. 27 | // 'i' must be in the range [0, 15] 28 | fn flag_mask_uu(U i) UU 29 | ct UU[16] table = UU[16]( 30 | ~U(1<<0), ~U(1<<1), ~U(1<<2), ~U(1<<3), ~U(1<<4), ~U(1<<5), ~U(1<<6), ~U(1<<7), 31 | ~U(1<<8), ~U(1<<9), ~U(1<<10), ~U(1<<11), ~U(1<<12), ~U(1<<13), ~U(1<<14), ~U(1<<15)) 32 | return table[i] 33 | 34 | // Returns the number of bits set in the lower 4 bits of 'i': 35 | fn popcount_nybble(U i) U 36 | : +inline 37 | ct U[16] table = U[16](0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4) 38 | return table[i & $F] 39 | 40 | // Returns the number of bits set in 'i': 41 | fn popcount(U i) U 42 | : -inline 43 | U count = 0 44 | while i 45 | if i >>= 1 46 | count += 1 47 | return count 48 | 49 | // Returns the number of bits set in 'i': 50 | fn popcount_uu(U i) U 51 | : -inline 52 | U count = 0 53 | while i 54 | if i >>= 1 55 | count += 1 56 | return count 57 | -------------------------------------------------------------------------------- /src/o_locator.cpp: -------------------------------------------------------------------------------- 1 | #include "o_locator.hpp" 2 | 3 | #include "globals.hpp" 4 | #include "ir.hpp" 5 | 6 | bool o_optimize_locators(log_t* log, ir_t& ir) 7 | { 8 | bool changed = false; 9 | 10 | for(cfg_node_t const& cfg : ir) 11 | for(ssa_ht ssa_it = cfg.ssa_begin(); ssa_it; ++ssa_it) 12 | { 13 | unsigned input_size = ssa_it->input_size(); 14 | 15 | for(unsigned i = 0; i < input_size; ++i) 16 | { 17 | ssa_value_t const input = ssa_it->input(i); 18 | 19 | if(!input.is_locator()) 20 | continue; 21 | 22 | locator_t loc = input.locator(); 23 | if(loc.is() == IS_BANK) 24 | loc.set_offset(0); // Banks ignore offsets. 25 | 26 | switch(loc.lclass()) 27 | { 28 | default: 29 | break; 30 | 31 | case LOC_GCONST: 32 | if(loc.is() == IS_PTR && loc.byteified() 33 | && mod_test(loc.const_()->mods(), MOD_align) && loc.const_()->type().array_length() >= 256) 34 | { 35 | ssa_it->link_change_input(i, ssa_value_t(0u, TYPE_U)); 36 | changed = true; 37 | continue; 38 | } 39 | break; 40 | 41 | 42 | case LOC_NAMED_LABEL: 43 | // Named label banks should map to the same bank locator: 44 | if(loc.is() == IS_BANK) 45 | { 46 | global_ht const g = loc.global(); 47 | if(g->gclass() == GLOBAL_CONST) 48 | loc = locator_t::gconst(g->handle()).with_is(IS_BANK); 49 | } 50 | break; 51 | } 52 | 53 | if(loc != input.locator()) 54 | { 55 | ssa_it->link_change_input(i, loc); 56 | changed = true; 57 | } 58 | } 59 | } 60 | 61 | return changed; 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/token.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TOKEN_HPP 2 | #define TOKEN_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "lex_tables.hpp" 10 | #include "pstring.hpp" 11 | 12 | namespace bc = boost::container; 13 | 14 | struct token_t 15 | { 16 | using int_type = std::uint64_t; // Should match fixed_uint_t 17 | static_assert(sizeof(int_type) >= sizeof(std::uintptr_t)); 18 | 19 | lex::token_type_t type = {}; 20 | pstring_t pstring = {}; 21 | int_type value = {}; 22 | 23 | std::int64_t signed_() const { return static_cast(value); } 24 | 25 | void set_ptr(void const* ptr) { value = reinterpret_cast(ptr); } 26 | 27 | template 28 | T* ptr() const { return reinterpret_cast(value); } 29 | 30 | template 31 | static token_t make_ptr(lex::token_type_t type, pstring_t pstring, T const* ptr) 32 | { 33 | token_t token{ .type = type, .pstring = pstring }; 34 | token.set_ptr(ptr); 35 | return token; 36 | } 37 | 38 | // Used for debugging and logging. 39 | std::string to_string(char const* source) const; 40 | }; 41 | 42 | constexpr bool is_operator(lex::token_type_t type) 43 | { return type > lex::TOK_lparen && type < lex::TOK_rparen; } 44 | 45 | constexpr bool operator_right_assoc(lex::token_type_t type) 46 | { return lex::token_right_assoc_table[type] & 0x80; } 47 | 48 | constexpr int operator_precedence(lex::token_type_t type) 49 | { return lex::token_precedence_table[type] & 0x7F; } 50 | 51 | constexpr bool is_type_prefix(lex::token_type_t type) 52 | { return (type >= lex::TOK_Void && type <= lex::TOK_Bool) || type == lex::TOK_type_ident || type == lex::TOK_lbracket; } 53 | 54 | constexpr bool is_ident(lex::token_type_t type) 55 | { return type == lex::TOK_ident || type == lex::TOK_type_ident; } 56 | 57 | 58 | enum value_time_t : char 59 | { 60 | CT, 61 | LT, 62 | RT, 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/lt.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LT_HPP 2 | #define LT_HPP 3 | 4 | // LT = linktime 5 | 6 | #include 7 | 8 | #include "type.hpp" 9 | #include "handle.hpp" 10 | #include "parser_decl.hpp" 11 | #include "ast.hpp" 12 | #include "locator.hpp" 13 | #include "rom_decl.hpp" 14 | #include "rval.hpp" 15 | 16 | struct lt_value_t 17 | { 18 | static constexpr compiler_phase_t impl_deque_phase = PHASE_COMPILE; 19 | 20 | type_t type; 21 | ast_node_t ast; 22 | 23 | // If this was handled in 'rom_prune.hpp': 24 | bool prune_processed = false; 25 | 26 | struct result_t 27 | { 28 | rval_t rval; 29 | std::vector bytes; 30 | }; 31 | 32 | // After linking, the resolved value: 33 | std::array results; 34 | bool resolved(romv_t romv) const { return results[romv].bytes.size(); } 35 | 36 | void resolve(romv_t romv); 37 | 38 | template 39 | void for_each_locator(Fn const fn) const 40 | { 41 | for_each_locator(ast, fn); 42 | } 43 | 44 | private: 45 | template 46 | static void for_each_locator(ast_node_t const& ast, Fn const fn) 47 | { 48 | if(ast.token.type == lex::TOK_shift_atom) 49 | { 50 | fn(locator_t::from_uint(ast.uint)); 51 | return; 52 | } 53 | 54 | unsigned const num_children = ast.num_children(); 55 | for(unsigned i = 0; i < num_children; ++i) 56 | { 57 | ast_node_t const& child = ast.children[i]; 58 | 59 | if(child.token.type == lex::TOK_rpair) 60 | { 61 | for(auto const& v : child.token.ptr()->value) 62 | if(ssa_value_t const* ssa = std::get_if(&v)) 63 | if(ssa->is_locator()) 64 | fn(ssa->locator()); 65 | } 66 | else 67 | for_each_locator(child, fn); 68 | } 69 | } 70 | }; 71 | 72 | lt_ht alloc_lt_value(type_t type, ast_node_t const& expr); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/format.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FORMAT_HPP 2 | #define FORMAT_HPP 3 | 4 | // Functions for formatting strings. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | template 12 | std::string to_hex_string(T const& t) 13 | { 14 | std::stringstream ss; 15 | ss << std::hex << t; 16 | return ss.str(); 17 | } 18 | 19 | template 20 | void fmt_impl(std::ostringstream& ss, char const* str) 21 | { 22 | while(*str) 23 | ss.rdbuf()->sputc(*str++); 24 | } 25 | 26 | template 27 | void fmt_impl(std::ostringstream& ss, char const* str, T const& t, Ts const&... ts) 28 | { 29 | while(*str) 30 | { 31 | char const c = *str++; 32 | if(c == F) 33 | { 34 | ss << t; 35 | fmt_impl(ss, str, ts...); 36 | return; 37 | } 38 | else 39 | ss.rdbuf()->sputc(c); 40 | } 41 | } 42 | 43 | // A really basic wrapper around ostringstream. 44 | // Example use: fmt("value % = %", str, num) 45 | template 46 | std::string fmt(char const* str, Ts const&... ts) 47 | { 48 | std::ostringstream ss; 49 | fmt_impl(ss, str, ts...); 50 | return ss.str(); 51 | } 52 | 53 | template 54 | int ffmt(FILE* fp, char const* str, Ts const&... ts) 55 | { 56 | return std::fputs(fmt(str, ts...).c_str(), fp); 57 | } 58 | 59 | 60 | template 61 | void ezcat_impl(std::ostringstream& ss, P const& postfix) {} 62 | 63 | template 64 | void ezcat_impl(std::ostringstream& ss, P const& postfix, T const& t, Ts const&... ts) 65 | { 66 | ss << t << postfix; 67 | ezcat_impl(ss, postfix, ts...); 68 | } 69 | 70 | // Just combines the string representations together. 71 | template 72 | std::string ezcat(P const& postfix, Ts const&... ts) 73 | { 74 | std::ostringstream ss; 75 | ezcat_impl(ss, postfix, ts...); 76 | return ss.str(); 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /src/eval.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INTERPRET_HPP 2 | #define INTERPRET_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "flat/small_map.hpp" 12 | 13 | #include "bitset.hpp" 14 | #include "pstring.hpp" 15 | #include "globals.hpp" 16 | #include "byte_block.hpp" 17 | 18 | namespace bc = ::boost::container; 19 | 20 | class ir_t; 21 | class fn_t; 22 | class eval_t; 23 | class type_t; 24 | class locator_t; 25 | struct rpair_t; 26 | struct pstring_t; 27 | struct token_t; 28 | 29 | // Thrown when the interpreter takes too much time to complete 30 | struct out_of_time_t : public std::exception 31 | { 32 | explicit out_of_time_t(std::string const& msg) 33 | : msg(msg) {} 34 | 35 | virtual const char* what() const noexcept { return msg.c_str(); } 36 | std::string msg; 37 | }; 38 | 39 | struct var_lookup_error_t : public std::exception 40 | { 41 | virtual const char* what() const noexcept { return "Failed var lookup."; } 42 | }; 43 | 44 | struct fn_not_rt_t : public std::exception 45 | { 46 | explicit fn_not_rt_t(pstring_t pstring) : pstring(pstring) {} 47 | 48 | virtual const char* what() const noexcept { return "Function is not defined for run-time."; } 49 | pstring_t pstring; 50 | }; 51 | 52 | rpair_t interpret_local_const(pstring_t pstring, fn_t* fn, ast_node_t const& expr, 53 | type_t expected_type, local_const_t const* local_consts); 54 | 55 | rpair_t interpret_expr(pstring_t pstring, ast_node_t const& ast, 56 | type_t expected_type, eval_t* env = nullptr, 57 | local_const_t const* local_consts = nullptr); 58 | 59 | byte_block_data_t interpret_byte_block( 60 | pstring_t pstring, ast_node_t const& ast, fn_t* fn = nullptr, 61 | local_const_t const* local_consts = nullptr); 62 | 63 | rpair_t interpret_lt(romv_t romv, ast_node_t const& ast, type_t expected_type); 64 | 65 | precheck_tracked_t build_tracked(fn_t& fn, local_const_t const* local_consts); 66 | 67 | void build_ir(ir_t& ir, fn_t& fn); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /lib/mapper/mmc3.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | /////////////////////////////////////////////////////////////////////////////// 8 | // Helper code for interfacing MMC3 /////////////////////////////////////////// 9 | /////////////////////////////////////////////////////////////////////////////// 10 | 11 | // IMPORTANT NOTE: 12 | // 13 | // In NESFab's implementation of MMC3, 14 | // the two highest bits of $8000 cannot and should not be set. 15 | // When writing to $8000, leave the two highest bits zero. 16 | // Otherwise you will break the bankswitching behavior. 17 | 18 | // Registers: 19 | ct AA MMC3_BANK_SELECT = $8000 20 | ct AA MMC3_BANK_DATA = $8001 21 | ct AA MMC3_MIRRORING = $A000 22 | ct AA MMC3_RAM_PROTECT = $A001 23 | ct AA MMC3_IRQ_LATCH = $C000 24 | ct AA MMC3_IRQ_RELOAD = $C001 25 | ct AA MMC3_IRQ_DISABLE = $E000 26 | ct AA MMC3_IRQ_ENABLE = $E001 27 | 28 | // Changes the MMC3 CHR bank by writing to $8000 then $8001. 29 | fn mmc3_select(U bank_select, U bank_data) 30 | : +inline 31 | {&__mapper_detail}(bank_select) // Shadow register used by the runtime. 32 | {MMC3_BANK_SELECT, MMC3_BANK_DATA}(bank_select, bank_data) 33 | 34 | // Changes the MMC3 CHR bank. This is the unsafe bank switch version. 35 | fn mmc3_select_unsafe(U bank_select, U bank_data) 36 | : +inline 37 | {MMC3_BANK_SELECT, MMC3_BANK_DATA}(bank_select, bank_data) 38 | 39 | // Triggers IRQ after a set number of scanlines: 40 | fn mmc3_timer(U scanlines) 41 | : +inline 42 | {MMC3_IRQ_DISABLE}(scanlines) 43 | {MMC3_IRQ_LATCH}(scanlines) 44 | {MMC3_IRQ_RELOAD}(scanlines) 45 | {MMC3_IRQ_DISABLE}(scanlines) 46 | {MMC3_IRQ_ENABLE}(scanlines) 47 | 48 | // Resets the MMC3 mapper state to known values. 49 | fn mmc3_reset() 50 | {MMC3_RAM_PROTECT}($80) 51 | {MMC3_IRQ_DISABLE}(0) 52 | {MMC3_MIRRORING}(0) 53 | mmc3_select(0, 0) 54 | mmc3_select(1, 0) 55 | mmc3_select(2, 0) 56 | mmc3_select(3, 0) 57 | mmc3_select(4, 0) 58 | mmc3_select(5, 0) 59 | -------------------------------------------------------------------------------- /src/convert_map.cpp: -------------------------------------------------------------------------------- 1 | #include "convert_png.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "format.hpp" 8 | #include "lodepng/lodepng.h" 9 | 10 | std::vector map_to_nt(std::uint8_t const* map, std::size_t size) 11 | { 12 | if(size < 4) 13 | throw convert_error_t(".map file contains no footer."); 14 | 15 | // Extract the width and height: 16 | unsigned const width = map[size-4] | (map[size-3] << 8); 17 | unsigned const height = map[size-2] | (map[size-1] << 8); 18 | 19 | if(width == 0 || height == 0 || width % 32 != 0 || height % 30 != 0) 20 | throw convert_error_t(fmt(".map file must be divisible into 32x30 nametables. Specified size is %x%.", width, height)); 21 | 22 | unsigned const expected_size = (width * height) + (width*height)/16 + 8; 23 | if(expected_size != size) 24 | throw convert_error_t(fmt("Invalid %x% .map file. Size of % does not match expected size of %.", 25 | width, height, size, expected_size)); 26 | 27 | std::uint8_t const* tiles = map; 28 | std::uint8_t const* attrs = map + (width * height); 29 | unsigned const nt_w = width / 32; 30 | unsigned const nt_h = height / 30; 31 | 32 | std::vector result; 33 | 34 | for(unsigned ny = 0; ny < nt_h; ++ny) 35 | for(unsigned nx = 0; nx < nt_w; ++nx) 36 | { 37 | for(unsigned y = 0; y < 30; ++y) 38 | for(unsigned x = 0; x < 32; ++x) 39 | result.push_back(tiles[(nx*32 + x) + (ny*30 + y)*width]); 40 | 41 | for(unsigned y = 0; y < 8; ++y) 42 | for(unsigned x = 0; x < 8; ++x) 43 | { 44 | // Man... fuck attributes. 45 | unsigned const i = (nx*8 + x) + y*width/4 + (ny*7*width)/4 + (ny/2)*width/4; 46 | unsigned attr = attrs[i]; 47 | 48 | if(ny % 2) 49 | { 50 | attr <<= 4; 51 | attr |= attrs[i + width/4] >> 4; 52 | } 53 | 54 | if(y == 7) 55 | attr &= 0xF; 56 | 57 | result.push_back(attr); 58 | } 59 | } 60 | 61 | return result; 62 | } 63 | -------------------------------------------------------------------------------- /src/flat/impl/class_def.hpp: -------------------------------------------------------------------------------- 1 | // Copyright Pubby 2016 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // http://www.boost.org/LICENSE_1_0.txt 4 | 5 | private: 6 | using D = FLATNAME; 7 | using Key = FLATKEY; 8 | public: 9 | #include "container_traits.hpp" 10 | 11 | FLATNAME() = default; 12 | explicit FLATNAME(Compare const& comp) : B(comp) {} 13 | 14 | template 15 | FLATNAME(InputIt first, InputIt last) 16 | : FLATNAME() { this->insert(first, last); } 17 | 18 | template 19 | FLATNAME(InputIt first, InputIt last, Compare const& comp) 20 | : FLATNAME(comp) { this->insert(first, last); } 21 | 22 | template 23 | FLATNAME(InputIt first, InputIt last, delay_sort_t d) 24 | : FLATNAME() { this->insert(first, last, d); } 25 | 26 | template 27 | FLATNAME(InputIt first, InputIt last, Compare const& comp, delay_sort_t d) 28 | : FLATNAME(comp) { this->insert(first, last, d); } 29 | 30 | FLATNAME(FLATNAME const&) = default; 31 | FLATNAME(FLATNAME&&) = default; 32 | 33 | FLATNAME(std::initializer_list ilist) 34 | : FLATNAME() { this->insert(ilist); } 35 | 36 | FLATNAME(std::initializer_list ilist, delay_sort_t d) 37 | : FLATNAME() { this->insert(ilist, d); } 38 | 39 | FLATNAME(std::initializer_list ilist, 40 | Compare const& comp, delay_sort_t d) 41 | : FLATNAME(comp) { this->insert(ilist, d); } 42 | 43 | template 44 | explicit FLATNAME(container_construct_t, Args&&... args) 45 | : container(std::forward(args)...), Compare() {} 46 | 47 | template 48 | FLATNAME(Compare const& comp, container_construct_t, Args&&... args) 49 | : container(std::forward(args)...), Compare(comp) {} 50 | 51 | FLATNAME& operator=(FLATNAME const&) = default; 52 | FLATNAME& operator=(FLATNAME&&) = default; 53 | FLATNAME& operator=(std::initializer_list ilist) 54 | { this->clear(); this->insert(ilist); return *this; } 55 | 56 | Container container; 57 | 58 | #undef FLATNAME 59 | #undef FLATKEY 60 | -------------------------------------------------------------------------------- /src/ast.cpp: -------------------------------------------------------------------------------- 1 | #include "ast.hpp" 2 | 3 | #include "assert.hpp" 4 | 5 | unsigned ast_node_t::num_children() const 6 | { 7 | using namespace lex; 8 | 9 | switch(token.type) 10 | { 11 | case TOK_apply: 12 | case TOK_mode_apply: 13 | case TOK_cast: 14 | case TOK_implicit_cast: 15 | case TOK_byte_block_proc: 16 | case TOK_byte_block_data: 17 | case TOK_byte_block_if: 18 | case TOK_min: 19 | case TOK_max: 20 | case TOK_read_hw: 21 | case TOK_write_hw: 22 | case TOK_push: 23 | case TOK_pop: 24 | assert(!token.value || children); 25 | return token.value; 26 | 27 | case TOK_at: 28 | case TOK_unary_plus: 29 | case TOK_unary_minus: 30 | case TOK_unary_xor: 31 | case TOK_unary_negate: 32 | case TOK_unary_ref: 33 | case TOK_sizeof_expr: 34 | case TOK_len_expr: 35 | case TOK_period: 36 | case TOK_byte_block_call: 37 | case TOK_byte_block_goto: 38 | case TOK_byte_block_goto_mode: 39 | case TOK_write_state: 40 | case TOK_abs: 41 | return 1; 42 | 43 | case TOK_byte_block_asm_op: 44 | return children ? 1 : 0; 45 | 46 | case TOK_index8: 47 | case TOK_index16: 48 | case TOK_replace_atom: 49 | case TOK_read: 50 | return 2; 51 | 52 | case TOK_write: 53 | return 3; 54 | 55 | default: 56 | if(is_operator(token.type)) 57 | { 58 | assert(children); 59 | return 2; 60 | } 61 | 62 | passert(!children, token_string(token.type), token.type); 63 | // fall-through 64 | // These use other pointers in the union instead of 'children': 65 | case TOK_character: 66 | case TOK_string_compressed: 67 | case TOK_string_uncompressed: 68 | case TOK_shift_atom: 69 | case TOK_state: 70 | case TOK_byte_vec: 71 | case TOK_locator_vec: 72 | return 0; 73 | } 74 | } 75 | 76 | void ast_node_t::weaken_idents() 77 | { 78 | if(token.type == lex::TOK_ident) 79 | { 80 | token.type = lex::TOK_weak_ident; 81 | return; 82 | } 83 | 84 | unsigned const n = num_children(); 85 | for(unsigned i = 0; i < n; ++i) 86 | children[i].weaken_idents(); 87 | } 88 | -------------------------------------------------------------------------------- /src/o_shift.cpp: -------------------------------------------------------------------------------- 1 | #include "o_defork.hpp" 2 | 3 | #include "ir.hpp" 4 | #include "runtime.hpp" 5 | 6 | bool o_shl_tables(log_t* log, ir_t& ir) 7 | { 8 | bool modified = false; 9 | 10 | for(cfg_node_t& cfg : ir) 11 | for(ssa_ht ssa_it = cfg.ssa_begin(); ssa_it; ++ssa_it) 12 | { 13 | if(ssa_it->op() != SSA_shl || (ssa_it->type() != TYPE_U && ssa_it->type() != TYPE_S) || carry_used(*ssa_it)) 14 | continue; 15 | 16 | ssa_value_t const value = ssa_it->input(0); 17 | ssa_value_t const amount_v = ssa_it->input(1); 18 | if(!amount_v.is_num()) 19 | continue; 20 | unsigned amount = amount_v.whole(); 21 | 22 | if(amount == 7) 23 | { 24 | // This can be implemented as 'ALR #1' followed by 'ROR'. 25 | ssa_ht const masked = cfg.emplace_ssa(SSA_and, value.type(), value, ssa_value_t(1, value.type().name())); 26 | ssa_ht const ror = cfg.emplace_ssa(SSA_ror, value.type(), masked, ssa_value_t(0, TYPE_BOOL)); 27 | ssa_ht const carry = cfg.emplace_ssa(SSA_carry, TYPE_BOOL, ror); 28 | 29 | ssa_it->link_change_input(0, ror); 30 | ssa_it->link_change_input(1, carry); 31 | ssa_it->unsafe_set_op(SSA_ror); 32 | 33 | if(!carry_output(*ssa_it)) 34 | cfg.emplace_ssa(SSA_carry, TYPE_BOOL, ssa_it); 35 | 36 | modified = true; 37 | } 38 | else if(amount >= MIN_SHL_TABLE && amount <= MAX_SHL_TABLE) 39 | { 40 | // When using the tables, we have to mask the input to be in bounds. 41 | unsigned const mask = 0xFF >> amount; 42 | ssa_ht const masked = cfg.emplace_ssa(SSA_and, value.type(), value, ssa_value_t(mask, value.type().name())); 43 | 44 | ssa_it->link_change_input(0, masked); 45 | ssa_it->unsafe_set_op(SSA_shl_table); 46 | 47 | // Remove the carry if it exists. 48 | if(ssa_ht carry = carry_output(*ssa_it)) 49 | { 50 | assert(carry->output_size() == 0); 51 | carry->prune(); 52 | } 53 | 54 | modified = true; 55 | } 56 | } 57 | 58 | return modified; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /examples/hang_glider/cliff.fab: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Patrick Bene 3 | * This file is distributed under the Boost Software License, Version 1.0. 4 | * See LICENSE_1_0.txt or https://www.boost.org/LICENSE_1_0.txt 5 | */ 6 | 7 | ct U SCROLL_HEIGHT = 60 8 | 9 | struct Cliff 10 | U begin_hole 11 | U end_hole 12 | 13 | vars /game 14 | U cliff_hole = 4 15 | U cliff_midpoint = 15 16 | U cliff_index = 0 17 | U cliff_y = 30 18 | Cliff[16] cliffs = Cliff[16](Cliff(1, 31)) 19 | 20 | 21 | fn update_cliff() 22 | cliff_y += 1 23 | 24 | if cliff_y == SCROLL_HEIGHT 25 | cliff_y = 0 26 | 27 | cliff_index += 1 28 | 29 | if cliff_index & %1 30 | return 31 | 32 | U i = (cliff_index & %11111) >> 1 33 | 34 | if cliff_index & %1110 35 | cliffs[i].begin_hole = 1 36 | cliffs[i].end_hole = 31 37 | else 38 | U move_amount = randb(12 - cliff_hole) 39 | 40 | if cliff_midpoint < 16 41 | cliff_midpoint += move_amount 42 | else 43 | cliff_midpoint -= move_amount 44 | 45 | if rand() & %111 == 0 46 | cliffs[i].begin_hole = cliff_midpoint - cliff_hole 47 | cliffs[i].end_hole = cliff_midpoint + cliff_hole 48 | else if cliff_index & %10000 49 | cliffs[i].begin_hole = cliff_midpoint - cliff_hole 50 | cliffs[i].end_hole = 31 51 | else 52 | cliffs[i].begin_hole = 1 53 | cliffs[i].end_hole = cliff_midpoint + cliff_hole 54 | 55 | 56 | 57 | ct fn cliff_ppuaddr_table() UU[SCROLL_HEIGHT] 58 | UU[60] ret 59 | for U i = 0; i < 30; i += 1 60 | ret[i] = $2000 + (UU(i) << 5) 61 | for U i = 0; i < 30; i += 1 62 | ret[i+30] = $2800 + (UU(i) << 5) 63 | return ret 64 | 65 | fn ppu_upload_cliff() 66 | {PPUSTATUS}() 67 | {PPUADDR}(cliff_ppuaddr_table()[cliff_y].b) 68 | {PPUADDR}(cliff_ppuaddr_table()[cliff_y].a) 69 | 70 | Cliff cliff = cliffs[(cliff_index & %11111) >> 1] 71 | U i = 0 72 | 73 | for ; i < cliff.begin_hole; i += 1 74 | {PPUDATA}($01) 75 | for ; i < cliff.end_hole; i += 1 76 | {PPUDATA}($00) 77 | for ; i < 32; i += 1 78 | {PPUDATA}($01) 79 | 80 | -------------------------------------------------------------------------------- /examples/wifi/server/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // import libraries 4 | const { performance } = require("perf_hooks"); 5 | const dgram = require("dgram"); 6 | 7 | // set some constants 8 | const HEARTBEAT_INTERVAL = 10000; // 10 seconds 9 | const HTTP_PORT = 1234; 10 | 11 | const server = dgram.createSocket("udp4"); 12 | const client = dgram.createSocket("udp4"); 13 | 14 | let users = {}; 15 | 16 | server.on("listening", function () { 17 | let address = server.address(); 18 | console.log( 19 | "UDP Server listening on " + address.address + ":" + address.port 20 | ); 21 | }); 22 | 23 | server.on("message", (data, remote) => { 24 | let msg_code = data.readUInt8(0); 25 | console.log(`receive: ${msg_code}`); 26 | 27 | users[`${remote.address}:${remote.port}`] = { address: remote.address, port: remote.port, lastSeen: performance.now() }; 28 | 29 | if(msg_code > 0) { 30 | // Send the message to all users: 31 | for (const [key, info] of Object.entries(users)) { 32 | console.log(`send to ${key}: ${msg_code}`); 33 | client.send(new Uint8Array([msg_code]), info.port, info.address); 34 | } 35 | } 36 | }); 37 | 38 | // start listening 39 | server.bind(HTTP_PORT); 40 | 41 | // heartbeat handler 42 | const interval = setInterval(function ping() { 43 | for (const [key, info] of Object.entries(users)) { 44 | // check when we saw the user for the last time 45 | if (performance.now() - info.lastSeen > HEARTBEAT_INTERVAL) { 46 | delete users.key; 47 | console.log( 48 | `${address} has been disconnected (${ 49 | (performance.now() - info.lastSeen) / 1000 50 | }).` 51 | ); 52 | } 53 | } 54 | }, HEARTBEAT_INTERVAL); 55 | 56 | // helpers to convert ArrayBuffer <=> String 57 | // source: http://stackoverflow.com/a/11058858 58 | function ab2str(buf) { 59 | return String.fromCharCode.apply(null, new Uint8Array(buf)); 60 | } 61 | function str2ab(str) { 62 | var buf = new ArrayBuffer(str.length); 63 | var bufView = new Uint8Array(buf); 64 | for (var i = 0, strLen = str.length; i < strLen; i++) { 65 | bufView[i] = str.charCodeAt(i); 66 | } 67 | return buf; 68 | } 69 | --------------------------------------------------------------------------------