Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(+2)

I'm weirdly interested in targeting the original CHIP8 implementation, with the goal being to ensure the game at least runs playably (if not particularly quickly) on the old hardware. It'll be a turn based game, so speed isn't critical. I of course lack the original hardware to test with. Questions about the "VIP" compatability mode:

- Sprite drawing trigger vblank: does this mean no more than roughly 60 sprite calls per second, but other instructions have more leeway?
- Is 15 cycles/frame a reasonable approximation of the original hardware?

And a more general question: have I missed something, or is the only way to update I with a runtime value really to use self-modifying code? The resulting code structures feel quite awkward.

(+2)

Cool! I made two games last year that targeted the original COSMAC VIP CHIP-8 implementation, and I'm working on another now. Historical accuracy is definitely one of the things I enjoy most in Octojam.

"Sprite drawing trigger vblank" means that when Octo encounters a  `sprite` instruction, it will then halt and wait for the next frame is drawn before continuing execution, so drawing sprites will be a bottleneck for speed.

I think 7 or 15 cycles per frame is reasonable. It's all an approximation, of course, since in Octo all instructions take one "cycle" (AFAIK) while it varies between instructions on COSMAC VIP. Here's a table of real-world timing for each instruction.

The best approach is probably to test your game in the Emma02 emulator, which emulates the COSMAC VIP faithfully. As far as I remember it's a little hard to understand how to use it though.

(+1)

It's true that self-modifying code is necessary if you want to load an arbitrary 12-bit address in one cycle. Depending on the specific circumstances, there may be simpler alternatives:

  • just use `i:=NNN` to set a base address and then add an offset with `i += vx`. This is sufficient for many kinds of array indexing.
  • If your array is no more than 256b and elements are wider than 1 byte, you might be able to use pre-multiplied offsets.
  • If you need an array that is larger than 256b and elements are wider than 1 byte, use `i:=NNN` to set a base address and then add your offset several times: twice for 2-byte-wide elements, 4 times for 4-byte-wide elements, etc.
  • If you only need to choose between a handful of 12-bit addresses, you could use conditionals or a jump table dispatched via `jump0`.

I have some examples and more discussion in the Chip8 programming techniques guide