Coder Social home page Coder Social logo

chip8's Introduction

CHIP-8 Interpreter, Assembler and Disassembler

This package contains an interpreter for CHIP-8 as well as a command-line assembler and disassembler.

It also supports the SuperChip instructions.

The syntax of the assembler and disassembler is based on the syntax described in Cowgod's Chip-8 Technical Reference v1.0, by Thomas P. Greene

Frédéric Devernay's SVision-8 website has a wealth of information. He also has a collection of CHIP-8 games and programs in his GAMES.zip.

Compilation and Usage

  • Linux: Type make from the shell.
  • Windows: The system was built and tested with the MinGW tools. To compile it type make from the MSYS shell.

To use the emulator:

  • Under Linux: Type ./chip8 game.ch8 where game.ch8 is the binary CHIP-8 file.
  • Under Windows: Type chip8 game.ch8 or chip8-gdi game.ch8 depending on which of the implementations (see below) you want to use.

The assembler and disassemblers are simple command line applications and platform independent.

To use the assembler, type

$ ./c8asm -o file.c8h file.asm

This will assemble file.asm into a binary file.c8h. If the -o is not specified it will default to a.c8h.

To use the disassembler, run the command

$ ./c8dasm a.ch8 > outfile.asm

where a.ch8 is the file you want to disassemble.

Interpreter Implementations

The core of the emulator is in chip8.c. The idea is that this core be platform independent and then hooks are provided for platform specific implementations.

The API is described in chip8.h. The docs target in the Makefile generates HTML documentation from it.

