├── roms ├── Tron.cfg ├── Vers.cfg ├── Puzzle.cfg ├── Timebomb.cfg ├── X-Mirror.cfg ├── Blinky.cfg ├── Rocket_Launcher.cfg ├── Addition_Problems.cfg ├── Blinky_alt.cfg ├── Landing.cfg ├── Airplane.cfg ├── Brix.ch8 ├── Cave.ch8 ├── Missile.cfg ├── Nim.ch8 ├── Pong.ch8 ├── Rocket.cfg ├── Tank.ch8 ├── Tron.ch8 ├── UFO.ch8 ├── Vers.ch8 ├── Wall.ch8 ├── Blinky.ch8 ├── Blitz.ch8 ├── Bowling.ch8 ├── Brick.ch8 ├── Craps.ch8 ├── Figures.ch8 ├── Filter.ch8 ├── Guess.ch8 ├── Hi-Lo.ch8 ├── Hidden.ch8 ├── Landing.ch8 ├── Merlin.ch8 ├── Missile.ch8 ├── Paddles.ch8 ├── Puzzle.ch8 ├── Reversi.ch8 ├── Rocket.ch8 ├── Slide.ch8 ├── Soccer.ch8 ├── Squash.ch8 ├── Sum_Fun.ch8 ├── Syzygy.ch8 ├── Tetris.ch8 ├── Worm_V4.ch8 ├── 15_Puzzle.ch8 ├── Airplane.ch8 ├── Biorhythm.ch8 ├── Breakout.ch8 ├── Brix.cfg ├── Connect_4.ch8 ├── Guess_alt.ch8 ├── Pong_alt.ch8 ├── Rush_Hour.ch8 ├── Submarine.ch8 ├── Tapeworm.ch8 ├── Timebomb.ch8 ├── Wall.cfg ├── Wipe_Off.ch8 ├── X-Mirror.ch8 ├── ZeroPong.cfg ├── ZeroPong.ch8 ├── Animal_Race.ch8 ├── Astro_Dodge.ch8 ├── Blinky_alt.ch8 ├── Deflection.ch8 ├── Kaleidoscope.ch8 ├── Lunar_Lander.ch8 ├── Rocket_Launch.cfg ├── Space_Flight.ch8 ├── Spooky_Spot.ch8 ├── Tic-Tac-Toe.ch8 ├── 15_Puzzle_alt.ch8 ├── Coin_Flipping.ch8 ├── Pong_1_player.ch8 ├── Rocket_Launch.ch8 ├── Rush_Hour_alt.ch8 ├── Sequence_Shoot.ch8 ├── Shooting_Stars.ch8 ├── Space_Invaders.ch8 ├── Squash.cfg ├── Vertical_Brix.ch8 ├── test │ ├── Ibm_logo.ch8 │ └── test_opcode.ch8 ├── Addition_Problems.ch8 ├── Pong_1_player.cfg ├── Pong_2_Pong_hack.ch8 ├── Rocket_Launcher.ch8 ├── Russian_Roulette.ch8 ├── Space_Intercept.ch8 ├── Vertical_Brix.cfg ├── Wipe_Off.cfg ├── Breakout_Brix_hack.ch8 ├── Cave.cfg ├── Figures.cfg ├── Most_Dangerous_Game.ch8 ├── Space_Invaders_alt.ch8 ├── Programmable_Spacefighters.ch8 ├── Tapeworm.cfg ├── Soccer.cfg ├── Space_Flight.cfg ├── Mastermind_FourRow_Robert_Lindley,_1978.ch8 ├── Brick.cfg ├── Filter.cfg ├── Pong_alt.cfg ├── Pong_2_Pong_hack.cfg ├── Paddles.cfg ├── Rush_Hour_alt.cfg ├── Breakout.cfg ├── Tic-Tac-Toe.cfg ├── Nim.cfg ├── Russian_Roulette.cfg ├── Space_Invaders_alt.cfg ├── Shooting_Stars.cfg ├── Coin_Flipping.cfg ├── Guess_alt.cfg ├── Space_Invaders.cfg ├── Guess.cfg ├── Craps.cfg ├── Blitz.cfg ├── Tank.cfg ├── Breakout_Brix_hack.cfg ├── Pong.cfg ├── Hi-Lo.cfg ├── 15_Puzzle_alt.cfg ├── Spooky_Spot.cfg ├── Submarine.cfg ├── Space_Intercept.cfg ├── Merlin.cfg ├── Connect_4.cfg ├── Kaleidoscope.cfg ├── README.md ├── Hidden.cfg ├── Slide.cfg ├── Sum_Fun.cfg ├── Sequence_Shoot.cfg ├── UFO.cfg ├── Rush_Hour.cfg ├── Animal_Race.cfg ├── Tetris.cfg ├── Mastermind_FourRow_Robert_Lindley,_1978.cfg ├── Worm_V4.cfg ├── Astro_Dodge.cfg ├── 15_Puzzle.cfg ├── Bowling.cfg ├── Most_Dangerous_Game.cfg ├── Reversi.cfg ├── Deflection.cfg ├── Syzygy.cfg ├── Biorhythm.cfg ├── Lunar_Lander.cfg └── Programmable_Spacefighters.cfg ├── assets ├── quirks_sprite.png ├── demo_grid.gif └── demo_grid.mp4 ├── .gitattributes ├── .editorconfig ├── demo.cpp ├── .gitignore ├── include ├── keypad.hpp ├── chip8_tester.hpp ├── term.h ├── cfg_parser.hpp ├── frontend.hpp └── chip8.hpp ├── .github └── workflows │ └── c-cpp.yml ├── Makefile ├── LICENSE ├── test └── tests.cpp ├── src ├── cfg_parser.cpp └── chip8.cpp └── README.md /roms/Tron.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | on 3 | -------------------------------------------------------------------------------- /roms/Vers.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | on 3 | -------------------------------------------------------------------------------- /roms/Puzzle.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | on 3 | -------------------------------------------------------------------------------- /roms/Timebomb.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | on 3 | -------------------------------------------------------------------------------- /roms/X-Mirror.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | on 3 | -------------------------------------------------------------------------------- /roms/Blinky.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | 2000 3 | on 4 | -------------------------------------------------------------------------------- /roms/Rocket_Launcher.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | on 3 | -------------------------------------------------------------------------------- /roms/Addition_Problems.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | on 3 | -------------------------------------------------------------------------------- /roms/Blinky_alt.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | 2000 3 | on 4 | -------------------------------------------------------------------------------- /roms/Landing.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | 0x8: Shoot 3 | on 4 | -------------------------------------------------------------------------------- /roms/Airplane.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x8: Shoot 4 | on 5 | -------------------------------------------------------------------------------- /roms/Brix.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Brix.ch8 -------------------------------------------------------------------------------- /roms/Cave.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Cave.ch8 -------------------------------------------------------------------------------- /roms/Missile.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x8: Shoot 4 | on 5 | -------------------------------------------------------------------------------- /roms/Nim.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Nim.ch8 -------------------------------------------------------------------------------- /roms/Pong.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Pong.ch8 -------------------------------------------------------------------------------- /roms/Rocket.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0xf: Launch 4 | on 5 | -------------------------------------------------------------------------------- /roms/Tank.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Tank.ch8 -------------------------------------------------------------------------------- /roms/Tron.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Tron.ch8 -------------------------------------------------------------------------------- /roms/UFO.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/UFO.ch8 -------------------------------------------------------------------------------- /roms/Vers.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Vers.ch8 -------------------------------------------------------------------------------- /roms/Wall.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Wall.ch8 -------------------------------------------------------------------------------- /roms/Blinky.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Blinky.ch8 -------------------------------------------------------------------------------- /roms/Blitz.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Blitz.ch8 -------------------------------------------------------------------------------- /roms/Bowling.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Bowling.ch8 -------------------------------------------------------------------------------- /roms/Brick.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Brick.ch8 -------------------------------------------------------------------------------- /roms/Craps.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Craps.ch8 -------------------------------------------------------------------------------- /roms/Figures.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Figures.ch8 -------------------------------------------------------------------------------- /roms/Filter.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Filter.ch8 -------------------------------------------------------------------------------- /roms/Guess.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Guess.ch8 -------------------------------------------------------------------------------- /roms/Hi-Lo.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Hi-Lo.ch8 -------------------------------------------------------------------------------- /roms/Hidden.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Hidden.ch8 -------------------------------------------------------------------------------- /roms/Landing.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Landing.ch8 -------------------------------------------------------------------------------- /roms/Merlin.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Merlin.ch8 -------------------------------------------------------------------------------- /roms/Missile.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Missile.ch8 -------------------------------------------------------------------------------- /roms/Paddles.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Paddles.ch8 -------------------------------------------------------------------------------- /roms/Puzzle.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Puzzle.ch8 -------------------------------------------------------------------------------- /roms/Reversi.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Reversi.ch8 -------------------------------------------------------------------------------- /roms/Rocket.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Rocket.ch8 -------------------------------------------------------------------------------- /roms/Slide.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Slide.ch8 -------------------------------------------------------------------------------- /roms/Soccer.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Soccer.ch8 -------------------------------------------------------------------------------- /roms/Squash.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Squash.ch8 -------------------------------------------------------------------------------- /roms/Sum_Fun.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Sum_Fun.ch8 -------------------------------------------------------------------------------- /roms/Syzygy.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Syzygy.ch8 -------------------------------------------------------------------------------- /roms/Tetris.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Tetris.ch8 -------------------------------------------------------------------------------- /roms/Worm_V4.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Worm_V4.ch8 -------------------------------------------------------------------------------- /roms/15_Puzzle.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/15_Puzzle.ch8 -------------------------------------------------------------------------------- /roms/Airplane.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Airplane.ch8 -------------------------------------------------------------------------------- /roms/Biorhythm.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Biorhythm.ch8 -------------------------------------------------------------------------------- /roms/Breakout.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Breakout.ch8 -------------------------------------------------------------------------------- /roms/Brix.cfg: -------------------------------------------------------------------------------- 1 | # controls 2 | 0x6: right 3 | 0x4: left 4 | # frequency 5 | 350 6 | on 7 | -------------------------------------------------------------------------------- /roms/Connect_4.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Connect_4.ch8 -------------------------------------------------------------------------------- /roms/Guess_alt.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Guess_alt.ch8 -------------------------------------------------------------------------------- /roms/Pong_alt.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Pong_alt.ch8 -------------------------------------------------------------------------------- /roms/Rush_Hour.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Rush_Hour.ch8 -------------------------------------------------------------------------------- /roms/Submarine.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Submarine.ch8 -------------------------------------------------------------------------------- /roms/Tapeworm.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Tapeworm.ch8 -------------------------------------------------------------------------------- /roms/Timebomb.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Timebomb.ch8 -------------------------------------------------------------------------------- /roms/Wall.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x4: Left 4 | 0x6: Right 5 | on 6 | -------------------------------------------------------------------------------- /roms/Wipe_Off.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Wipe_Off.ch8 -------------------------------------------------------------------------------- /roms/X-Mirror.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/X-Mirror.ch8 -------------------------------------------------------------------------------- /roms/ZeroPong.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x2: up 4 | 0x8: down 5 | on 6 | -------------------------------------------------------------------------------- /roms/ZeroPong.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/ZeroPong.ch8 -------------------------------------------------------------------------------- /roms/Animal_Race.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Animal_Race.ch8 -------------------------------------------------------------------------------- /roms/Astro_Dodge.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Astro_Dodge.ch8 -------------------------------------------------------------------------------- /roms/Blinky_alt.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Blinky_alt.ch8 -------------------------------------------------------------------------------- /roms/Deflection.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Deflection.ch8 -------------------------------------------------------------------------------- /roms/Kaleidoscope.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Kaleidoscope.ch8 -------------------------------------------------------------------------------- /roms/Lunar_Lander.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Lunar_Lander.ch8 -------------------------------------------------------------------------------- /roms/Rocket_Launch.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | 0x4: left 3 | 0x6: right 4 | 2000 5 | on 6 | -------------------------------------------------------------------------------- /roms/Space_Flight.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Space_Flight.ch8 -------------------------------------------------------------------------------- /roms/Spooky_Spot.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Spooky_Spot.ch8 -------------------------------------------------------------------------------- /roms/Tic-Tac-Toe.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Tic-Tac-Toe.ch8 -------------------------------------------------------------------------------- /roms/15_Puzzle_alt.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/15_Puzzle_alt.ch8 -------------------------------------------------------------------------------- /roms/Coin_Flipping.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Coin_Flipping.ch8 -------------------------------------------------------------------------------- /roms/Pong_1_player.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Pong_1_player.ch8 -------------------------------------------------------------------------------- /roms/Rocket_Launch.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Rocket_Launch.ch8 -------------------------------------------------------------------------------- /roms/Rush_Hour_alt.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Rush_Hour_alt.ch8 -------------------------------------------------------------------------------- /roms/Sequence_Shoot.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Sequence_Shoot.ch8 -------------------------------------------------------------------------------- /roms/Shooting_Stars.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Shooting_Stars.ch8 -------------------------------------------------------------------------------- /roms/Space_Invaders.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Space_Invaders.ch8 -------------------------------------------------------------------------------- /roms/Squash.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x1: up 4 | 0x4: down 5 | 250 6 | on 7 | -------------------------------------------------------------------------------- /roms/Vertical_Brix.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Vertical_Brix.ch8 -------------------------------------------------------------------------------- /roms/test/Ibm_logo.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/test/Ibm_logo.ch8 -------------------------------------------------------------------------------- /assets/quirks_sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/assets/quirks_sprite.png -------------------------------------------------------------------------------- /roms/Addition_Problems.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Addition_Problems.ch8 -------------------------------------------------------------------------------- /roms/Pong_1_player.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x1: up 4 | 0x4: down 5 | 400 6 | on 7 | -------------------------------------------------------------------------------- /roms/Pong_2_Pong_hack.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Pong_2_Pong_hack.ch8 -------------------------------------------------------------------------------- /roms/Rocket_Launcher.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Rocket_Launcher.ch8 -------------------------------------------------------------------------------- /roms/Russian_Roulette.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Russian_Roulette.ch8 -------------------------------------------------------------------------------- /roms/Space_Intercept.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Space_Intercept.ch8 -------------------------------------------------------------------------------- /roms/Vertical_Brix.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x1: Up 4 | 0x4: Down 5 | 1000 6 | on 7 | -------------------------------------------------------------------------------- /roms/Wipe_Off.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x4: left 4 | 0x6: right 5 | 500 6 | on 7 | -------------------------------------------------------------------------------- /roms/test/test_opcode.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/test/test_opcode.ch8 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.gif filter=lfs diff=lfs merge=lfs -text 2 | *.mp4 filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /roms/Breakout_Brix_hack.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Breakout_Brix_hack.ch8 -------------------------------------------------------------------------------- /roms/Cave.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | 0x2: Up 3 | 0x8: Down 4 | 0x4: Left 5 | 0x6: Right 6 | 150 7 | on 8 | -------------------------------------------------------------------------------- /roms/Figures.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | 0x5: move left 3 | 0x6: move right 4 | 0x1: drop 5 | 600 6 | on 7 | -------------------------------------------------------------------------------- /roms/Most_Dangerous_Game.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Most_Dangerous_Game.ch8 -------------------------------------------------------------------------------- /roms/Space_Invaders_alt.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Space_Invaders_alt.ch8 -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | indent_style = tab 4 | indent_size = 4 5 | trim_trailing_whitespace = true 6 | -------------------------------------------------------------------------------- /roms/Programmable_Spacefighters.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Programmable_Spacefighters.ch8 -------------------------------------------------------------------------------- /roms/Tapeworm.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | 0x2: up 3 | 0x4: left 4 | 0x6: right 5 | 0x8: down 6 | 0xf: start 7 | 1000 8 | on 9 | -------------------------------------------------------------------------------- /roms/Soccer.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unspecified 2 | # Controls: 3 | 0x1: Up (P1) 4 | 0x4: Down (P1) 5 | 0xc: Up (P2) 6 | 0xd: Down (P2) 7 | on 8 | -------------------------------------------------------------------------------- /roms/Space_Flight.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x1: up 4 | 0x4: down 5 | 0xf: start game 6 | 0xe: launch 7 | 500 8 | on 9 | -------------------------------------------------------------------------------- /roms/Mastermind_FourRow_Robert_Lindley,_1978.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonmavr/chip-8/HEAD/roms/Mastermind_FourRow_Robert_Lindley,_1978.ch8 -------------------------------------------------------------------------------- /roms/Brick.cfg: -------------------------------------------------------------------------------- 1 | # BRICK: a modified version of BRIX, a CHIP-8 game. 2 | # Original BRIX by Andreas Gustafsson. 3 | 0x4: left 4 | 0x6: right 5 | 350 6 | on 7 | -------------------------------------------------------------------------------- /roms/Filter.cfg: -------------------------------------------------------------------------------- 1 | # Objective: Collect as many rain drops as possible. 2 | # Author: Unknown 3 | # Controls: 4 | 0x4: Left 5 | 0x6: Right 6 | 250 7 | on 8 | -------------------------------------------------------------------------------- /assets/demo_grid.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1122530ebe0c70feb211a222aac0c6c2439029a768287f7e06cb3a71ca9f5775 3 | size 38217768 4 | -------------------------------------------------------------------------------- /assets/demo_grid.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f242433c13378481691ff781cd430ade9340262a67ece5b2a6b485ea8aab9179 3 | size 2695654 4 | -------------------------------------------------------------------------------- /roms/Pong_alt.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x1: Left player up 4 | 0x4: Left player down 5 | 0xc: Right player up 6 | 0xd: Right player down 7 | on 8 | -------------------------------------------------------------------------------- /roms/Pong_2_Pong_hack.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x1: Left player up 4 | 0x4: Left player down 5 | 0xc: Right player up 6 | 0xd: Right player down 7 | on 8 | -------------------------------------------------------------------------------- /roms/Paddles.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x4: left (top player) 4 | 0x6: right (top player) 5 | 0x7: left (bottom player) 6 | 0x9: right (bottom player) 7 | 500 8 | on 9 | -------------------------------------------------------------------------------- /roms/Rush_Hour_alt.cfg: -------------------------------------------------------------------------------- 1 | # Author: Not specified 2 | # Controls: 3 | 0x5: Up 4 | 0x8: Down 5 | 0x7: Left 6 | 0x9: Right 7 | 0xa: OK/hold to slide 8 | 0x1: Options/back 9 | 1000 10 | on 11 | -------------------------------------------------------------------------------- /roms/Breakout.cfg: -------------------------------------------------------------------------------- 1 | # Breakout, by: Carmelo Cortez 2 | # 3 | # The game, Breakout, is a variation of the Wipe-Off game. 4 | # Author: Carmelo Cortez, 1979 5 | # Controls: 6 | 0x4: Left 7 | 0x6: Right 8 | on 9 | -------------------------------------------------------------------------------- /roms/Tic-Tac-Toe.cfg: -------------------------------------------------------------------------------- 1 | # Author: Unknown 2 | # Controls: 3 | 0x1: tile 1 4 | 0x2: tile 2 5 | 0x3: tile 3 6 | 0x4: tile 4 7 | 0x5: tile 5 8 | 0x6: tile 6 9 | 0x7: tile 7 10 | 0x8: tile 8 11 | 0x9: tile 9 12 | 500 13 | on 14 | -------------------------------------------------------------------------------- /demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "chip8.hpp" 4 | 5 | int main(int argc, char** argv) { 6 | auto ch8 = std::make_unique(); 7 | ch8->LoadRom(argv[1]); 8 | ch8->Run(); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /roms/Nim.cfg: -------------------------------------------------------------------------------- 1 | # Nim, by: Carmelo Cortez 2 | # 3 | # The Nim Game is a little less graphic than most games. The player may go first by pressing. "F" key, any other let the computer go first. 4 | # Author: Carmelo Cortez, 1978 5 | # Controls: 6 | 0xf: action 7 | on 8 | -------------------------------------------------------------------------------- /roms/Russian_Roulette.cfg: -------------------------------------------------------------------------------- 1 | # Russian Roulette , by: Carmelo Cortez 2 | # 3 | # This game is called Russian RouLette. Press any key to Spin and pull the Trigger. 4 | # Author: Carmelo Cortez, 1978 5 | # Controls: 6 | 0x0: (or any other key) spin 7 | 1000 8 | on 9 | -------------------------------------------------------------------------------- /roms/Space_Invaders_alt.cfg: -------------------------------------------------------------------------------- 1 | # Space Invaders, by: David Winter 2 | # 3 | # The well known game. Destroy the invaders with your ship. Shoot 4 | # with 5, move with 4 and 6. Press 5 to begin a game. 5 | # 6 | 0x4: Left 7 | 0x5: Shoot 8 | 0x6: Right 9 | 10 | 400 11 | on 12 | -------------------------------------------------------------------------------- /roms/Shooting_Stars.cfg: -------------------------------------------------------------------------------- 1 | # Shooting Stars, by: Philip Baltzer 2 | # 3 | # Objective: Avoid the shooting stars and get to the base as many times as 4 | # possible. 5 | # 6 | # Author: Philip Baltzer, 1978 7 | # Controls: 8 | 0x2: up 9 | 0x4: left 10 | 0x6: right 11 | 0x8: down 12 | 1000 13 | on 14 | -------------------------------------------------------------------------------- /roms/Coin_Flipping.cfg: -------------------------------------------------------------------------------- 1 | # Coin Flipping, by: Carmelo Cortez 2 | # 3 | # The game is a Coin FlLpping program. Flip run up and the 4 | # computer starts to flip a coin, and at the same tine shosing heads 5 | # and tails on the screen, stopping at the value set in VC. 6 | # Author: Carmelo Cortez, 1978 7 | 1500 8 | on 9 | -------------------------------------------------------------------------------- /roms/Guess_alt.cfg: -------------------------------------------------------------------------------- 1 | # Think to a number between 1 and 63. CHIP8 shows you several boards and you have to tell if you see your number in them. Press 5 if so, or another key if not. CHIP8 gives you the number... 2 | 3 | # Author: David Winter 4 | 0x5: See the number you thought 5 | 0x0: Or other keys; don't see it 6 | 1500 7 | on 8 | -------------------------------------------------------------------------------- /roms/Space_Invaders.cfg: -------------------------------------------------------------------------------- 1 | # Space Invaders, by: David Winter 2 | # 3 | # The well known game. Destroy the invaders with your ship. Shoot 4 | # with 5, move with 4 and 6. Press 5 to begin a game. 5 | # 6 | # Author: David Winter 7 | # Controls: 8 | 0x4: Left 9 | 0x5: Shoot/start 10 | 0x6: Right 11 | 12 | 500 13 | on 14 | -------------------------------------------------------------------------------- /roms/Guess.cfg: -------------------------------------------------------------------------------- 1 | # Think to a number between 1 and 63. CHIP8 shows you several boards and you 2 | # have to tell if you see your number in them. Press 5 if so, or another key if 3 | # not. CHIP8 gives you the number... 4 | # Author: David Winter 5 | 0x5: See the number you thought 6 | 0x0: Or other keys; don't see it 7 | 1500 8 | on 9 | -------------------------------------------------------------------------------- /roms/Craps.cfg: -------------------------------------------------------------------------------- 1 | # Craps, by: Camerlo Cortez 2 | # 3 | # To use the Craps program, press any key to roll dice. 4 | # 7 or 11 wins, 12, 2 or 3 loses on first roll. The second roll must match the first to win, but if you roll a seven you lose. This program could be expanded to include on-the-screen scoring of bets. 5 | # Author: Camerlo Cortez, 1978 6 | # Status: Not working 7 | on 8 | -------------------------------------------------------------------------------- /roms/Blitz.cfg: -------------------------------------------------------------------------------- 1 | # Blitz, by: David Winter 2 | # 3 | # This game is a BOMBER clone. You are in a plane, and you must destroy the 4 | # towers of a town. Your plane is flying left to right, and goes down. Use 5 to 5 | # drop a bomb. The game ends when you crash yourself on a tower... 6 | # 7 | # Author: David Winter 8 | # Status: Not working 9 | # Controls: 10 | 0x5: drop bomb 11 | on 12 | -------------------------------------------------------------------------------- /roms/Tank.cfg: -------------------------------------------------------------------------------- 1 | # You are in a tank which has 25 bombs. Your goal is to hit 25 times a mobile 2 | # target. The game ends when all your bombs are shot. If your tank hits the 3 | # target, you lose 5 bombs. Use 2 4 6 and 8 to move. This game uses the original 4 | # CHIP8 keyboard, so directions 2 and 8 are swapped. 5 | 6 | # Author: Unknown 7 | # Controls: 8 | 0x5: shoot 9 | 500 10 | on 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /roms/Breakout_Brix_hack.cfg: -------------------------------------------------------------------------------- 1 | # Breakout (Brix hack), by: David Winter 2 | # 3 | # This game is an "arkanoid" precursor. You have 5 lives, and your goal is the destruction of all the brixs. Use 4 and 6 to move your paddle. The game ends when all the brixs are destroyed. 4 | # 5 | # This game is the same than BRIX, but has graphics looking like the game on the Atari 2600 console. 6 | # 7 | # Author: David Winter, 1997 8 | 0x4: Left 9 | 0x6: Right 10 | on 11 | -------------------------------------------------------------------------------- /include/keypad.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KEYPAD_HPP 2 | #define KEYPAD_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace Keypad { 8 | const std::unordered_map keyboard2keypad = { 9 | {'1', 0x1}, {'2', 0x2}, {'3', 0x3}, {'4', 0xC}, {'q', 0x4}, {'w', 0x5}, 10 | {'e', 0x6}, {'r', 0xD}, {'a', 0x7}, {'s', 0x8}, {'d', 0x9}, {'f', 0xE}, 11 | {'z', 0xA}, {'x', 0x0}, {'c', 0xB}, {'v', 0xF} 12 | }; 13 | }; // namespace Keypad 14 | #endif // KEYPAD_HPP 15 | -------------------------------------------------------------------------------- /roms/Pong.cfg: -------------------------------------------------------------------------------- 1 | # OK. here is PONG version 1.1. The ball is a little faster in this 2 | # version making play a little more realistic. I know PONG 1.0 was 3 | # just posted yesterday, but I think this version is significantly 4 | # better, so here it is. 5 | # 6 | # Use keys 7 and 4 move left player and / and * move right player. 7 | # 8 | # Enjoy!! 9 | # 10 | # Author: Paul Vervalin, 1990 11 | # Controls: 12 | 0x1: Left player up 13 | 0x4: Left player down 14 | 0xc: Right player up 15 | 0xd: Right player down 16 | on 17 | -------------------------------------------------------------------------------- /roms/Hi-Lo.cfg: -------------------------------------------------------------------------------- 1 | # Hi-Lo, by: Jef Winsor 2 | # 3 | # You have 10 chances to guess the value of a random number between 00 and 99 selected by the program. The number at the right of the screen shows the number of the guess you are using. Enter a two digit number and the computer tells you if you are high or low. Press any key to erase this number and then, try again. If you have failed after ten guesses, press any key and the number will be shown. If you are good you will never need more than seven guesses. 4 | # Author: Jef Winsor, 1978 5 | on 6 | -------------------------------------------------------------------------------- /roms/15_Puzzle_alt.cfg: -------------------------------------------------------------------------------- 1 | # Same instructions as in 15Puzzle. 2 | # Author: Unknown 3 | # Controls: 4 | 0x1: shuffle 1st tile 5 | 0x2: shuffle 2nd tile 6 | 0x3: shuffle 3rd tile 7 | 0xd: shuffle 4th tile 8 | 0x4: shuffle 5th tile 9 | 0x5: shuffle 6th tile 10 | 0x6: shuffle 7th tile 11 | 0xd: shuffle 8th tile 12 | 0x7: shuffle 9th tile 13 | 0x8: shuffle 10th tile 14 | 0x9: shuffle 11th tile 15 | 0xe: shuffle 12th tile 16 | 0xa: shuffle 13th tile 17 | 0x0: shuffle 14th tile 18 | 0xb: shuffle 15th tile 19 | 0xf: shuffle 16th tile 20 | 500 21 | on 22 | -------------------------------------------------------------------------------- /roms/Spooky_Spot.cfg: -------------------------------------------------------------------------------- 1 | # Spooky Spot, by: Joseph Weisbecker 2 | # 3 | # Now you can let the computer make your big decisions or predict the future 4 | # just like governmentt or industry leaders do. You will see the words YES and 5 | # NO at the right of the screen. Ask the computer any question that can be 6 | # answered with YES or NO. Press KEY 0 and the spooky spot will show you the 7 | # computer's answer. This program replaces your old fashioned mechanical OUIJA 8 | # board. 9 | # 10 | # Author: Joseph Weisbecker, 1978 11 | 0x0: Show answer 12 | 1000 13 | on 14 | -------------------------------------------------------------------------------- /roms/Submarine.cfg: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Submarine # 3 | # by Carmelo Cortez # 4 | ######################################################################### 5 | # The Sub Game is my favorlte. 6 | # Press "5" key to fire depth charges at the subs below. 7 | # You score 15 points for a small sub and 5 points for the larger. 8 | # You get 25 depth charges to start. 9 | 10 | 0x5: shoot 11 | 500 12 | on 13 | -------------------------------------------------------------------------------- /roms/Space_Intercept.cfg: -------------------------------------------------------------------------------- 1 | # Space Intercept, by Joseph Weisbecker 2 | # 3 | # At startup, Press 1 to select the large UFO which counts 5 points when hit or 4 | # 2 to select the small UFO which counts 15 points when hit. Launch your rocket 5 | # by pressing key 4,5 or 6. You get 15 rockets as shown in the lower right 6 | # corner of the screen. Your score is shown in the lower left corner of the 7 | # screen. 8 | # Author: Joseph Weisbecker, 1978 9 | # Controls: 10 | 0x1: Select large UFO (5 pts) 11 | 0x2: Select small UFO (15 pts) 12 | 0x4: Launch 13 | 0x5: Launch 14 | 0x6: Launch 15 | 1000 16 | on 17 | -------------------------------------------------------------------------------- /roms/Merlin.cfg: -------------------------------------------------------------------------------- 1 | # Merlin, by: David Winter 2 | # 3 | # This is the SIMON game. The goal is to remember in which order the squares are 4 | # lighted. The game begins by lighting 4 random squares, and then asks you to 5 | # light the squares in the correct order. You win a level when you give the 6 | # exact order, and each increasing level shows a additionnal square. The game 7 | # ends when you light an incorrect square. Keys are 4 and 5 for the two upper 8 | # squares, then 1 and 2 for the two other ones. Author: David Winter 9 | # Controls: 10 | 0x4: Toggle upper left 11 | 0x5: Toggle upper right 12 | 0x1: Toggle lower left 13 | 0x2: Toggle lower right 14 | on 15 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | paths-ignore: 7 | - '*README*' 8 | - 'assets/**' 9 | - 'roms/**' 10 | - 'LICENSE*' 11 | - '.git*' 12 | - '.editor*' 13 | pull_request: 14 | branches: [ "master" ] 15 | paths-ignore: 16 | - '*README*' 17 | - 'assets/**' 18 | - 'roms/**' 19 | - 'LICENSE*' 20 | - '.git*' 21 | - '.editor*' 22 | 23 | jobs: 24 | build: 25 | 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | - name: compile and run sanity test(s) 31 | run: make test 32 | -------------------------------------------------------------------------------- /roms/Connect_4.cfg: -------------------------------------------------------------------------------- 1 | # Connect 4, by: David Winter 2 | # 3 | # This game is for two players. The goal is to align 4 coins in the 4 | # game area. Each player's coins are colored. When you drop a coin, 5 | # it is paced on the latest dropped coin in the same column, or at 6 | # the bottom if the column is empty. Once the column is full, you 7 | # cannot place any more coins in it. To select a column, use 4 and 6. 8 | # 9 | # To drop a coin, use 5. There is no winner detection yet. This will 10 | # be soon avalaible (Hey! I don't spend my life on CHIP8 !). 11 | # Author: David Winter 12 | 0x4: Select left column 13 | 0x6: Select right column 14 | 0x5: Drop coin 15 | on 16 | -------------------------------------------------------------------------------- /roms/Kaleidoscope.cfg: -------------------------------------------------------------------------------- 1 | # VIP Kaleidoscope, by: Joseph Weisbecker 2 | # 3 | # Four spots appear in a group at the center of the screen. Press keys 2, 4, 6, 4 | # or 8 to create a pattern. Keep your pattern smaller than 138 key depressions. 5 | # 6 | # Push key 0 to terminate pattern entry. Pushing key 0 causes your pattern to be 7 | # continuously repeated forming a fascinating, changing kaleidoscope display on 8 | # the screen. A "44444442220" key sequence provides a very nice effect 9 | # Experiment to find other nice patterns. 10 | # 11 | # Author: Joseph Weisbecker, 1978 12 | # Controls: 13 | 0x0: terminate pattern 14 | 0x2: create a pattern 15 | 0x4: create a pattern 16 | 0x6: create a pattern 17 | 0x8: create a pattern 18 | 1000 19 | on 20 | -------------------------------------------------------------------------------- /roms/README.md: -------------------------------------------------------------------------------- 1 | ### Roms directory 2 | 3 | Each ROM (.ch8 file) is optionally accompanied by a config file (.cfg). 4 | The config file describes the controls and the CPU speed so they can be 5 | displayed in the UI. It can also include comments, whose lines are preceeded by `#`. 6 | 7 | Here's an example config: 8 | ``` 9 | # Game objective 10 | 0x4: Move left 11 | 0x6: Move right 12 | 0x5: Shoot 13 | # Frequency (optional) 14 | 1000 15 | # Quirks (optional - can be "on" or "off") 16 | on 17 | ``` 18 | 19 | ### Credits 20 | 21 | All roms but `test/test_opcode.ch8` are copied from [dmatlack's repo](https://github.com/dmatlack/chip8/tree/master/roms/games). 22 | 23 | `test_opcode.ch8` is copied from [corax89's repo](https://github.com/corax89/chip8-test-rom/tree/master). 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | TARGET = play 3 | SRC_DIR = src 4 | INC_DIR = include 5 | CFLAGS = -std=c++17 -I$(INC_DIR) -Wall 6 | LDFLAGS = 7 | DEMO_SRC = demo.cpp 8 | TEST_SRC = test/tests.cpp 9 | SRC = $(wildcard $(SRC_DIR)/*.cpp) $(DEMO_SRC) 10 | 11 | # Compare cmd arguments; if `test`, extend it to handle unit tests 12 | ifeq ($(MAKECMDGOALS), test) 13 | CFLAGS += -DRUN_UNIT_TESTS 14 | SRC = $(wildcard $(SRC_DIR)/*.cpp) $(TEST_SRC) 15 | TARGET = test/test 16 | endif 17 | 18 | OBJECTS = $(SRC:%.cpp=%.o) 19 | RM = rm -rf 20 | 21 | all: $(TARGET) 22 | 23 | $(TARGET): $(OBJECTS) 24 | $(CC) $(OBJECTS) -o $(TARGET) $(LDFLAGS) 25 | 26 | %.o: %.cpp 27 | $(CC) $(CFLAGS) -c $< -o $@ 28 | 29 | test: $(TARGET) 30 | ./$(TARGET) 31 | 32 | .PHONY: clean 33 | 34 | clean: 35 | $(RM) $(TARGET) 36 | $(RM) $(SRC_DIR)/*.o 37 | $(RM) demo.o 38 | $(RM) test/*.o 39 | 40 | -------------------------------------------------------------------------------- /include/chip8_tester.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CHIP8_TESTER_HPP 2 | #define CHIP8_TESTER_HPP 3 | 4 | #include "chip8.hpp" 5 | #include 6 | 7 | /** 8 | * This class is responsible for testing the Chip8 class as a whole. The goal 9 | * is to verify Chip8's sanity. It does so by checking the internal state of 10 | * Chip8, e.g. the rendered output (frame buffer), PC, etc. 11 | */ 12 | class Chip8Tester { 13 | public: 14 | template 15 | void AssertFrameBuffer(const Chip8& ch8, const std::array& target) { 16 | for (size_t i = 0; i < target.size(); ++i) { 17 | assert(static_cast(ch8.frame_buffer_[i]) == 18 | static_cast(target[i])); 19 | } 20 | } 21 | 22 | template 23 | void AssertPC(const Chip8& ch8, const T& target) { 24 | assert(static_cast(ch8.PC_) == static_cast(target)); 25 | } 26 | }; 27 | 28 | #endif // CHIP8_TESTER_HPP 29 | -------------------------------------------------------------------------------- /roms/Hidden.cfg: -------------------------------------------------------------------------------- 1 | # ---------------------------------------- 2 | # HIDDEN! 3 | # Copyright (1996) by David WINTER 4 | # ---------------------------------------- 5 | # 6 | # 7 | # HIDDEN is a "memory" game. It is very simple to play. 8 | # 9 | # The rules are as follow: your goal is to find all the identical cards 10 | # in a minimum time. 11 | # 12 | # You are playing in a 4*4 card grid. You can see only two cards at the 13 | # same time. Once this time passed, these two cards will remain shown 14 | # if they are identical, otherwise they will be hidden again. 15 | # 16 | # When the game is finished, two scores are shown: 17 | # SC is your score, corresponding to the number of tries 18 | # HI is the best score (smallest number of tries made to finish) 19 | # 20 | # The keys are: 21 | # 22 | # [2] : More DOWN 23 | # [4] : Move LEFT 24 | # [5] : Show card 25 | # [6] : Move RIGHT 26 | # [8] : Move UP 27 | # 28 | # Enjoy !!! 29 | # Author: David Winter, 1996 30 | on 31 | -------------------------------------------------------------------------------- /roms/Slide.cfg: -------------------------------------------------------------------------------- 1 | # 5. VIP SLIDE 2 | # 3 | # Slide is a two-person game. Each player tries to slide a "puck" over the high-scoring 4 | # "spots" without hitting the back wall. 5 | # 6 | # How To Play VIP Slide 7 | # 8 | # 1. Load the CHIP-8 interpretor at 0000-01FF 9 | # and the game starting at 0200. 10 | # 11 | # 2. Turn RUN switch on. 12 | # 13 | # 3. The puck moves up and down randomly. Press "0" key to stop the puck. The puck 14 | # will move towards the spots after you release the key. The longer you hold the 15 | # key down, the further the puck travels (maximum time approximately 2.5 seconds). 16 | # 17 | # 4. You get 2 points for hitting the first spot, 4 points for either of the next two 18 | # spots and 8 points for either of the last two. The highest score possible is 216 19 | # (two spots can be hit on one slide). 20 | # 21 | # 5. If you hit the back wall, though, you get zero points for that slide, even though 22 | # you've hit a spot. 23 | # 24 | # 6. Each player gets 3 pucks per turn and 6 turns in a game. 25 | # Author: Joyce Weisbecker 26 | # Controls: 27 | 0x0: stop and hold the puck 28 | 1000 29 | on 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022- Leontios Mavropalias 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 | -------------------------------------------------------------------------------- /include/term.h: -------------------------------------------------------------------------------- 1 | #ifndef TERM_H 2 | #define TERM_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | 8 | /* 9 | * Description: 10 | * 11 | * Header to print at specific coordinates on the terminal. Requires a POSIX system. 12 | * Does not work on Windows. 13 | */ 14 | extern "C" { 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #define TPRINT_CLEAR() printf("\033[H\033[J") 22 | #define TPRINT_GOTO_TOPLEFT() printf("\033[0;0H") 23 | #define TPRINT_HIDE_CURSOR() printf("\e[?25l") 24 | #define TPRINT_SHOW_CURSOR() printf("\e[?25h") 25 | #define TPRINT_INIT() do { \ 26 | TPRINT_CLEAR(); \ 27 | TPRINT_HIDE_CURSOR(); \ 28 | TPRINT_GOTO_TOPLEFT(); \ 29 | _TPRINT_GET_SIZE(); \ 30 | } while(0) 31 | 32 | #define TPRINT_PRINT_AT(x, y, c) printf("\033[%d;%dH%c", (y), (x), (c)); 33 | } // extern "C" 34 | #endif // __cplusplus 35 | 36 | #endif // TERM_H 37 | -------------------------------------------------------------------------------- /roms/Sum_Fun.cfg: -------------------------------------------------------------------------------- 1 | # 11. VIP Sum Fun 2 | # 3 | # The object of this game is to add up the three digits 4 | # which appear in the middle of the screen and then hit 5 | # the key representing the total as fast as you can. 6 | # 7 | # How to Play VIP Sum FUn 8 | # 9 | # 1. Load the CHIP-8 interpreter at 0000-01FF and 10 | # the game starting at 0200. 11 | # 12 | # 2. Turn RUN switch on. 13 | # 14 | # 3. You get twenty sets of three digits per game and 15 | # between zero and ten points per set. The faster you 16 | # enter the correct total, the more points you win. 17 | # 18 | # 4. If you wait more that 3 seconds, you get zero points. 19 | # The correct sum is shown above the three digits after 20 | # you enter it, and there is a pause before the next 21 | # set appears. 22 | # 23 | # 5. The score is shown in the upper right-hand corner of 24 | # the screen. The maximum number of points you can score 25 | # is 200. Between 120 and 159 points is above average. 26 | # A score of 160 or higher is outstanding. 27 | # 28 | # Author: Joyce Weisbecker 29 | # Controls: 30 | 0x0: Enter 0 31 | 0x1: Enter 1 32 | 0x2: Enter 2 33 | 0x3: Enter 3 34 | 0x4: Enter 4 35 | 0x5: Enter 5 36 | 0x6: Enter 6 37 | 0x7: Enter 7 38 | 0x8: Enter 8 39 | 0x9: Enter 9 40 | 500 41 | on 42 | -------------------------------------------------------------------------------- /roms/Sequence_Shoot.cfg: -------------------------------------------------------------------------------- 1 | # 12. VIP Sequence Shoot 2 | # 3 | # You score points by having the sharp-shooter hit the targets in the proper sequence. 4 | # 5 | # How to Play VIP Sequence Shoot 6 | # 7 | # 1. Load the CHIP-8 interpreter at 0000-01FF and 8 | # the game starting at 0200. 9 | # 10 | # 2. Turn RUN switch on. 11 | # 12 | # 3. Pressing the Key C causes the little man to shoot the top target, Key D shoots the 13 | # one below it, Key E the next lower and Key F the bottom target. 14 | # 15 | # 4. One of the four targets flashes and the computer waits for you to shoot it. When 16 | # you press teh correct key, you get one point. Then two flash, on after the other. 17 | # Shoot the one that flashed first, then shoot the other. This gives you two more 18 | # points. Three targets (when hit in the same order as they flashed) add three 19 | # points to your score. 20 | # 21 | # 5. Each time you shoot such a series of targets in proper sequence, your score 22 | # increases by the number of targets you hit. An the next series you see is longer 23 | # by one target. The maximum length of sequence is 22 targets, that is a score of 254. 24 | # 25 | # Author: Joyce Weisbecker 26 | 0xc: Shoot 1st target (up) 27 | 0xd: Shoot 2nd target 28 | 0xe: Shoot 3rd target 29 | 0xf: Shoot 4th target (bottom) 30 | 1000 31 | on 32 | -------------------------------------------------------------------------------- /include/cfg_parser.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CFG_PARSER_HPP 2 | #define CFG_PARSER_HPP 3 | 4 | #include "cfg_parser.hpp" 5 | #include "keypad.hpp" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | /** 19 | * Description: 20 | * Parses a .cfg file for Chip8 emulator roms and reads rom's settings (keypad, 21 | * frequency. 22 | */ 23 | class CfgParser { 24 | public: 25 | CfgParser(const std::string& filename); 26 | int frequency() const { return frequency_; } 27 | const std::vector>& key_descrs() const { 28 | return key_descrs_; 29 | } 30 | bool quirks() const { return quirks_; } 31 | 32 | private: 33 | void ParseConfigFile(const std::string& filename); 34 | int frequency_; 35 | const std::unordered_map keyboard2keypad_ = 36 | [input = Keypad::keyboard2keypad]{ 37 | std::unordered_map ret; 38 | for (const auto& pair: input) 39 | ret[pair.second] = pair.first; 40 | return ret; 41 | }(); 42 | std::vector> key_descrs_; 43 | bool quirks_; 44 | bool cfg_file_found_; 45 | }; 46 | 47 | #endif // CFG_PARSER_HPP 48 | -------------------------------------------------------------------------------- /roms/UFO.cfg: -------------------------------------------------------------------------------- 1 | # Here's how to play UFO: 2 | # 3 | # You have a stationary missle launcher at the bottom of the screen. You 4 | # can shoot in three directions; left diagonal, straight up, and right 5 | # diagonal.. using the keys 4, 5, and 6 respectively.. You try to hit 6 | # one of two objects flying by.. at apparently varying speeds.. Your 7 | # score is displayed on your left, the number of missles you have left 8 | # is displayed on your right. (You get 15).. 9 | # 10 | # This game ("UFO") is not new. I have a copy of it from 1977 (!). It 11 | # was one of the original CHIP-8 games on the audio cassette that was 12 | # included when I bought my first computer, the Finnish-made Telmac 13 | # 1800. 14 | # 15 | # It was also the first real program to run under CHIP-48 (it was used 16 | # as a test case during the development of the CHIP-48 interpreter). The 17 | # reason I have not posted it to the net myself is that I have no idea 18 | # about its copyright status. I don't even know where it originated 19 | # (RCA, perhaps?). 20 | # 21 | # The cassette that was bundled with the Telmac 1800 contains more than 22 | # a dozen CHIP-8 programs. If someone could convince me that these 23 | # programs are indeed freely redistributable, the other programs could 24 | # also be posted. Otherwise, perhaps this one shouldn't have been. 25 | # Author: Lutz V, 1992 26 | # Controls: 27 | 0x4: Shoot top left 28 | 0x5: Shoot straight 29 | 0x6: Shoot top right 30 | on 31 | -------------------------------------------------------------------------------- /roms/Rush_Hour.cfg: -------------------------------------------------------------------------------- 1 | # Rush Hour (v1.1) for CHIP-8 by hap 08-02-08, http://hap.samor.nl/ 2 | # Originally released on 17-12-06. Version 1.1 improves a few things. 3 | # Based on a boardgame by Nobuyuki Yoshigahara "Nob" and ThinkFun, 4 | # http://www.thinkfun.com/ 5 | # 6 | # This game contains 170 puzzles. Most were taken from the original cardsets, 7 | # some were made by me, and some were generated with Michel's PyTraffic, 8 | # http://alpha.uhasselt.be/Research/Algebra/Members/pytraffic . Refer to 9 | # the source code for detailed information on this. 10 | # 11 | # HOW TO PLAY 12 | # =========== 13 | # 14 | # The goal of the game is to slide the arrow block(s) out of the 6*6 grid. 15 | # Completing 10 boards will be awarded with the inclusion of the next boardset(s) 16 | # as seen from the connections on the boardset select screen, a password will be 17 | # given so you don't have to complete the game in a single session. 18 | # 19 | # HEX key PC key* Use 20 | # --------------------------- 21 | # 5 W up 22 | # 8 S down 23 | # 7 A left 24 | # 9 D right 25 | # A Z ok/hold to slide 26 | # 1 1 option(in-game)/back 27 | # 28 | # * = for interpreters that have the 4*4 keypad at 1234/QWER/ASDF/ZXCV. 29 | # Author: Hap, 2006 30 | # Controls: 31 | 0x5: Up 32 | 0x8: Down 33 | 0x7: Left 34 | 0x9: Right 35 | 0xa: OK/hold to slide 36 | 0x1: Options/back 37 | 1000 38 | on 39 | -------------------------------------------------------------------------------- /roms/Animal_Race.cfg: -------------------------------------------------------------------------------- 1 | # 9. VIP Animal Race 2 | # 3 | # Animal Race is a fun game for one person, with an element of luck - sure to 4 | # put a smile on your face. Five different animals race against one another 5 | # and you have the chance to test your expertise at picking the winner. 6 | # 7 | # 8 | # How To Play Animal Race 9 | # 10 | # 1. Load the CHIP-8 interpretor at 0000-01FF 11 | # and the game starting at 0200. 12 | # 13 | # 2. Turn RUN switch on. 14 | # 15 | # 3. Wait until the animals are lined up and read to start, then select an animal 16 | # by pressing a letter A through E. A mark will appear to the left of the 17 | # animal to remind you of the one you have selected. 18 | # 19 | # 4. Decide how much you want to bet (up to a limit of $9), then press that key. 20 | # 21 | # 5. After the race is over, press zero (or any key) to start the next race. 22 | # Your winnings or losses will be computed and the new total displayed at 23 | # the begining of the next race. 24 | # 25 | # Hints for expert players 26 | # All animals move at approximately the same speed, but they start from different 27 | # positions. The odds for each animal are related to the starting position but 28 | # include a random element. Some races favor the player and you should bet up to 29 | # the limit on these. Some races are unfavorable and you should bet carefully 30 | # on these. 31 | # 32 | # You can win the game by accumulating $256 or more. 33 | # Author: Brian Astle 34 | # Controls: 35 | 0xa: Select girafe (A) 36 | 0xb: Select elephant (B) 37 | 0xc: Select T-Rex (C) 38 | 0xd: Select camel (D) 39 | 0xe: Select lama (E) 40 | 0x1: Bet $1 41 | 0x2: Bet $2 42 | 0x3: Bet $3 43 | 0x4: Bet $4 44 | 0x5: Bet $5 45 | 0x6: Bet $6 46 | 0x7: Bet $7 47 | 0x6: Bet $8 48 | 0x9: Bet $9 49 | 1500 50 | on 51 | -------------------------------------------------------------------------------- /roms/Tetris.cfg: -------------------------------------------------------------------------------- 1 | #################################################################### 2 | # TETRIS # 3 | # by Fran Dachille # 4 | #################################################################### 5 | 6 | # This is my first release of the famous Tetris game on the HP48S. I was 7 | # inspired by the lack enjoyable games for our favorite handheld. [Not since the 8 | # Goodies Disks have been available! -jkh-] This game, though it lacks some of 9 | # the whistles and bangs of fancy versions, performs on par with quality arcade 10 | # versions (nota bene -> SPEED). At my college, every person who picks up my 11 | # calculator is immediately hooked for hours. 12 | 13 | # This version is written for the CHIP48 game interpreter (c) 14 | # copyright 1990 Andreas Gustafsson. 15 | 16 | # After every 5 lines, the speed increases slightly and peaks at 45 lines. 17 | 18 | # There is room for improvement in this version. Notably, background 19 | # pictures, a pause key (for now, hold ON), two rotate keys, various 20 | # starting skill levels, a B version which starts with randomn blocks, 21 | # finishing graphics, and high scores, just to name a few. 22 | 23 | # In order for improvements, I need to know if there is reasonable 24 | # demand. If this game is worth playing for hours upon hours, please let 25 | # me know. If you wish to support the improvements, want future versions, 26 | # and want to see other games ported to the HP48S, send $5.00 to: 27 | 28 | # FRAN DACHILLE 29 | # WEBB INSTITUTE 30 | # GLEN COVE, NY 11542 31 | # 32 | # Controls: 33 | 0x4: rotate left 34 | 0x5: move left 35 | 0x6: move right 36 | 0x7: drop 37 | 2000 38 | 39 | on 40 | -------------------------------------------------------------------------------- /test/tests.cpp: -------------------------------------------------------------------------------- 1 | #include "chip8_tester.hpp" 2 | #include "chip8.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | constexpr static auto ibm_logo = []() { 11 | return std::array { 12 | []() constexpr{ 13 | std::array ret {}; 14 | auto mp = std::make_pair; 15 | std::array, 38> ones = { 16 | mp(524, 531), mp(533, 541), mp(545, 549), mp(559, 563), mp(652, 659), 17 | mp(661, 671), mp(673, 678), mp(686, 691), mp(782, 785), mp(791, 793), 18 | mp(797, 799), mp(803, 807), mp(813, 817), mp(910, 913), mp(919, 925), 19 | mp(931, 937), mp(939, 945), mp(1038, 1041), mp(1047, 1053), mp(1059, 1061), 20 | mp(1063, 1069), mp(1071, 1073), mp(1166, 1169), mp(1175, 1177), 21 | mp(1181, 1183), mp(1187, 1189), mp(1192, 1196), mp(1199, 1201), 22 | mp(1292, 1299), mp(1301, 1311), mp(1313, 1317), mp(1321, 1323), 23 | mp(1327, 1331), mp(1420, 1427), mp(1429, 1437), mp(1441, 1445), 24 | mp(1450, 1450), mp(1455, 1459) 25 | }; 26 | for (const auto& o: ones) { 27 | for (auto oo = o.first; oo <= o.second; ++oo) 28 | ret[oo] = 1; 29 | } 30 | return ret; 31 | }() 32 | }; 33 | }(); 34 | 35 | 36 | int main(int argc, char** argv) { 37 | auto ch8 = std::make_unique(); 38 | auto tester = std::make_unique(); 39 | ch8->LoadRom("./roms/test/Ibm_logo.ch8"); 40 | ch8->Run(1000); 41 | tester->AssertFrameBuffer(*ch8, ibm_logo); 42 | std::cout << "\033[2J\033[H"; 43 | std::cout << std::string(80, '=') + "\n" + "All tests passed!\n" + std::string(80, '=') << std::endl;; 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /roms/Mastermind_FourRow_Robert_Lindley,_1978.cfg: -------------------------------------------------------------------------------- 1 | # MASTERMIND by Robert Lindley 2 | # 3 | # I have progranmed two versLons of the game Mastermind. This game is 4 | # distributed by Invicta Plastics, Suite 940, 200 - 5th Ave., New York, NY 5 | # 10010, and is available most pLaces where toys and games are sold. For 6 | # complete details of the game, please refer to their instructions. The 7 | # information given here refers to this particular VIP iurplementation. The two 8 | # versions are essentially the same except that the first has a four number code 9 | # and the second has a five nr:mber hidden code selected at random.. In the 10 | # four number version, the digits one through six are used and in the five 11 | # number version the diglts zero through seven are used. While the game is 12 | # running, the other hex keys, except key F, have no effect. Key F is used when 13 | # you change your mind and want to change your input. This key erases the 14 | # current partial entry. The game starts by displaying a series of dashes 15 | # arranged in rows. As the game progresses, the player attempts to deduce the 16 | # hidden code by replacing the dashes with digits entered via the hex keyboard. 17 | # Each tlme a hex key is used, the selected digit replaces a dash in one 18 | # vertical column. This vertical column is one guess of the ten allowed to 19 | # deduce the hidden number. When the bottom dash in any column is replaced by a 20 | # digit, that try is imidiately scored. This score appears below the current 21 | # column. If any digit in the column exactly matches the hidden number digit in 22 | # the same row, a broken bar will appear. then four or five (one for each row in 23 | # the game) appear, the hidden number has been deduced and it will be revealed 24 | # at the right end of the screen. If any digit in the column matches a hidden 25 | # digit, but in an incorrect erorr, a white bar will appear. Note that the 26 | # scoring is across all rows. For example, if there are two fives in a column 27 | # and one of then is in the correct row and there is only one five in the hidden 28 | # numbers, one broken bar will appear. When all allowed ten tries have been 29 | # used, the hidden number will be revealed. 30 | # Controls: 31 | 0xf: change your mind and input 32 | on 33 | -------------------------------------------------------------------------------- /roms/Worm_V4.cfg: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # ///////////////// 3 | # ////////////////// 4 | # //// //// 5 | # //// /////////// 6 | # //// /////////// 7 | # // //// 8 | # // /////////// 9 | # // ////////// 10 | # 11 | # www.revival-studios.com 12 | # ----------------------------------------------------------------------------- 13 | # Title : SuperWorm V4 14 | # Author : RB (Original game) 15 | # Updates and fixes by: Martijn Wenting / Revival Studios 16 | # Genre : Game 17 | # System : Chip-8 / SuperChip8 18 | # Date : 10/08/2007 19 | # Product ID : RS-C8001 20 | # ----------------------------------------------------------------------------- 21 | # 22 | # All the contents of this package are (c)Copyright 2007 Revival Studios. 23 | # Original game: SuperWorm is (c)Copyright 1992 RB 24 | # 25 | # The contents of the package may only be spread in its original form, and may not be 26 | # published or distributed otherwise without the written permission of the authors. 27 | # 28 | # Description: 29 | # ------------ 30 | # SuperWorm V4 is an update of the SuperChip8 game: Worm3 by RB. 31 | # The original game was only for SuperChip, so i've created a Chip-8 port. 32 | # It also includes several speed fixes and a new control system. 33 | # 34 | # Running the game: 35 | # ----------------- 36 | # Use the Megachip emulator or any other Chip-8/SuperChip compatible emulator to run the game. 37 | # 38 | # Credits: 39 | # -------- 40 | # Chip-8 version, Updates and fixes by: Martijn Wenting 41 | # Original game by: RB 42 | # 43 | # Distribution: 44 | # ------------- 45 | # This package can be freely distributed in its original form. 46 | # If you would like to include this game in your rom package, please let me know. 47 | # 48 | # Watch out for more releases soon! 49 | # 50 | # 51 | # Martijn Wenting / Revival Studios 52 | # 53 | # Author: RB-Revival Studios, 2007 54 | # 55 | # Controls: 56 | # --------- 57 | 0x2: Up 58 | 0x6: Right 59 | 0x8: Down 60 | 0x4: Left 61 | 350 62 | on 63 | -------------------------------------------------------------------------------- /roms/Astro_Dodge.cfg: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # ///////////////// 3 | # ////////////////// 4 | # //// //// 5 | # //// /////////// 6 | # //// /////////// 7 | # //// //// 8 | # //// /////////// 9 | # //// ////////// 10 | # 11 | # www.revival-studios.com 12 | # ----------------------------------------------------------------------------- 13 | # Title : Astro Dodge 14 | # Author : Martijn Wenting / Revival Studios 15 | # Genre : Game 16 | # System : Chip8 / SuperChip8 17 | # Date : 18/12/2008 18 | # Product ID : RS-C8003 19 | # ----------------------------------------------------------------------------- 20 | # 21 | # All the contents of this package are (c)Copyright 2008 Revival Studios. 22 | # 23 | # The contents of the package may only be spread in its original form, and may not be 24 | # published or distributed otherwise without the written permission of the authors. 25 | # 26 | # Description: 27 | # ------------ 28 | # Astro Dodge is an arcade game for the Chip8 and SuperChip8 systems. 29 | # Both versions of the game are included in this package. 30 | # 31 | # Your goal is to make your way through the asteroids field and dodge the asteroids, scoring points for each asteroid you are able to dodge. 32 | # Button 2,4,6,8 will move your ship, button 5 will start the game. 33 | # 34 | # Running the game(s): 35 | # -------------------- 36 | # The CHIP8 version of the game has been programmed to be compatible with original hardware like the Cosmac VIP and Telmac 1800. 37 | # Use the Megachip emulator or any other Chip8/SuperChip compatible emulator to run the game(s). 38 | # 39 | # Credits: 40 | # -------- 41 | # Programming and Graphics by: Martijn Wenting 42 | # 43 | # Distribution: 44 | # ------------- 45 | # This package can be freely distributed in its original form. 46 | # If you would like to include this game in your rom package, please let me know. 47 | # 48 | # Watch out for more releases soon! 49 | # 50 | # 51 | # Martijn Wenting / Revival Studios 52 | # 53 | # Author: Revival Studios, 2008 54 | # Controls: 55 | 0x5: Start 56 | 0x2: Up 57 | 0x4: Left 58 | 0x6: Right 59 | 0x8: Down 60 | 1500 61 | on 62 | -------------------------------------------------------------------------------- /roms/15_Puzzle.cfg: -------------------------------------------------------------------------------- 1 | # Same as PUZZLE2. 2 | # 3 | # The puzzle consists of 15 tiles, numbered 1 through F (15), and one empty 4 | # space. The tiles are shuffled randomly on a 4x4 grid, and the player’s task is 5 | # to rearrange them into the correct order. To complete the puzzle, the empty 6 | # tile should be in the bottom, right corner, i.e. the winning configuaration is: 7 | # 8 | # 1 2 3 4 9 | # 5 6 7 8 10 | # 9 A B C 11 | # D E F _ 12 | # 13 | ### Instructions: 14 | # 1. Wait for randomization. It might take 10-30 sec or so. 15 | # Pressing a couple of keys helps. 16 | # 2. Shuffle the blank tile with a filled tile. Shuffling works like in any 17 | # typical 15puzzle game. See here: https://personal.math.ubc.ca/~cass/courses/m308-02b/projects/grant/fifteen.html 18 | # 19 | # The only difference between this 15puzzle and the mainstream one is that the 20 | # player can shuffle more than one tile at a time. 21 | # 22 | # 1 6 3 D 1 up 1 6 3 D 2 left 1 6 3 D 23 | # B 2 7 8 ------> B 2 7 8 ------> B 2 7 8 24 | # 9 F 5 C 9 F 5 _ 9 _ F 5 25 | # 4 E A _ 4 E A C 4 E A C 26 | # 27 | ### Controls 28 | # To control the tiles, imagine that your keyboard (default chip8 keyboard, i.e. 29 | # 1 to 4, q to r, a to f, z to v -- otherwise you'll need to adjust it) is 30 | # overlaid on the game's tiles. Pressing a key on your keyboard will make the 31 | # blank tile shift to the coordinates of the key. 32 | # Example (these are some dumb moves for demonstration only): 33 | # 34 | # Real keyboard: | Tiles: 35 | # 1 2 3 4 | 1 3 7 0 x 1 3 7 0 2 1 _ 7 0 36 | # q w e R | A D 9 8 ----> A D 9 8 ----> A 3 9 8 37 | # a s d f | F 3 4 6 F 5 4 6 F D 4 6 38 | # z x c v | C B _ E C _ B E C 5 B E 39 | # 40 | # The game does not care if you win, so if you will you'll have to restart it! 41 | # 42 | # Explanation: Roger Ivie and Leontios Mavropalias 43 | # Author: Roger Ivie 44 | # Controls: 45 | 46 | 0x1: shuffle 1st tile 47 | 0x2: shuffle 2nd tile 48 | 0x3: shuffle 3rd tile 49 | 0xd: shuffle 4th tile 50 | 0x4: shuffle 5th tile 51 | 0x5: shuffle 6th tile 52 | 0x6: shuffle 7th tile 53 | 0xd: shuffle 8th tile 54 | 0x7: shuffle 9th tile 55 | 0x8: shuffle 10th tile 56 | 0x9: shuffle 11th tile 57 | 0xe: shuffle 12th tile 58 | 0xa: shuffle 13th tile 59 | 0x0: shuffle 14th tile 60 | 0xb: shuffle 15th tile 61 | 0xf: shuffle 16th tile 62 | 63 | 500 64 | on 65 | -------------------------------------------------------------------------------- /roms/Bowling.cfg: -------------------------------------------------------------------------------- 1 | # 7. VIP Bowling 2 | # 3 | # Bowling is a great game for recreation and competion requiring skill and a little 4 | # bit of luck. This program simulates bowling closely with regular scoring and the 5 | # option of using three different spins on the ball. 6 | # 7 | # How to play VIP Bowling 8 | # 9 | # 1. Load the CHIP-8 interpretor at 0000-01FF 10 | # and the game starting at 0200. 11 | # 12 | # 2. Turn RUN switch on. 13 | # 14 | # 3. Up to 6 persons can compete. Make the choice by pressing Key 1,2,3,4,5 or 6. 15 | # The players will be referred to as A,B,C,D,E and F. 16 | # 17 | # 4. It is possible to play up to 10 frames in one game. Make your choice of the 18 | # number of frames by pressing Key 1,2,3,4,5,6,7,8,9 or 0 (for 10 frames). 19 | # 20 | # 5. It is possible to choose 3 ball speeds (Key 1,2 or 3). The speed option only 21 | # affects the ball after it is released. 22 | # 23 | # 6. At the start of a player's turn, the video will display whose turn it is and 24 | # whether he is working on a spare, strike, 2 strikes or nothing. 25 | # Release the ball by pressing on of the following keys: Key 1,2,3,5,7,8 or 9. 26 | # -Key 5 will cause a straight ball. 27 | # -Key 1,2 and 3 will cause the ball to spin up. 28 | # -Key 1 before the first pin 29 | # -Key 2 after the first pin 30 | # -Key 3 after the second pin 31 | # -Key 7,8 and 9 will cause the ball to spin up. 32 | # -Key 7 before the first pin 33 | # -Key 8 after the first pin 34 | # -Key 9 after the second pin 35 | # 36 | # 7. After the player's turn ends, the video will display whose turn it was, the 37 | # frame, and the score. The next players follows the instructions at 6 above. 38 | # 39 | # 8. After all players have had their turn in a frame, the video will display the 40 | # total of each player and whether he is working on a spare, 1 strike, 2 strikes 41 | # or nothing. It will display the frame number and the total number of frames 42 | # to go (unless the last frame was played). 43 | # After pressing Any key, player A can start in the next frame. Continue with 44 | # instruction 6 above. 45 | # 46 | # 9. If all frames in the game have been finished, press any key again. The player's 47 | # who are still working on a spare will recieve 1 more ball, and those working 48 | # on 1 or 2 strikes will receive 2 balls. 49 | # 50 | # 10. After all players have finished the game, the final score will be displayed. 51 | # 52 | # 53 | # Author: Gooitzen van der Wal 54 | on 55 | -------------------------------------------------------------------------------- /roms/Most_Dangerous_Game.cfg: -------------------------------------------------------------------------------- 1 | # 10. VIP Most Dangerous Game 2 | # 3 | # VIP Most Dangerous Game pits a hunter against a hunted in a maze. The hunter must 4 | # shoot the hunted before either time runs out or the hunted escapes the maze. 5 | # However, neither the hunted nor the hunter can see a wall in the maze until he runs 6 | # into it. There is always at least one path through the maze. 7 | # 8 | # How To Play VIP Most Dangerous Game 9 | # 10 | # 1. Load the CHIP-8 interpretor at 0000-01FF 11 | # and the game starting at 0200. 12 | # 13 | # 2. Turn RUN switch on. 14 | # 15 | # 3. Hunted's turn: 16 | # When the arrow appears in the lower left, it is the hunted's turn. Keys 2-4-6-8 17 | # control direction (up-left-right-down, respectively). The hunted continues to 18 | # move until a) he makes 5 moves, b) he hits a wall or c) key 0 is pressed. 19 | # 20 | # 4. Hunter's turn 21 | # When the arrow appears in the lower right, it is the hunter's turn. The hunter's 22 | # turn consists of two modes, the move and the shoot. Keys 2-4-6-8 and 0 control both 23 | # modes. Like the hunted, the hunter continues to move until a) he makes 3 moves, 24 | # b) he hits a wall or c) key 0 is pressed. If he hits the wall, case b), he cannot 25 | # fire a bullet. The hunter may fire one of his bullets using the direction keys 26 | # 2-4-6-8. The bullet will travel four squares in the corresponding direction, unless 27 | # a wall is encountered. If the hunter prefers not to use a bullet, he must press key 0. 28 | # 29 | # 5. Repeat steps 3 and 4 until: 30 | # a) The number of turns (upper left on the display) expire. The hunted wins. 31 | # b) The hunter lands on or shoots the hunted. The hunter wins. 32 | # c) The hunted lands on hunter, thereby giving up. The hunter wins. 33 | # d) The hunted reaches the lower right corner of the maze with at least 1 move 34 | # remaining in his turn. The hunted wins, by escaping the maze. 35 | # 36 | # 6. Variations of VIP Most Dangerous Game may be played by changing certain bytes in 37 | # the program. 38 | # 39 | # Location Default Meaning 40 | # 0215 0F Number of turns in the game. 41 | # 0217 06 Number of bullets hunter has at start. 42 | # 0219 03 Number of moves per turn for the hunter. 43 | # 0221 01 Number of squares per move (hunted and hunter). 44 | # 022B 05 Number of moves per turn for the hunted. 45 | # 024B 01 If zero, walls are invisible. 46 | # 0355 04 Number of squares a bullet will go. 47 | # 04E3 70 Probability (out of 100Hex) of a wall appearing. 48 | # 04EB FE If FF, a wall may appear at previously tested boundaries. 49 | # Author: Peter Maruhnic 50 | on 51 | -------------------------------------------------------------------------------- /roms/Reversi.cfg: -------------------------------------------------------------------------------- 1 | # 6. VIP REVERSI 2 | # Reversi is a game over 100 years old, which has become popular recently under the 3 | # name Othello. The game is played on a 8x8 square, using two kinds of markers. 4 | # In VIP Reversi one player has the open markers and the other player the solid 5 | # markers. The score for either player at any time is the total number of his markers 6 | # on the square. 7 | # 8 | # How to play VIP Reversi 9 | # 10 | # 1. Load the CHIP-8 interpretor at 0000-01FF 11 | # and the game starting at 0200. 12 | # 13 | # 2. Turn RUN switch on. 14 | # 15 | # 3. The 8x8 square will be formed and the starting marker configuration shown, having 16 | # two of each kind of marker in the center four locations of the square. The starting 17 | # score for each player is shown as 02 above a line of his markers. 18 | # 19 | # 4. Your VIP indicates the player's turn by blinking that player's score and also blinking 20 | # a cursor dot in the 8x8 square. A player moves the blinking cursor dot in the 8x8 21 | # square by pressing the direction keys 1-4 and 6-9 as shown. (VIPG1-6.JPG) 22 | # 23 | # 5. When the cursor-dot is properly located, the player presses key 5 to place his marker 24 | # on the square. You will find that your VIP will not allow you to make a non-valid 25 | # play. The only placements allowed are onces for which at least one of the other 26 | # player's markers is surrounded between an existing marker of your own and your new 27 | # marker being placed. All these markers must be in consecutive positions on the 28 | # square and can be in any horizontal, vertical or diagonal direction. Once a new 29 | # marker has been placed, your VIP will change all such surrounded markers of the 30 | # other player to your kind and change the scores. 31 | # 32 | # 6. Sometimes it may not be possible for a player to make a valid move. If this happens 33 | # he must forfeit his move by pressing the "F" key! The game ends when neither player 34 | # can play or when the 8x8 square is completely filled with markers. The goal is to 35 | # end the game with the highest score. Do not be discouraged if during the game you 36 | # seem to be losing because this is a game with dramatic reversals! Develop a winning 37 | # strategem and become a champion!! 38 | # 39 | # Gameplay 40 | # 41 | # The following video explains the rules: 42 | # 43 | # https://www.youtube.com/watch?v=QioKbksiJnA 44 | # 45 | # Author: Philip Baltzer 46 | # Controls: 47 | 0x5: place marker to finish a move 48 | 0xF: forfeit (only when no move) 49 | 0x2: up (P1/P2) 50 | 0x4: left (P1/P2) 51 | 0x6: right (P1/P2) 52 | 0x8: down (P1/P2) 53 | 1000 54 | on 55 | -------------------------------------------------------------------------------- /src/cfg_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "cfg_parser.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* 8 | * Description: 9 | * Parses a .cfg file for Chip8 emulator roms and fetches rom's settings (keypad, 10 | * frequency. 11 | */ 12 | 13 | static bool IsWhitespaceOnly(const std::string& str) { 14 | return std::all_of(str.begin(), str.end(), [](unsigned char ch) { 15 | return std::isspace(ch); 16 | }); 17 | } 18 | 19 | static void Trim(std::string &key) { 20 | // Remove spaces from the beginning and from the end 21 | key.erase(key.begin(), std::find_if_not(key.begin(), key.end(), ::isspace)); 22 | key.erase(std::find_if_not(key.rbegin(), key.rend(), ::isspace).base(), key.end()); 23 | } 24 | 25 | 26 | CfgParser::CfgParser(const std::string& filename) : 27 | frequency_(350), 28 | quirks_(true), 29 | cfg_file_found_(true) { 30 | ParseConfigFile(filename); 31 | } 32 | 33 | void CfgParser::ParseConfigFile(const std::string& filename) { 34 | std::ifstream file(filename); 35 | if (!file.is_open()) { 36 | cfg_file_found_ = false; 37 | return; 38 | } 39 | 40 | std::string line; 41 | while (std::getline(file, line)) { 42 | if (line.empty() || line[0] == '#' || IsWhitespaceOnly(line)) 43 | continue; 44 | // Trim whitespace from the beginning of the line 45 | line.erase(line.begin(), std::find_if(line.begin(), line.end(), 46 | [](unsigned char ch) { return !std::isspace(ch); })); 47 | 48 | std::istringstream iss(line); 49 | std::string key; 50 | std::string value; 51 | std::unordered_map keymap; 52 | if (std::getline(iss, key, ':') && std::getline(iss, value)) { 53 | Trim(key); 54 | Trim(value); 55 | uint8_t hex_key = static_cast(std::stoi(key.substr(2), nullptr, 16)); 56 | key_descrs_.push_back(std::make_pair(std::string(keyboard2keypad_.at(hex_key)), value)); 57 | } else { 58 | std::string line_lower = line; 59 | std::transform(line_lower.begin(), line_lower.end(), line_lower.begin(), 60 | [](char c){ return std::tolower(c); }); 61 | bool contains_quirks = line_lower == "on" || line_lower == "off"; 62 | bool contains_freq = !line_lower.empty() && 63 | std::all_of(line_lower.begin(), line_lower.end(), ::isdigit); 64 | quirks_ = (contains_quirks && line_lower == "on") ? true : 65 | (contains_quirks && line_lower == "off") ? false : quirks_; 66 | if (contains_freq) 67 | frequency_ = std::stoi(line); 68 | } 69 | } 70 | file.close(); 71 | } 72 | -------------------------------------------------------------------------------- /roms/Deflection.cfg: -------------------------------------------------------------------------------- 1 | # 8. VIP DEFLECTION 2 | # 3 | # In the VIP Deflection game you position mirrors anywhere on the display screen. 4 | # The object of the game is to deflect a ball of the mirrors a maximum number of 5 | # times before hitting the target. 6 | # 7 | # The number of deflections times the target number gives you deflection points. 8 | # These are added to your previous point total. If you fail to hit the target you 9 | # get no points. The winner of the game is the player who accumulates 257 or 10 | # more deflected points. 11 | # 12 | # How to Play VIP Deflection 13 | # 14 | # 1. Load the CHIP-8 interpretor at 0000-01FF 15 | # and the game starting at 0200. 16 | # 17 | # 2. Turn RUN switch on. 18 | # 19 | # 3. After the scores appear a letter, the target with a number and a ball. The 20 | # letter signifies the player who will program the mirrors. This sequence will 21 | # be repeated until there is a winner. 22 | # 23 | # 4. To program the mirrors, select the proper mirror type by using Table 1 (VIP1-8.jpg) 24 | # as a guide. Key 1 will place a horizontal mirror on the board. Key 2 selects a 25 | # vertical mirror, Key 3 a slant-left mirror, Key 4 a slant-right mirror. 26 | # 27 | # 5. After a mirror has been selected, you may position the mirror by using keys 1 28 | # through 4 and 6 through 9 (see Table 2 for movement of a mirror). Once a 29 | # mirror has been selected and positioned, it may be fixed into position by pressing 30 | # Key 5. 31 | # 32 | # Table 2 - Mirror Positioning and Ball Direction 33 | # Key Selection | Positioning and Direction 34 | # ------------------+--------------------------- 35 | # 1 | up and to the left 36 | # 2 | up 37 | # 3 | up and to the right 38 | # 4 | left 39 | # 5 | right 40 | # 7 | down and to the left 41 | # 8 | down 42 | # 9 | down and to the right 43 | # 44 | # 6. You may position up to 10 mirrors on the game board. After you have the maximum 45 | # amount on the board you must press Key 0 to progress to the fire mode. If you 46 | # wish to progress to the fire mode with less than 10 mirrors programmed, you may 47 | # do so by pressing Key 0. You fire the ball in the direction you want by using 48 | # Keys 1 through 4 and Keys 6 through 9 (see Table 2). 49 | # 50 | # 7. After the ball has reached the target or leaves the game board, the player's 51 | # score is computed and displayed and a new ball and target appear. 52 | # 53 | # 54 | # 55 | # Author: John Fort 56 | # 57 | # Controls: 58 | 0x1: up and to the left 59 | 0x2: up 60 | 0x3: up and to the right 61 | 0x4: left 62 | 0x5: right 63 | 0x7: down and to the left 64 | 0x8: down 65 | 0x9: down and to the right 66 | on 67 | -------------------------------------------------------------------------------- /roms/Syzygy.cfg: -------------------------------------------------------------------------------- 1 | # SYZYGY v0.1 2 | # 3 | # One of the first games I remember playing on a computer was 4 | # called "syzygy" on a now ancient TRS-80 Model 1. It has since 5 | # appeared on other computers under various names. Why it was 6 | # called syzygy, I have no idea (consult Websters). However, since 7 | # the HP48SX has approximately the same memory, graphics and cpu 8 | # power as my TRS-80 did (something like 16kB, 128x64, and a 1.2Mhz Z80), 9 | # I thought it would be amusing to play it again. Now, approximately 10 | # to my recollection, and with many apologies to the original author, 11 | # here is a CHIP48 version of SYZYGY. Enough drivel. 12 | # 13 | # The object of the game is to seek out "targets". You do this with 14 | # your syzygy. Initially small, the syzygy will grow by some amount 15 | # each time a target is hit. Eventually, your syzygy will 16 | # be so long as to make tougher and tougher to get any points (and easier 17 | # and easier to get killed). Confused? Just try it. 18 | # 19 | # Anyways, the syzygy is not allowed to run into anything except targets. 20 | # It cannot run into the screen border (if present), or itself (this 21 | # includes backing into itself). Fast and immediate death will result. 22 | # Don't worry if you die quickly a few times. The keys take a few 23 | # minutes to get used to. 24 | # 25 | # To start: + start/restart game with border 26 | # - start/restart borderless game 27 | # 28 | # To play: 9 up 29 | # 6 down 30 | # 1 left 31 | # 2 right 32 | # 33 | # [Hint for frustrated beginners: hold left hand on 1 & 2, and right hand on 34 | # 6 & 9 keys. Alternate between left hand, right hand, left hand... -jkh-] 35 | # 36 | # 37 | # Fine print (borrowed from Andreas Gustafsson, author of CHIP-48): 38 | # 39 | # SYZYGY is (c) copyright 1990 by Roy Trevino (RTT) 40 | # 41 | # Noncommercial distribution allowed, provided that this 42 | # copyright message is preserved, and any modified versions 43 | # are clearly marked as such. 44 | # 45 | # SYZYGY, via CHIP-48, makes use of undocumented low-level features 46 | # of the HP48SX calculator, and may or may not cause loss of data, 47 | # excessive battery drainage, and/or damage to the calcultor 48 | # hardware. The Author takes no responsibility whatsoever for 49 | # any damage caused by the use of this program. 50 | # 51 | # THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 52 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 53 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 54 | # PURPOSE. 55 | # 56 | # Roy 57 | # 58 | # -------------------------------------------------------------- 59 | # Roy Trevino Intel Corp. 60 | # E-mail: rtrevino@sedona.intel.com Tel: (602) 554 2816 61 | # UUCP: decwrl!apple!oliveb!orc!inews!rtrevino@sedona.intel.com 62 | # Author: Roy Trevino, 1990 63 | # Controls: 64 | 0x9: up 65 | 0x6: down 66 | 0x1: left 67 | 0x2: down 68 | 250 69 | on 70 | -------------------------------------------------------------------------------- /roms/Biorhythm.cfg: -------------------------------------------------------------------------------- 1 | # 13. VIP Biorhythm 2 | # 3 | # The theory of Biorhythm states that there are thre predominant cycles that can influence 4 | # human behavior. These include a 23-day physical cycle, a 28-day emotional cycle and a 5 | # 33-day intellectual cycle. All three cycles start at birth and continue throughout life. 6 | # 7 | # Each cycle consists of a positive and a negative period. Physical, Emotional and 8 | # Intellectual aspects are enhanced during positive periods. Poor performance is 9 | # indicated by the negative period of a cycle. 10 | # 11 | # A critical day occurs on the crossover from the positive to the negative period or 12 | # vice versa. A critical day indicates instability in a particular aspect. 13 | # 14 | # 15 | # How to Use VIP Biorhythm 16 | # 17 | # 1. Load the CHIP-8 interpreter at 0000-01FF and 18 | # the game starting at 0200. 19 | # 20 | # 2. Turn RUN switch on. 21 | # 22 | # 3. Enter birthdate using 2-digit month, 2-digit day, and 4-digit year. 23 | # 24 | # 4. Enter start date using 2-digit month, 2-digit day, and 4-digit year. The start date 25 | # indicates the first day of the 32-day Biorhythm chart the program will generate. 26 | # 27 | # 5. After a period of calculation proportional to the span of time involved, the three 28 | # cycle curves will be shown for a 32-day period. Each horizontal bit position 29 | # represents one day in the cycle. The start day, on the left, and every seventh 30 | # day are indicated by week markers. The first day of a positive or negative period 31 | # is a critical day. 32 | # 33 | # 6. To advance the start date, hold key F down until the desired date is reached. 34 | # To decrement the start date, hold key B down. These functions allow changing the 35 | # start date slightly without reentering all the dates and waiting for the calculations 36 | # to be preformed. 37 | # 38 | # 7. Press Key 0 to clear the screen and enter a new set of dates. 39 | # 40 | # 41 | # 42 | # See VIPG1-13.jpg 43 | # 44 | # 45 | # INTERPRETATION 46 | # 47 | # 48 | # Physical Emotional Intellectual 49 | # 50 | # Positive Period Strong Good moods Good Judgement 51 | # (Up) Vigorous Cooperative Sharp Mentally 52 | # 53 | # Negative Period Tire easily Low Enthusiasm Low attentiveness 54 | # (Down) Less Stamina Feelings Subdued and concentration 55 | # 56 | # Critical Days Susceptible to injury emotionally unstable Poor memory 57 | # (Crossover) or Illness Upset easily Prone to mistakes 58 | # Low endurance 59 | # 60 | # Author: Jef Winsor 61 | # Controls: 62 | 0x0: Enter number 0/clear screen 63 | 0x1: Enter number 1 64 | 0x2: Enter number 2 65 | 0x3: Enter number 3 66 | 0x4: Enter number 4 67 | 0x5: Enter number 5 68 | 0x6: Enter number 6 69 | 0x7: Enter number 7 70 | 0x8: Enter number 8 71 | 0x9: Enter number 9 72 | 0xf: advance start date 73 | 0xb: decrement start date 74 | 2000 75 | on 76 | -------------------------------------------------------------------------------- /include/frontend.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FRONTEND_HPP 2 | #define FRONTEND_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | namespace Frontend { 12 | std::string WriteRight(std::string& input, unsigned row, const std::string& appended) { 13 | unsigned count = 0; 14 | size_t pos = 0; 15 | while ((pos = input.find('\n', pos)) != std::string::npos) { 16 | count++; 17 | if (count == row + 1) { 18 | // Replace the n-th newline with the appended string 19 | input.replace(pos, 1, "\t" + appended); 20 | break; 21 | } 22 | pos++; 23 | } 24 | return input; 25 | } 26 | 27 | std::string WriteAbove(std::string& input, const std::string& text, int width) { 28 | return std::string(width - text.length()/2, ' ') + text + 29 | std::string(width - text.length()/2, ' ') + "\n" + input; 30 | } 31 | 32 | std::string WriteRegs(std::string& input, std::array regs) { 33 | for (size_t i = 0; i < regs.size(); i += 4) { 34 | std::stringstream ss; 35 | ss << std::uppercase << 36 | "V" << std::hex << i+0 << ": " << std::setfill('0') << std::setw(2) << (int) regs[i+0] << " " << 37 | "V" << std::hex << i+1 << ": " << std::setfill('0') << std::setw(2) << (int) regs[i+1] << " " << 38 | "V" << std::hex << i+2 << ": " << std::setfill('0') << std::setw(2) << (int) regs[i+2] << " " << 39 | "V" << std::hex << i+3 << ": " << std::setfill('0') << std::setw(2) << (int) regs[i+3] << std::endl; 40 | WriteRight(input, i/4, ss.str()); 41 | } 42 | return input; 43 | } 44 | 45 | std::string WritePC(std::string& input, uint16_t PC) { 46 | std::stringstream ss; 47 | ss << std::hex << "PC: " << std::setw(3) << std::setfill('0') << std::uppercase << (int)PC << std::endl; 48 | WriteRight(input, 5, ss.str()); 49 | return input; 50 | } 51 | 52 | std::string WriteStack(std::string& input, std::array& stack) { 53 | std::stringstream ss; 54 | WriteRight(input, 6, "Stack[0-3]:\n"); 55 | ss << std::hex << std::setw(3) << std::setfill('0') << std::uppercase << (int) stack[0] << " "; 56 | ss << std::hex << std::setw(3) << std::setfill('0') << std::uppercase << (int) stack[1] << " "; 57 | ss << std::hex << std::setw(3) << std::setfill('0') << std::uppercase << (int) stack[2] << " "; 58 | ss << std::hex << std::setw(3) << std::setfill('0') << std::uppercase << (int) stack[3] << std::endl; 59 | WriteRight(input, 7, ss.str()); 60 | return ""; 61 | } 62 | 63 | std::string WriteSP(std::string& input, uint16_t SP) { 64 | std::stringstream ss; 65 | ss << std::hex << "SP: " << std::setw(2) << std::setfill('0') << std::uppercase << (int)SP << std::endl; 66 | WriteRight(input, 8, ss.str()); 67 | return input; 68 | } 69 | 70 | 71 | std::string WriteI(std::string& input, uint16_t I) { 72 | std::stringstream ss; 73 | ss << std::hex << "I: " << std::setw(3) << std::setfill('0') < 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | #ifdef RUN_UNIT_TESTS 20 | class Chip8Tester; // forward declaration of unit tester class 21 | #endif 22 | 23 | #define ROWS 32 24 | #define COLS 64 25 | #define ROM_OFFSET 0x200 26 | 27 | typedef struct opcode_t { 28 | uint8_t prefix : 4; 29 | uint8_t x : 4; 30 | uint8_t y : 4; 31 | uint8_t n : 4; 32 | uint8_t nn : 8; 33 | uint16_t nnn : 12; 34 | } opcode_t; 35 | 36 | enum { 37 | STATE_RUNNING = 0, 38 | STATE_STEPPING, 39 | STATE_PAUSED, 40 | STATE_STOPPED, 41 | }; 42 | 43 | class Chip8 { 44 | public: 45 | Chip8(); 46 | ~Chip8(); 47 | /** 48 | * @brief Load a ROM from a filepath 49 | * @param file path to the ROM file 50 | */ 51 | void LoadRom(const char* filename); 52 | /** 53 | * @brief Run the emulator after loading the ROM. It runs the 54 | fetch-decode-execute cycle until the user interrupts it. 55 | */ 56 | void Run(size_t max_iterations = std::numeric_limits::max()); 57 | #ifdef RUN_UNIT_TESTS 58 | /** Unit tester class; has access to this class' data */ 59 | friend class Chip8Tester; 60 | #endif 61 | private: 62 | /** 63 | * @brief Fetch the instruction that PC points to. 64 | * @return The current instruction as a 2-byte. 65 | */ 66 | inline uint16_t Fetch() const; 67 | /** 68 | * @brief Decode the given instruction. 69 | * @param instr The 2-byte instruction to decode. 70 | * @returns The decoded instruction as an opcode structure. 71 | */ 72 | inline opcode_t Decode(uint16_t instr) const; 73 | /** 74 | * @brief Execute the decoded opcode 75 | * @param opc The opcode to execute 76 | */ 77 | void Exec(const opcode_t& opc); 78 | /** @brief Listen for a key press (without blocking the program). */ 79 | void ListenForKey(); 80 | /** 81 | * @brief Wait for a key press (blocks the program). 82 | * @returns The pressed Chip8 keypad key. 83 | */ 84 | uint8_t WaitForKey(); 85 | /** @brief Clear the screen. */ 86 | inline void Cls(); 87 | /** @brief Render the entire screen. */ 88 | void RenderFrame(); 89 | /** @brief Update the delay and sound timer. */ 90 | void UpdateTimers(); 91 | std::array ram_; // Main memory 92 | uint16_t PC_; // Program counter - points to current instruction 93 | std::array regs_; // Arithmetic operation registers 94 | std::array stack_; // Stack - stores addresses for subroutine calls 95 | uint16_t SP_; // Stack pointer 96 | uint16_t I_; // Index register - read and write in RAM 97 | std::array frame_buffer_; // Pixels to render (monochrome) 98 | /** The hardware clock - i.e. how many instructions the emulator can run per sec */ 99 | unsigned freq_; 100 | /** If non zero, ticks down at 60 Hz */ 101 | std::atomic delay_timer_; 102 | /** If non zero, ticks down at 60 Hz. Should make the system beep is zero. */ 103 | std::atomic sound_timer_; 104 | /** Maps keys from a real keyboard to Chip8's keypad */ 105 | std::unordered_map keyboard2keypad_ = Keypad::keyboard2keypad; 106 | std::unordered_map pressed_keys_; 107 | 108 | std::atomic run_timers_; // flag to start delay and sound timer thread 109 | std::atomic run_key_thread_; // flag to start keyboard listener thread 110 | std::mutex mutex_key_press_; 111 | /** Running state - running/paused/stepping/stopped */ 112 | std::atomic state_; 113 | std::unique_ptr cfg_parser_; 114 | /** Last key pressed by the actual keyboard */ 115 | std::atomic kbd_pressed_key_; 116 | // NOTE: threads must be started after their addressees (atomic vars) are constructed 117 | std::thread timer_thread_; 118 | std::thread key_thread_; 119 | 120 | /** Whether to use SCHIP1.1's quirks: https://chip8.gulrak.net/ */ 121 | bool use_quirks_; 122 | 123 | }; 124 | 125 | #endif /* CHIP8_HPP */ 126 | -------------------------------------------------------------------------------- /roms/Lunar_Lander.cfg: -------------------------------------------------------------------------------- 1 | # 15. Lunar Lander 2 | # 3 | # You are descending to the floor of a 250-foot crater. The right side of the TV display 4 | # shows amount of fuel, feet from bottom and fall rate per cycle. Lenght of fall rate 5 | # cycle changes from about 1/4 second to one second. The fall rate increases by one 6 | # foot per second/per cycle. The computer will update the display only at the end of 7 | # each cycle. 8 | # 9 | # The left side of the display shows the Lunar Lander descending from 250 feet. The 10 | # Lander will first appear at one of sixteen locations. It will then slowly drop down 11 | # the crater. The crater walls have rock formations protruding from them which must 12 | # be avoided. If either rocket motor should touch any part of the crater, the Lander 13 | # will explode in a cloud of smoke and disintegrate. (Remember, these are atomic 14 | # thrusters and react like miniature atomic bombs upon impact). 15 | # 16 | # When you reach 32 feet, the display will change, and you will see the crater floor. 17 | # You must now ladn between the rock formations on the floo and maintain a minimum fall 18 | # rate. As you drop your fall rate to zero, you soon discover that the Lander becomes 19 | # more and more unstable just as any hovering object would. And now with perfect skill 20 | # you guide the ship in for a perfect touch down, or? 21 | # 22 | # How to Play VIP Lunar Lander 23 | # 24 | # 1. Load the CHIP-8 interpreter at 0000-01FF and the game starting at 0200. 25 | # 26 | # 2. Key 2 controls the thrust of the Lunar Lander. One tone beep equals one pound 27 | # of fuel and one foot of thrust. 28 | # 29 | # 3. Key 4 controlls the horizontal stabilizing engine, and moves the Lander one 30 | # foot to the left per one pound of fuel. 31 | # 32 | # 4. Key 6 controls the stabilization to the right. 33 | # 34 | # 5. Stabilization controls only react after: (a) a complete fall cycle, or (b) after 35 | # the Key 2 thrust control is pressed. You must be aware of this at all times or 36 | # you will crash even though either key 4 or 6 is pressed. 37 | # 38 | # 6. At the end of each cycle, the Lunar Lander will move randomly at the rate of one 39 | # foot either right or left. Therefore, as the fall rate approaches zero, the Lander 40 | # becomes more uncontrollable, and your horizontal stabilizers react more quickly. 41 | # (Just like the real thing). 42 | # 43 | # 7. If you land too fast, the Lander will bounce and flip over. 44 | # 45 | # 8. If you run out of fuel, the Lander will crash. 46 | # 47 | # 9. You will not be allowed to go above 250 feet. If you try to do so, you will just 48 | # waste precious fuel. 49 | # 50 | # 10. Once you reach 32 feet and move into the landing display, you will not be allowed 51 | # to rise above 26 feet. Trying to do so will waste more precious fuel. 52 | # 53 | # 11. Game options 54 | # 55 | # You may select one of three options at the begining of the game. 56 | # 57 | # Option 1: You start at 250 feet. You are given 150 pounds of fuel. You must land 58 | # at 5 feet per second or less. The initial descent display is easy to 59 | # maneuver through. 60 | # 61 | # Option 2: You start at 250 feet. You have 200 pounds of fuel. You must land at 3 feet 62 | # per second or less. The initial descent display is hard to maneuver through. 63 | # 64 | # Option 3: You start at 32 feet. You have 100 pounds of fuel. You must land at 3 feet 65 | # per second or less. This display allows you to practice landing. 66 | # 67 | # You should consider Option 2 as the complete game when trying for Astronaut Ratings. 68 | # 69 | # 12. Astronaut Ratings 70 | # 71 | # The computer will award you with ratings depending on how good a lander you are. 72 | # 73 | # You will obtain the rating of Astronaut by landing without regard to fall rate or 74 | # fuel. You must not exceed fall rate, or zero fuel. 75 | # 76 | # You will obtain the rating of Astronaut Pilot, by ladning with regard to fuel. 80 77 | # pounds or more if you land between the two rock formations on the floor, and 50 78 | # pounds or more if you land between the crater wall and a rock formation. 79 | # 80 | # You will obtain the rating of Astronaut Captain by landing the ship with regard to 81 | # fuel as per above and if the fall rate is two feet per second or less. (Not an 82 | # easy task, as you will soon discover). 83 | # 84 | # 13. About Graphics 85 | # 86 | # This game has some interesting graphic displays when you run out of fuel, crash, 87 | # land too fast, when you land successfully. I will leave them to your discovery, 88 | # as they are fun to watch. 89 | # 90 | # 14. Landing Sites 91 | # 92 | # The final landing display will appear somewhat like the illustration below 93 | # (VIPG1-15.jpg). You may land in any of the three locations pointed to. To land 94 | # between the crater walls and rock formations, you will have to first bring the 95 | # Lander to a zero fall rate while hovering in the center landing area. Then bring 96 | # the ship up and just over the rock formation, and let it drop down to the landing 97 | # site. At the last moment, a few taps on the thruster button will slow the lander 98 | # to the required fall rate. It will take you many tries to land here, but don't 99 | # give up, it can be done!! 100 | # 101 | # NOTE: VIP Lunar Lander requires at least 3K of RAM. RAM memories are available to 102 | # increase your VIP from 2K to 3K at your local computer store or by contacting: 103 | # RCA VIP Marketing 104 | # New Holland Avenue 105 | # Lancaster, PA 17604 106 | # Author: Udo Pernisz, 1979 107 | # Controls: 108 | 0x2: thrust 109 | 0x4: left 110 | 0x6: right 111 | on 112 | -------------------------------------------------------------------------------- /roms/Programmable_Spacefighters.cfg: -------------------------------------------------------------------------------- 1 | # 14. VIP Programmable Spacefighters 2 | # 3 | # Programmable Spacefighters is a combat game involving 2 to 8 spaceships competing 4 | # for the domination of a contained field in space. The field of play is a 5 | # two-dimensional representation of an oblong spheroid. 6 | # 7 | # The movement and fire of each spacefighter is controlled by programming a series of 8 | # commands into each fighter's instruction storage table. Once all the spacefighters 9 | # are programmed, they carry out their commands by sequentially executing a single step 10 | # at a time. The play of the game ins divided into rounds. Each spacefighter may 11 | # execute between 1 and 15 commands per round. 12 | # 13 | # The fighters all have the same appearance and capabilities. Players distinguish 14 | # between fighters by examining the defense strength and position of their fighters 15 | # at the begining of every round. 16 | # 17 | # Each fighter may face in any of 8 directions. All firing and forward movement 18 | # occurs in the direction the fighter is currently facing. As a spacefighter crosses 19 | # outside the two-dimensional field of play, it wraps around and re-enters on the 20 | # opposite side of the field. Laser bursts terminate when they travel outside the 21 | # field or hit a target. 22 | # 23 | # Each round consists of a selected number of steps. Each step is executed in 2 parts. 24 | # During the first part, every spacefighter wishing to fire may execute a fire operation. 25 | # The defense strength of any fighter which is hit by a laser burst is reduced by 1 26 | # and a small flash appears. 27 | # 28 | # After all fighters have had an opportunity to execute fire instructions, the movement 29 | # part of the step begins. Any fighter which has had its defense strength reduced to 0 30 | # is destroyed and a longer flash appears. The defense strength is changed to a special 31 | # code so that the fighter will no longer be programmable or take part in the execution 32 | # phase. The destroyed fighter will still be open to examination during the Defense/ 33 | # Position Check phase. Fighers having a defense strength greater than 0 may execute 34 | # a movement command if there is one. Breaking each step into 2 such parts removes any 35 | # strategic advantage to moving first. 36 | # 37 | # The nature of the game, in that there are variable parameters and no fixed victory 38 | # conditions, allows the players a lot of freedom. Two to eight players can command 39 | # single fighters. Four or less players can each command multiple fighters. Two 40 | # fleets could complete to destroy their opponents' flagship first. Handicaps can 41 | # be implemented through an imbalance of fighters in different fleets. An odd number 42 | # of players can play in a free for all or team game. In a non-combat approach, a full 43 | # complement of space fighters could be programmed to preform in kaleidoscope or other 44 | # type formations. 45 | # 46 | # 47 | # How to Play VIP Programmable Spacefighters 48 | # 49 | # 1. Load the CHIP-8 interpreter at 0000-01FF and 50 | # the game starting at 0200. 51 | # 52 | # 2. Turn RUN switch on. 53 | # 54 | # 3. Initalization of Game. 55 | # Various parameters are entered at the start of the game to determine the number 56 | # of spacefighters and other aspects of play. 57 | # 58 | # S: Enter number of spacefighters. 1 to 8 spacefighters may be used. The program 59 | # will adjust invalid enteries to the nearest valid number. 60 | # 61 | # D: Enter defense strength. A spacefighter may be hit from 1 to F times by laser 62 | # before being destroyed. 63 | # 64 | # E: Enter number of command entries. A spacefighter may be programmed with 1 to F 65 | # commands during the programming phase of each round. 66 | # 67 | # C: Enter clock duration/no clock. A timer clock 1 to F phorseks in duration may 68 | # be selected. Each phorsek equals 4 seconds. The clock is not enabled if a 0 69 | # is entered. The clock time is the time allowed for each fighter to be set up. 70 | # 71 | # F: Enter fire power available. A spacefighter may be allowed to fire 1 to F 72 | # laser bursts per round. 73 | # 74 | # A: Enter accumulation/no accumulation. A spacefighter may be allowed to accumulate 75 | # all unused laser bursts by entering 1 to F. Accumulation is not allowed if a 76 | # 0 is entered. 77 | # 78 | # 4. Presentation of Field and Spacefighters. 79 | # The two-dimensional representation of the palying fields consists of 10 vertical 80 | # by 15 horizontal positons indicated by grid markings around the perimeter. The 81 | # spacefighters will be in their initial positions. 82 | # 83 | # 5. Defense/Position Check. 84 | # Enter number of spacefighter to be examined. 85 | # S: Current spacefighter being examined. 86 | # D: Defense strength of current spacefighter. 87 | # Enter 0 to end defense/position check phase. 88 | # 89 | # 6. Program Spacefighters. 90 | # Surviving spacefighters are programmed in ascending order. Enter 0 to begin 91 | # programming first spacefighter. Defense strength and position are shown during 92 | # programming. 93 | # E: indicates number of enteries left after current command. 94 | # C: indicates time remaining to program current fighter if clock was entered. 95 | # 96 | # COMMAND FUNCTION 97 | # 1 Rotate 45 ccw, Move fwd 98 | # 2 Move fwd 99 | # 3 Rotate 45 cw, Move fwd 100 | # 4 Rotate 45 ccw 101 | # 5 Fire 102 | # 6 Rotate 45 cw 103 | # B Erase all commands and reprogram current spacefighter 104 | # E End programming of current spacefighter 105 | # 7-A,C,D,F Rest, No operation 106 | # 0 Rest, Begin programming next spacefighter 107 | # 108 | # Enter 0 to begin programming each successive spacefighter. 109 | # 110 | # 7. Execute Commands. 111 | # Enter 0 after all spacefighters are programmed to start execution of commands. 112 | # 113 | # 8. The recommended starting point when tryint the game out is with 1 or 2 spacefighters, 114 | # any defense, F (15) entries per round, no clock, F (15) fire power and any accumulation 115 | # S: 1 or 2 116 | # D: 1 117 | # E: F 118 | # C: 0 119 | # F: F 120 | # A: 0 121 | # 122 | # Use the above to try out the movement and fire execution of the fighters. Try 123 | # performing loops, figure 8's, tight turns, maximum fire coverage techniques, etc. 124 | # Start out simple when first playing the game against an opponent. 125 | # Eight spacefighters can get very complicated. 126 | # 127 | # 9. Changing different parameters creates very different effects on the play of the game. 128 | # The main effect of a large number of fighters is a more complicated game. A large 129 | # number of enteries per round results in a more lively game involving farsighted 130 | # planning. Fewer enteries involves more thinking and reacting to immediate developments 131 | # but is not without strategy when there are multiple fighters opposing each other. 132 | # The clock provides pressure which can be greatly increased by the moves-to-time ratio. 133 | # More fire power increases the importance of movement and positioning. Accumulation 134 | # of fire power can have long range effects if there is low fire power and a large 135 | # number of moves per round. 136 | # Author: Jef Winsor 137 | on 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | ▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 3 | ▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒ 4 | ▒▒▒▒▒▒ ▒▒▒ ▒▒▒ ▒▒▒▒ ▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒ 5 | ▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 6 | ▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 7 | ▒▒▒▒▒▒ ▒▒▒ ▒▒▒ ▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒ 8 | ▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒ 9 | ▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 10 | ``` 11 |
12 | 13 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 14 | [![C/C++ CI build & test](https://github.com/leonmavr/chip-8/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/leonmavr/chip-8/actions/workflows/c-cpp.yml) 15 | 16 | kopimi logo 17 | 18 | 19 |
20 | 21 | # 1. About 22 | 23 | I wrote a Chip8 virtual machine. Chip8 was implemented by a number of 24 | [computers](http://www.hobbylabs.org/telmac.htm) in the 70s and HP 25 | [calculators](https://www.hpcalc.org/hp48/docs/faq/48faq-8.html) in the 80s. 26 | Instead of using actual microprocessor opcodes, it was always designed to be a 27 | virtual language and be interpreted at 28 | [runtime](http://www.emulator101.com/introduction-to-chip-8.html). 29 | 30 | Demo gif 31 | 32 | (A high quality video version of the gif is found 33 | [here](https://github.com/leonmavr/chip-8/raw/master/assets/demo_grid.mp4).) 34 | 35 | # 2. Usage 36 | 37 | ### 2.1 Compilation 38 | 39 | You'll need a Linux computer with a compiler that supports C++17 and `make` installed. 40 | This project has no third-party dependencies and renders on the terminal. 41 | To compile: 42 | ``` 43 | make 44 | ``` 45 | To run: 46 | ``` 47 | ./play path/to/rom.ch8 48 | ``` 49 | Compile and run the sanity tests (mostly for CI): 50 | ``` 51 | make test 52 | ``` 53 | To clean all object and executable files: 54 | ``` 55 | make clean 56 | ``` 57 | 58 | ### 2.2 Usage 59 | 60 | #### 2.2.1 Controls and UI 61 | 62 | The keys along with their descriptions are listed on the right panel of the UI. 63 | You can press each \[B\]racketed key to send an input. 64 | 65 | #### 2.2.2 Changing the controls 66 | 67 | Chip8's hex keypad has been mapped to the keyboard in the 68 | following way by default: 69 | ``` 70 | +---+---+---+---+ +---+---+---+---+ 71 | | 1 | 2 | 3 | C | | 1 | 2 | 3 | 4 | 72 | +---+---+---+---+ +---+---+---+---+ 73 | | 4 | 5 | 6 | D | | q | w | e | r | 74 | +---+---+---+---+ --> +---+---+---+---+ 75 | | 7 | 8 | 9 | E | | a | s | d | f | 76 | +---+---+---+---+ +---+---+---+---+ 77 | | A | 0 | B | F | | z | x | c | v | 78 | +---+---+---+---+ +---+---+---+---+ 79 | ``` 80 | You can edit the keys in 81 | [keypad.hpp](https://github.com/leonmavr/chip-8/blob/master/include/keypad.hpp). 82 | 83 | #### 2.2.3 .cfg files 84 | 85 | Each rom (`.ch8` file in `roms` directory) is accompanied by a `.cfg` file. 86 | A `.cfg` file is optional and describes each control key and sets the frequency 87 | each rom will run at. Chip8 games used to run on different machines over the 88 | decades so each one runs on a different frequency. More detailed description of 89 | `.cfg` files can be found at `roms/README.md`. 90 | 91 | # 3. Features 92 | 93 | - [x] CPU and renderer. 94 | - [x] UI including controls and debugger view including pause, exit, and stepping key. 95 | - [x] .cfg file for each ROM with editable presets (such as emulation frequency) and key descriptions for each rom. Found at `roms/*.cfg`. 96 | - [x] Togglable quirks \[1\] (XO-CHIP and SCHIP1.1 quirks are offered together). 97 | - [x] Configurable keys. 98 | - [x] CI. 99 | - [ ] Sound; probably never going to implement this. 100 | 101 | # 4. Architecture and implementation 102 | 103 | ### 4.1 System components 104 | 105 | ``` 106 | <--- Main memory ---> 107 | Read Registers 108 | <- only -> Stack 0 1 15 109 | +-----------+-------+ +-----------+ +--+ +--+ +--+ 110 | | | | | | | | | | | | | . . . | | 111 | | Instr/s | Fonts | +-----------+ +--+ +--+ +--+ 112 | | | | 0x18 ^ ^ 113 | +-----------+-------+ ^ | | 114 | 0xFFF ^ 0x200 0x0 | 2 bytes 1 byte 115 | | ^ | 116 | | | | 117 | Program ------+ | Delay Sound 118 | start | | timer time 119 | +-----------------------|------+ +---+ +---+ 120 | Program | Stack | | | | | | 121 | counter | pointer | Index register +---+ +---+ 122 | (PC) | (SP) | (I) 1 byte 1 byte 123 | +----+ | +----+ | +----+ 124 | | |----+ | |------+ | | 125 | +----+ +----+ +----+ 126 | 2 bytes 2 bytes 2 bytes 127 | 128 | Monochrome renderer 129 | +----------------------+ ^ 130 | | | | 131 | | | 32 132 | | | | 133 | +----------------------+ v 134 | <-------- 64 ----------> 135 | ``` 136 | 137 | 138 | ### 4.2 Implementation overview 139 | 140 | Here is the idea of this implementation. Details are omitted. 141 | ``` 142 | +-------+ 143 | | start | 144 | +-------+ 145 | | 146 | +------>---------------------+ 147 | | | 148 | +------>--------+ | 149 | | | | 150 | | +~~~~~~~~~+ +~~~~~~~~~~+ 151 | +---------+ | Delay & | | Key | 152 | | Set PC | | sound | | listener | 153 | +---------+ | thread | | thread | 154 | | +~~~~~~~~~+ +~~~~~~~~~~+ 155 | v | | 156 | +-----<-------+ | | 157 | | | +-->--+--<---+ 158 | +-------+ | | 159 | | Fetch | ^ x 160 | +-------+ | / \ 161 | | | / \ 162 | instruction | / \ 163 | v +---x End x----------+ 164 | +--------+ n \ loop/ y | 165 | | Decode | \ ? / | 166 | +--------+ \ / | 167 | | x v 168 | opcode | | 169 | v | | 170 | +---------+ | +--------------+ 171 | | Execute | | | Join threads | 172 | +---------+ | +--------------+ 173 | | | | 174 | v ^ v 175 | | | | 176 | +--------+ | | 177 | | Render | | | 178 | +--------+ | | 179 | | | | 180 | +--------->-----------+ | 181 | | 182 | +------------------------------------+ 183 | | 184 | v 185 | +-----+ 186 | | End | 187 | +-----+ 188 | ``` 189 | The emulator uses 3 threads in total. More detailed comments on how the fetch- 190 | decode-execute cycle works are found in the source file `chip8.cpp`. 191 | Instructions are implemented in a neat but non-optimised instruction table. 192 | This suffices since we typically run on a 300 to 2000 instruction per second 193 | clock. More comprehensive techninal resources are found in the reference 194 | section. 195 | 196 | ### 4.3 Quirks 197 | 198 | As mentioned in the features, togglable XO-CHIP and SCHIP1.1 quirks are offered 199 | together. The most noticeable quirk is the sprite wrapping; if any pixel of a 200 | sprite meets the right border, it's wrapped around. If quirks are off, then the 201 | sprite is clipped till the origin crosses the right wall. This is demonstrated 202 | below during a Brix gameplay. 203 | 204 | quirks Text 205 | 206 | # 5. Minor improvements/fixed issues 207 | 208 | - [x] Get rid of excessive screen flicker ([f27bd6d](https://github.com/leonmavr/chip-8/commit/f27bd6d0bb2c32fe9879f90c9354cb34d11e9438)) 209 | - [x] Several race conditions fixed - thanks to @Skeeto. 210 | 211 | # References and credits 212 | 1. [Steffen Schumann's instruction table](https://chip8.gulrak.net/) 213 | 2. [Eric Bryntse's overview](http://devernay.free.fr/hacks/chip8/schip.txt) 214 | 3. [Matthew Mikolay's instruction set writeup](https://github.com/mattmikolay/chip-8/wiki/CHIP%E2%80%908-Instruction-Set#notes) 215 | 4. [Columbia uni's technical guide](https://www.cs.columbia.edu/~sedwards/classes/2016/4840-spring/reports/Chip8.pdf) 216 | 5. [emudev subreddit](https://reddit.com/r/emudev) for the inspiration 217 | 6. [dmatlack](github.com/dmatlack) for the [roms](https://github.com/dmatlack/chip8/tree/master/roms/games) 218 | -------------------------------------------------------------------------------- /src/chip8.cpp: -------------------------------------------------------------------------------- 1 | #include "chip8.hpp" 2 | #include "frontend.hpp" 3 | #include "cfg_parser.hpp" 4 | #include "term.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define KEY_ESC 27 19 | #define ERROR_POINTER() { Chip8::~Chip8(); throw std::runtime_error("Fatal: Invalid program or stack pointer!\n"); } 20 | 21 | static struct termios orig_termios; 22 | 23 | static void SetNonBlockingInput() { 24 | tcgetattr(STDIN_FILENO, &orig_termios); 25 | struct termios tty = orig_termios; 26 | // do not return what's written, do not echo 27 | tty.c_lflag &= ~(ICANON | ECHO); 28 | tcsetattr(STDIN_FILENO, TCSANOW, &tty); 29 | fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); 30 | } 31 | 32 | static void ResetBlockingInput() { 33 | tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); 34 | fcntl(STDIN_FILENO, F_SETFL, 0); 35 | TPRINT_SHOW_CURSOR(); 36 | } 37 | 38 | 39 | Chip8::Chip8(): 40 | ram_{}, 41 | PC_(ROM_OFFSET), 42 | regs_{}, 43 | stack_{}, 44 | SP_(0x00), 45 | I_(0x000), 46 | frame_buffer_{}, 47 | freq_(350), 48 | delay_timer_(0x00), 49 | sound_timer_(0x00), 50 | run_timers_(true), 51 | run_key_thread_(true), 52 | state_(STATE_RUNNING), 53 | cfg_parser_(nullptr), 54 | kbd_pressed_key_('\0'), 55 | timer_thread_(std::thread(&Chip8::UpdateTimers, this)), 56 | key_thread_(std::thread(&Chip8::ListenForKey, this)), 57 | use_quirks_(true) 58 | { 59 | // preload memory with sprites 60 | constexpr std::array font_sprites = { 61 | 0xF0, 0x90, 0x90, 0x90, 0xF0, /* 0 */ 0x20, 0x60, 0x20, 0x20, 0x70, /* 1 */ 62 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, /* 2 */ 0xF0, 0x10, 0xF0, 0x10, 0xF0, /* 3 */ 63 | 0x90, 0x90, 0xF0, 0x10, 0x10, /* 4 */ 0xF0, 0x80, 0xF0, 0x10, 0xF0, /* 5 */ 64 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, /* 6 */ 0xF0, 0x10, 0x20, 0x40, 0x40, /* 7 */ 65 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, /* 8 */ 0xF0, 0x90, 0xF0, 0x10, 0xF0, /* 9 */ 66 | 0xF0, 0x90, 0xF0, 0x90, 0x90, /* A */ 0xE0, 0x90, 0xE0, 0x90, 0xE0, /* B */ 67 | 0xF0, 0x80, 0x80, 0x80, 0xF0, /* C */ 0xE0, 0x90, 0x90, 0x90, 0xE0, /* D */ 68 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, /* E */ 0xF0, 0x80, 0xF0, 0x80, 0x80 /* F */ 69 | }; 70 | std::copy(std::begin(font_sprites), std::end(font_sprites), std::begin(ram_)); 71 | 72 | constexpr bool is_pressed = false; 73 | for (size_t i = 0x0; i < 0xF; ++i) 74 | pressed_keys_[i] = is_pressed; 75 | SetNonBlockingInput(); 76 | TPRINT_GOTO_TOPLEFT(); 77 | TPRINT_CLEAR(); 78 | TPRINT_HIDE_CURSOR(); 79 | } 80 | 81 | Chip8::~Chip8 () { 82 | run_timers_ = false; 83 | if (timer_thread_.joinable()) 84 | timer_thread_.join(); 85 | run_key_thread_ = false; 86 | if (key_thread_.joinable()) 87 | key_thread_.join(); 88 | ResetBlockingInput(); 89 | }; 90 | 91 | void Chip8::LoadRom(const char* filename) { 92 | std::ifstream infile(filename); 93 | if (!infile.good()) 94 | throw std::runtime_error("ROM not found\n"); 95 | // Write to memory 96 | infile.read(reinterpret_cast(&ram_[ROM_OFFSET & 0xFFF]), 0xFFF - ROM_OFFSET); 97 | infile.close(); 98 | 99 | const size_t pos_last_dot = std::string(filename).find_last_of("."); 100 | std::string cfg_filename = std::string(filename).substr(0, pos_last_dot) + ".cfg" ; 101 | std::cout << cfg_filename << std::endl; 102 | cfg_parser_ = std::make_unique(cfg_filename); 103 | freq_ = cfg_parser_->frequency(); 104 | use_quirks_ = cfg_parser_->quirks(); 105 | } 106 | 107 | void Chip8::Run(size_t max_iterations) { 108 | auto t_keyboard_start = std::chrono::high_resolution_clock::now(); 109 | // clock functions - get time and sleep (milliseconds) 110 | auto now_ms = []() -> unsigned { 111 | auto now = std::chrono::system_clock::now(); 112 | auto since_epoch = now.time_since_epoch(); 113 | return std::chrono::duration_cast(since_epoch).count(); 114 | }; 115 | auto sleep_ms = [](unsigned ms) { 116 | std::this_thread::sleep_for(std::chrono::milliseconds(ms)); 117 | }; 118 | // t0 and t1 enforce the loop to cycle at frequency `freq_` 119 | unsigned t0 = now_ms(), t1 = now_ms(); 120 | // t0_render and t1_render enforce the renderer to flush at 60 Hz 121 | unsigned t0_render = now_ms(), t1_render = now_ms(); 122 | /** Throttles the instructions at `freq_` instructions per second. It checks 123 | * how many instructions have run every 1/20 sec. If more than `freq_/20`, 124 | * stall the loop until 1/20 of a sec has ellapsed. */ 125 | static unsigned instr_per_50ms = 0; 126 | 127 | size_t iteration = 0; 128 | while (iteration < max_iterations) { 129 | if (state_ == STATE_PAUSED) { 130 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 131 | RenderFrame(); 132 | continue; 133 | } else if (state_ == STATE_STEPPING) { 134 | kbd_pressed_key_ = '\0'; 135 | while (kbd_pressed_key_ != 'S' && kbd_pressed_key_ != 'R' && kbd_pressed_key_ != 'P' && kbd_pressed_key_ != 'Q') { 136 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 137 | if (kbd_pressed_key_ == 'P') { 138 | state_ = STATE_PAUSED; 139 | break; 140 | } else if (kbd_pressed_key_ == 'R') { 141 | state_ = STATE_RUNNING; 142 | break; 143 | } else if (kbd_pressed_key_ == KEY_ESC) { 144 | state_ = STATE_STOPPED; 145 | break; 146 | } 147 | } 148 | } else if (state_ == STATE_STOPPED) { 149 | break; 150 | } 151 | 152 | const uint16_t instr = Fetch(); 153 | const opcode_t opc = Decode(instr); 154 | Exec(opc); 155 | 156 | t1_render = now_ms(); 157 | if (t1_render - t0_render > 16) { 158 | RenderFrame(); 159 | t1_render = t0_render; 160 | } 161 | 162 | 163 | const auto t_keyboard_end = std::chrono::high_resolution_clock::now(); 164 | const unsigned dt_keyboard_ms = std::chrono::duration_cast(t_keyboard_end - t_keyboard_start).count(); 165 | if (dt_keyboard_ms >= 100) { 166 | std::lock_guard lock(mutex_key_press_); 167 | for (auto& pair : pressed_keys_) 168 | pair.second = false; 169 | t_keyboard_start = t_keyboard_end; 170 | } 171 | if (instr_per_50ms++ % (freq_/20) == 0) { 172 | t1 = now_ms(); 173 | if (t1 - t0 < 50) 174 | sleep_ms(50 - (t1 - t0)); 175 | t0 = t1; 176 | } 177 | PC_ += 2; 178 | iteration++; 179 | } 180 | } 181 | 182 | inline uint16_t Chip8::Fetch() const { 183 | /* 184 | * Read two consecutive bytes from RAM and join them into a 16-bit instruction. 185 | * Start to read where PC is currently pointing. Return a 16-bit field containing 186 | * [mem[PC] mem[PC+1]]. Here's how it's done e.g. for the 0x00E0 (CLS) instruction. 187 | * 188 | * RAM bytes: Instruction 189 | * (destination) 190 | * @PC @PC+1 191 | * | | 192 | * v v <-16 bits-> 193 | * +----+----+ +---------+ 194 | * | 00 | E0 | | | 195 | * +----+----+ +---------+ 196 | * 197 | * 1. dest = | 2. dest <<= 8 | 3. dest |= 198 | * mem[PC] | | mem[PC+1] 199 | * +---------+ | +---------+ | +---------+ 200 | * | 00 | | | 00 | | | 00 e0 | 201 | * +---------+ | +---------+ | +---------+ 202 | */ 203 | uint16_t instr = ram_[PC_] << 8; 204 | instr |= ram_[PC_ + 1]; 205 | return instr; 206 | } 207 | 208 | inline opcode_t Chip8::Decode(uint16_t instr) const { 209 | /* 210 | * A Chip8 instruction always takes 2 bytes, or 4 nibbles (half-bytes). 211 | * N0 denotes the highest address nibble (leftmost), N1 the second-highest, 212 | * N2 the third and N3 the fourth. 213 | * An instruction encodes the following 6 fields in an overlapping region; 214 | * prefix, x operand, y operand, nnn, nn, n. These fields are decoded into 215 | * a structure. Not all of them are used together. For example, if either x 216 | * or y is used, then nn is not * used. 217 | * 218 | * MSB (@0xFE) LSB (@0x0) 219 | * | | 220 | * v v 221 | * +----+----+----+----+ 222 | * | N0 | N1 | N2 | N3 | (N: nibble = 4 bits) 223 | * +----+----+----+----+ 224 | * <----> | | | 225 | * prefix | | | 226 | * <----> | | 227 | * | x | | | 228 | * | <----> | 229 | * | | y | | 230 | * | | <----> 231 | * | | n | 232 | * | <---------> 233 | * | nn | 234 | * <--------------> 235 | * nnn 236 | * 237 | * References: 238 | * ----------- 239 | * 1. https://johnearnest.github.io/Octo/docs/chip8ref.pdf 240 | */ 241 | opcode_t decoded; 242 | decoded.prefix = (instr >> 12) & 0x00f; 243 | decoded.n = instr & 0x00f; 244 | decoded.x = (instr >> 8) & 0x00f; 245 | decoded.y = (instr >> 4) & 0x00f; 246 | decoded.nn = instr & 0x0ff; 247 | decoded.nnn = instr & 0xfff; 248 | return decoded; 249 | } 250 | 251 | 252 | void Chip8::Exec(const opcode_t& opc) { 253 | const auto x = opc.x; 254 | const auto y = opc.y; 255 | const auto n = opc.n; 256 | const auto nn = opc.nn; 257 | const auto nnn = opc.nnn; 258 | const auto prefix = opc.prefix; 259 | // aliases for registers and pointers 260 | auto& Vx = regs_[x]; 261 | auto& Vy = regs_[y]; 262 | auto& Vf = regs_[0xf]; // detects overflow, e.g. in addition 263 | auto& I = I_; 264 | auto& PC = PC_; 265 | auto& SP = SP_; 266 | // initialise RNG with a seed 267 | static std::mt19937 seed(std::random_device{}()); 268 | static std::uniform_int_distribution rng(0, 255); // inclusive 269 | 270 | #define EXEC_INSTRUCTION \ 271 | /* assembly , condition , instruction(s) */ \ 272 | X("ERR" , (prefix == 0x0 && nnn == 0x0ee && SP == 0) || \ 273 | ((prefix == 0x1 || prefix == 0x2) && nnn == 0) || \ 274 | (prefix == 0x2 && SP >= stack_.size() - 1) \ 275 | , ERROR_POINTER(); ) \ 276 | X("CLS" , prefix == 0x0 && nnn == 0x0e0 , Cls();) \ 277 | X("RET" , prefix == 0x0 && nnn == 0x0ee , PC = stack_[--SP];) \ 278 | X("JP nnn" , prefix == 0x1 , PC = nnn - 2;) \ 279 | X("CALL nnn" , prefix == 0x2 , stack_[SP++] = PC; PC = nnn - 2;) \ 280 | X("SE Vx nn" , prefix == 0x3 && nn == Vx , PC += 2;) \ 281 | X("SNE Vx nn" , prefix == 0x4 && nn != Vx , PC += 2;) \ 282 | X("SE Vx Vy" , prefix == 0x5 && n == 0x0 \ 283 | && Vx == Vy , PC += 2;) \ 284 | X("LD Vx nn" , prefix == 0x6 , Vx = nn;) \ 285 | X("ADD Vx nn" , prefix == 0x7 , Vx += nn;) \ 286 | X("LD Vx Vy" , prefix == 0x8 && n == 0x0 , Vx = Vy;) \ 287 | X("OR Vx Vy" , prefix == 0x8 && n == 0x1 , Vx |= Vy;) \ 288 | X("AND Vx Vy" , prefix == 0x8 && n == 0x2 , Vx &= Vy;) \ 289 | X("XOR Vx Vy" , prefix == 0x8 && n == 0x3 , Vx ^= Vy;) \ 290 | X("ADD Vx Vy" , prefix == 0x8 && n == 0x4 , uint16_t sum = Vx + Vy; Vx = sum & 0xFF; \ 291 | Vf = sum > 0xFF;) \ 292 | X("SUB Vx Vy" , prefix == 0x8 && n == 0x5 , Vf = Vx >= Vy; Vx = (Vx - Vy) & 0xFF;) \ 293 | X("SHR Vx Vy" , prefix == 0x8 && n == 0x6 , Vf = Vx & 0x1; \ 294 | if (use_quirks_) \ 295 | Vx >>= 1; \ 296 | else \ 297 | Vx = Vy >> 1;) \ 298 | X("SUBN Vx Vy" , prefix == 0x8 && n == 0x7 , Vf = Vy >= Vx; Vx = (Vy - Vx) & 0xFF;) \ 299 | X("SHL Vx Vy" , prefix == 0x8 && n == 0xe , Vf = (Vx >> 7) & 0x1; \ 300 | if (use_quirks_) \ 301 | Vx = (Vx << 1) & 0xFF; \ 302 | else \ 303 | Vx = (Vy << 1) & 0xFF;) \ 304 | X("SNE Vx Vy" , prefix == 0x9 && n == 0x0 \ 305 | && Vx != Vy , PC += 2;) \ 306 | X("LD I nnn" , prefix == 0xa , I = nnn;) \ 307 | X("JP V0 nnn" , prefix == 0xb , PC = nnn + regs_[0] - 2;) \ 308 | X("RND Vx nn" , prefix == 0xc , Vx = rng(seed) & nn;) \ 309 | X("DRW Vx Vy n", prefix == 0xd, \ 310 | do { \ 311 | const auto x0 = Vx; \ 312 | const auto y0 = Vy; \ 313 | Vf = 0; \ 314 | for (uint8_t row = 0; row < n; ++row) { \ 315 | uint8_t sprite = ram_[I + row]; \ 316 | for (uint8_t col = 0; col < 8; ++col, sprite <<= 1) { \ 317 | if (sprite & 0x80) { \ 318 | size_t x = x0 + col; \ 319 | size_t y = y0 + row; \ 320 | if (use_quirks_) { \ 321 | x %= COLS; \ 322 | y %= ROWS; \ 323 | } \ 324 | if (x < COLS && y < ROWS) { \ 325 | size_t index = y * COLS + x; \ 326 | Vf |= frame_buffer_[index]; \ 327 | frame_buffer_[index] ^= 1; \ 328 | } \ 329 | } \ 330 | } \ 331 | } \ 332 | } while(0); ) \ 333 | X("SKP Vx" , prefix == 0xe && nn == 0x9e , if ( pressed_keys_[Vx & 0xF]) PC += 2;) \ 334 | X("SKNP Vx" , prefix == 0xe && nn == 0xa1 , if (!pressed_keys_[Vx & 0xF]) PC += 2;) \ 335 | X("LD Vx DT" , prefix == 0xf && nn == 0x07 , Vx = delay_timer_;) \ 336 | X("LD Vx k" , prefix == 0xf && nn == 0x0a , Vx = WaitForKey();) \ 337 | X("LD DT Vx" , prefix == 0xf && nn == 0x15 , delay_timer_ = Vx;) \ 338 | X("LD ST Vx" , prefix == 0xf && nn == 0x18 , sound_timer_ = Vx;) \ 339 | X("ADD I Vx" , prefix == 0xf && nn == 0x1e , I += Vx;) \ 340 | X("LD F Vx" , prefix == 0xf && nn == 0x29 , I = Vx * 5;) \ 341 | X("LD B Vx" , prefix == 0xf && nn == 0x33 , ram_[(I + 0) & 0xFFF] = (Vx % 1000) / 100; \ 342 | ram_[(I + 1) & 0xFFF] = (Vx % 100) / 10; \ 343 | ram_[(I + 2) & 0xFFF] = Vx % 10;) \ 344 | X("LD [I] Vx" , prefix == 0xf && nn == 0x55 , for (unsigned xx = 0; xx <= x; xx++) \ 345 | ram_[I++ & 0xFFF] = regs_[xx]; \ 346 | if (!use_quirks_) I += x + 1;) \ 347 | X("LD Vx [I]" , prefix == 0xf && nn == 0x65 , for (unsigned xx = 0; xx <= x; xx++) \ 348 | regs_[xx] = ram_[I++ & 0xFFF]; \ 349 | if (!use_quirks_) I += x + 1;) 350 | 351 | #define X(assembly, condition, instructions) if (condition) { instructions; break; } 352 | do { 353 | std::lock_guard lock(mutex_key_press_); 354 | EXEC_INSTRUCTION 355 | } while(0); 356 | #undef X 357 | #undef EXEC_INSTRUCTION 358 | } 359 | 360 | void Chip8::ListenForKey() { 361 | while (run_key_thread_) { 362 | fd_set readfds; 363 | FD_ZERO(&readfds); 364 | FD_SET(STDIN_FILENO, &readfds); 365 | 366 | struct timeval timeout; 367 | timeout.tv_sec = 0; 368 | timeout.tv_usec = 25000; 369 | 370 | int success = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &timeout); 371 | if (success > 0 && FD_ISSET(STDIN_FILENO, &readfds)) { 372 | char c; 373 | read(STDIN_FILENO, &c, 1); 374 | kbd_pressed_key_ = c; 375 | { 376 | std::lock_guard lock(mutex_key_press_); 377 | if (kbd_pressed_key_ == 'S') state_ = STATE_STEPPING; 378 | else if (kbd_pressed_key_ == 'R') state_ = STATE_RUNNING; 379 | else if (kbd_pressed_key_ == 'P' && state_ != STATE_PAUSED) state_ = STATE_PAUSED; 380 | else if (kbd_pressed_key_ == 'P' && state_ == STATE_PAUSED) state_ = STATE_RUNNING; 381 | else if (kbd_pressed_key_ == KEY_ESC) state_ = STATE_STOPPED; 382 | else if (kbd_pressed_key_ == '+' && freq_ < 2000) freq_ += 50; 383 | else if (kbd_pressed_key_ == '-' && freq_ > 50) freq_ -= 50; 384 | else if (kbd_pressed_key_ == 'Q') use_quirks_ = !use_quirks_; 385 | if (keyboard2keypad_.find(kbd_pressed_key_) != keyboard2keypad_.end()) 386 | pressed_keys_[keyboard2keypad_[kbd_pressed_key_]] = true; 387 | } 388 | } 389 | } 390 | } 391 | 392 | 393 | uint8_t Chip8::WaitForKey() { 394 | char ch; 395 | do { 396 | ch = getchar(); 397 | } while (keyboard2keypad_.find(ch) == keyboard2keypad_.end()); 398 | return keyboard2keypad_[ch]; 399 | } 400 | 401 | inline void Chip8::Cls() { 402 | frame_buffer_.fill(0); 403 | TPRINT_GOTO_TOPLEFT(); 404 | TPRINT_CLEAR(); 405 | } 406 | 407 | void Chip8::RenderFrame() { 408 | TPRINT_GOTO_TOPLEFT(); 409 | std::string horizontal = ""; 410 | for (int i = 0; i < COLS; i++) 411 | horizontal += u8"\u2500"; 412 | const std::string border_up = u8"\u250C" + horizontal + u8"\u2510" + "\n"; 413 | const std::string border_down = u8"\u2514" + horizontal + u8"\u2518" + "\n"; 414 | std::string pixels = border_up; 415 | for (size_t row = 0; row < ROWS; ++row) { 416 | static std::array line_buffer {}; 417 | for (size_t col = 0; col < COLS; ++col) { 418 | const size_t index = row * COLS + col; 419 | frame_buffer_[index] != 0 ? line_buffer[col] = 24 : line_buffer[col] = ' '; 420 | } 421 | std::string frame_row(line_buffer.begin(), line_buffer.end()); 422 | const std::string border_left_right = u8"\u2502"; 423 | pixels += border_left_right + frame_row + border_left_right; 424 | pixels += "\n"; 425 | } 426 | pixels += border_down; 427 | // debugger and keyboard controls 428 | Frontend::WriteRegs(pixels, regs_); 429 | Frontend::WriteI(pixels, I_); 430 | Frontend::WritePC(pixels, PC_); 431 | Frontend::WriteSP(pixels, SP_); 432 | Frontend::WriteStack(pixels, stack_); 433 | int line_num = 10; 434 | Frontend::WriteRight(pixels, line_num++, "[P]ause/resume [S]tep [R]un [Esc]ape\n"); 435 | const std::string quirks_state = (use_quirks_) ? "ON " : "OFF"; 436 | Frontend::WriteRight(pixels, line_num++, "[Q]uirks: " + quirks_state + "\n"); 437 | Frontend::WriteRight(pixels, line_num++, "[-] " + std::to_string(freq_) + " Hz [+] \n"); 438 | line_num++; 439 | for (const auto& key_descr: cfg_parser_->key_descrs()) { 440 | std::string key = key_descr.first; 441 | std::string descr = key_descr.second; 442 | Frontend::WriteRight(pixels, line_num++, "[" + key + "] " + descr + "\n"); 443 | } 444 | std::cout << pixels << std::endl << std::flush; 445 | std::this_thread::sleep_for(std::chrono::microseconds(1000)); 446 | } 447 | 448 | void Chip8::UpdateTimers() { 449 | while (run_timers_) { 450 | std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 60)); 451 | if (delay_timer_ > 0) --delay_timer_; 452 | if (sound_timer_ > 0) --sound_timer_; 453 | } 454 | } 455 | --------------------------------------------------------------------------------