Two implementations are provided in this repository:

  1. A SDL-based implentation (https://www.libsdl.org/) which is intended for portability, and
  2. a native Windows implementation which is intended for small size and requires no third party dependencies.

In both versions

  • bmp.h and bmp.c (together with the fonts/ directory) is used to draw and manipulate the bitmap graphics. See also https://github.com/wernsey/bitmap
  • render.c implements the init_game(), deinit_game() and render() functions that forms the core of both implementations and demonstrates how the interpreter's API works.

The render() function checks the keyboard and executes the interpreter a couple of times by calling c8_step() and redraws the screen if it changed. The SDL and Win32 frameworks were written in such a way that the render() function works with both with only a couple of minor modifications.

The implementations feature a rudimentary debugger: Press F5 to pause a running game. The program counter and the current instruction will be displayed at the bottom of the screen, along with the values of the 16 Vx registers. Press F6 to step through the program to the next instruction and F8 to resume the program.

The Makefile will build the SDL version by default, and build the GDI version under Windows.

SDL Implementation

The SDL-based implementation is intended for portability. The files pocadv.c and pocadv.h implement a wrapper around the SDL that contains the main() function, the SDL event loops and so on.

The included emscripten.mak file is used to compile the SDL implementation to JavaScript with Emscripten for running the interpreter in a web browser. The chip8.html is a wrapper around the Emscripten-generated JavaScript. If you want to use this implementation:

  1. You need to put your CHIP-8 binary file in a ./GAMES/ directory
  2. Run make -f emscripten.mak
  3. Change the Module.arguments variable in the JavaScript in chip8.html
  4. Serve chip8.html in a web server.

I built the emscripten version through the emscripten SDK installed according to the installation instructions. I had some linker errors with Ubuntu's emscripten package that I couldn't resolve.

Win32/GDI Implementation

The native Windows version uses a simple hook around the Win32 GDI and requires no third party dependencies.

gdi.h and gdi.c implements the native Windows code. It implements a WinMain function with the main Win32 events processing loop. It binds the window's GDI context to a Bitmap object so that a render function can draw onto it and fires off periodic WM_PAINT messages which calls the render() function to draw the screen.

Implementation Notes

I've consulted several sources for my implementation (see references below), and there were some discrepancies. This is how I handled them:

  • Regarding 2nnn, cowgod says the stack pointer is incremented first (i.e. stack[++SP]), but that skips stack[0]. My implementation does it the other way round.
  • The Fx55 and Fx65 instructions doesn't change I in my implementation:
    • This is a known quirk.
    • The interpreter now provides QUIRKS_MEM_CHIP8 to control this
  • I've read David Winter's emulator's documentation when I started, but I implemented things differently:
    • His emulator scrolls only 2 pixels if it is in low-res mode, but 4 pixels is consistent with Octo.
    • His emulator's Dxy0 instruction apparently also works differently in lo-res mode.
  • instruction-draw says that images aren't generally wrapped, but muller and Octo seems to think differently.
    • This is alsp known quirk.
    • The interpreter now provides QUIRKS_CLIPPING to control this
  • According to chip8-wiki, the upper 256 bytes of RAM is used for the display, but it seems that modern interpreters don't do that. Besides, you'd need 1024 bytes to store the SCHIP's hi-res mode.
  • hp48_flags is not cleared between runs (See octo-superchip); I don't make any effort to persist them, though.
  • Apparently there are CHIP-8 interpreters out there that don't use the standard 64x32 and 128x64 resolutions, but I don't support those.
  • As far as I can tell, there is not much in terms of standard timings on CHIP-8 implementations. My implementation allows you to specify the speed as the number of instructions to execute per second (through the global variable speed in render.c). The value of 1200 instructions per second seems like a good value to start with.

References and Links

License

This code is licensed under the Apache license version 2:

    Copyright 2015-2016 Werner Stoop

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

TODO/Roadmap/Ideas

  • I really need to fix the "Display wait" quirk. See Timendus's 5-quirks.ch8 test.
  • The quirks need to be in a flags variable so that they can be controlled at runtime
  • The runtime should have a -q command line option to control the quirks
  • The assembler needs an include "file.asm" directive.
    • You need a way to specify how to do the include, because the assembler must be usable even if you're not loading the source from files. I suggest a function pointer that points to c8_load_txt() by default, but can be made to point elsewhere (or set to NULL and disable includes completely)
  • I should consider a text "hello" directive in the assembler, that places a null terminated string in the bytecode. Users might be able to display the text at some point if you have the right sprites; Octo does it.
  • Allow for some hooks in the library to let the SYS nnn (0nnn) instructions break out into the environment outside. * It's meant as a bit of a joke, might be neat if you embed a CHIP-8 interpreter in another program and call out to it as a sort of scripting language.
  • Command line option, like -m addr=val, that will set the byte at addr to val in the RAM before running the interpreter. * A immediate use case is for, example, Timendus's 5-quirks.ch8 test that allows you to write a value between 1 and 3 to 0x1FF and then the program will bypass the initial menu and skip directly to the corresponding test. I imagine that while developing and debugging CHIP-8 programs it might be useful to have such a mechanism.
  • Fix the assembler that doesn't do any bounds checks on stepper->token
  • Breakpoints in the debugger
  • A .map file output by the assembler...

Porting to the Amiga 500 might be an interesting challenge to get it truly portable: The Amiga's bus is word aligned, so if the program counter is ever an odd number then the system might crash when it tries to retrieve an instruction. Also, the Amiga is big endian, so that might reveal some problems as well.

XO-Chip compatibility seems like something worth striving for. Here's a short checklist of the changes. Also look at how Octo modifies some instructions.

chip8's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

chip8's Issues

Possible bug related to scroll left and scroll right

Hey! I think there's an inaccuracy in the implementations of scroll left (00FC) and scroll right (00FB). In scroll right there's a left shift and in scroll left there's a right shift, it should be the other way around if I'm not mistaken.

Here's a link to a possible solution, if you find it acceptable I can make a pull request.

PONG rom

The right side player's score does not increment when they get a point. I've tested the game on another emulator to verify that the problem does not lie in the rom itself.

Assembler not assembling correctly

I was testing this software out (the emulator, assembler and disassembler) and everything worked perfectly fine
until I tried to assemble a very simple program that I made which didn't work on other emulators but it did work on this emulator. I looked into it and I think I found the reason why on some emulators the assembled program doesn't work.

Well let's take a look at the program with which I noticed this issue:

start:
        cls
        ld V0, #0
        ld V1, #1
        ld V2, #2
        ld V3, #3
        ld V4, #4
        ld V5, #5
        ld V6, #5
        ld V7, #7
        ld V8, #8
        ld V9, #9
        ld VA, #a
        ld VB, #b
        ld VC, #c
        ld VD, #d
        ld VE, #e
        JP start

This program should work right? Well it does on this emulator but not on many others. Lets take a look at the assembled a.ch8 file that the assembler produces:

00000000  00 e0 60 00 61 01 62 02  63 03 64 04 65 05 66 05
00000010  67 07 68 08 69 09 6a 0a  6b 0b 6c 0c 6d 0d 6e 0e
00000020  12

(hexdump -C a.ch8 should produce the same result)

The last byte is 0x12 and as we know the opcode 1nnn is the jump (JP) opcode but on this executable it is never "completed" i.e. it's not 1200 it is 12 which is why some emulators/interpreters may interpret this as an unknown opcode or something like that.

Now to prove that that is the case I modified the original test program by adding these 2 lines:

JP start
offset #201

This means that the program doesn't start at 200 but 201, doing this caused the hexdump of the a.ch8 file to be this:

00000000  12 01 e0 60 00 61 01 62  02 63 03 64 04 65 05 66
00000010  05 67 07 68 08 69 09 6a  0a 6b 0b 6c 0c 6d 0d 6e 
00000020  0e 12 01              

Now as we can see at the end the program jumps to 0x201 where as previously it jumped to the address 0x2
And now this program works on all emulators.

Is this a bug or is this something that has been implemented on purpose?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.