A CPU emulator for running unit tests on Game Boy code.

Related tags

Emulators evunit
Overview

evunit

This is a unit testing application for Game Boy roms. It includes a CPU emulator, and loads test configurations from TOML files.

Configuring a test

Within the test config you can create a heading for each test you want to run, and assign default and expected values for registers

[add-one]
b = 1
[add-one.result]
a = 2

[add-two]
b = 2
[add-two.result]
a = 3

The values you can initialize are:

  • a, b, c, d, e, h, l (8-bit registers)
  • bc, de, hl, pc, sp (16-bit registers)
  • z.f, z.n, z.h, z.c (Boolean flags)

You can assign an integer (or true/false for flags) to any of these (0x for hex), or a label if you have a symfile loaded. To determine which functions should run, you can assign a label to pc.

Sometimes you have configurations which should apply to all tests, like a global variable or the stack pointer. Any configurations at the top of the file (before a heading) are global and apply to all tests.

sp = "wStack.end"

[my-test]
pc = "MyTest"
a = 42
[my-test.result]
b = 42

If the test result is absent, the test will always pass unless it crashes.

Creating an exhaustive set of tests by hand might be tedious, so remember that you an always generate tests in a bash script or any other language of your choice.

for i in {0..7}
do
	echo "
[my-test$i]
pc = \"GetBitA\"
a = $i
[my-test$i.result]
a = $((1 << $i))
"
done
"

Then pipe this into evunit. Note that evunit does not currently support opening - as stdin, so /dev/stdin must be used instead. This is guaranteed to work without issues.

bash config.bash | evunit -c /dev/stdin bin/rom.gb

And you can always use cat to add a handwritten file into the mix.

bash config.bash | cat config.toml - | evunit -c /dev/stdin bin/rom.gb

Additional configuration options

In addition to registers, there are a few other options you can configure. All of these can be configured globally as well as per-test.

crash

Marks an address as a "crash", causing the test to fail if pc reaches it. This is useful for crash handler functions such as rst $38

crash = 0x38
crash = "crash"

enable-breakpoints

Enables or disables printing register info after executing ld b, b and ld d, d. Enabled by default. This configuration can only be used globally.

enable-breakpoints = true
enable-breakpoints = false

timeout

Sets the maximum number of cycles before a test fails. This is useful if you have code that tends to get stuck in infinite loops, or code which can take an extremely long time to complete. The default value is 65536.

timeout = 65536

Diagnosing failures

When a test fails, it outputs some cpu registers depending on the failure reason to help you diagnose the issue. However, sometimes you need to check the state of memory as well; this can be accomplished with the --dump-dir (-d) flag. Pass a directory to this flag and when any test fails a text dump of memory will be placed in the provided directory.

evunit -c fail.toml -d dump/ rom.gb

The dump is simply a giant list of bytes, with headers for each memory type:

[WRAM 0]
0xc000: 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0xc010: 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0xc020: 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0xc030: 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0xc040: 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0xc050: 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0xc060: 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
...
Comments
  • Share ROM between tests

    Share ROM between tests

    Currently, every test uses its own copy of the ROM (Vec<u8> in the AddressSpace); since ROM is immutable, it should be possible to instead take a &[u8] instead; this might conflict with #3, though, unless using something like scoped_thread.

    opened by ISSOtm 6
  • Performance improvement

    Performance improvement

    this branch will track whatever performance improvements I can come up with and depends on #14 . I will only add here changes that provably improve the performance in the tests available to me. I will also later add the tests in a separate branch, including the setup to check performance.

    opened by DaKnig 4
  • Allow specifying (more) arbitrary termination conditions

    Allow specifying (more) arbitrary termination conditions

    Currently, the test requires PC = 0xFFFF to return (and this is enforced on ret by pushing that to the stack just prior). It would be useful to specify other stop location(s), to unit test inlined code as well.

    enhancement 
    opened by ISSOtm 3
  • Explain how tests know when to end

    Explain how tests know when to end

    If I initialize a test with pc = SomeRoutine, I would expect it to end when the routine returns. But there are more complex cases. What about testing a routine that calls other routines, where they may return? What about if SomeRoutine pops its return address off the stack and messes with it? Or if you start with call SomeRoutine / dw SomeData, and SomeRoutine pops its "return address" to use SomeData as an argument? Or if you're testing a whole ROM (so pc isn't initialized) and there may be interrupts?

    opened by Rangi42 2
  • Documentation mistake with Boolean flags

    Documentation mistake with Boolean flags

    The README claims you can use the values "z.f, z.n, z.h, z.c (Boolean flags)". I expect that should say "f.z, f.n, f.h, f.c (Boolean flags)".

    opened by Rangi42 0
  • Add performance analysis tools

    Add performance analysis tools

    without them, we can speculate about performance all we want. adding flame graphs is the easy first step but it is not the whole solution. IMO we should also look at how the program behaves over time under heavy load.

    opened by DaKnig 3
  • Allow setting and testing memory in config files

    Allow setting and testing memory in config files

    What is says on the tin. Suggested syntax: [8:addr], [16l:addr], [16b:addr] (specifying endianness). Larger sizes can be checked by composing, 16-bit is a convenience that cuts in half other "decompositions". (Endianness is too annoying to specify past 16-bit, too.)

    enhancement 
    opened by ISSOtm 1
  • Big input files consume a lot of memory

    Big input files consume a lot of memory

    Exhaustive testing will lead to big input files (obviously, due to combinatorial explosion), which currently makes evunit take a lot of memory (everything is read into memory before processing even begins.

    It might be possible to use serde to generate a "streaming" deserializer, for example.

    enhancement 
    opened by ISSOtm 0
Owner
Evie M.
I make Game Boy games 🐾
Evie M.
Rustual Boy - A Virtual Boy emulator.

Rustual Boy Description Rustual Boy is a Virtual Boy emulator. It can be used to play existing Virtual Boy games, as well as be a helpful development/

null 224 Nov 9, 2022
A Game Boy research project and emulator written in Rust

Mooneye GB Mooneye GB is a Game Boy research project and emulator written in Rust. The main goals of this project are accuracy and documentation. Some

Joonas Javanainen 802 Dec 28, 2022
RustBoyAdvance-NG is a Nintendo™ Game Boy Advance emulator and debugger, written in the rust programming language.

RustBoyAdvance-NG Nintendo GameBoy Advance ™ emulator and debugger, written in rust. WebAssembly Demo: https://michelhe.github.io/rustboyadvance-ng/ P

MishMish 510 Dec 30, 2022
A cross-platform sequencer that internally uses a Game Boy emulator to synthesize the sound

chiptrack chiptrack is a cross-platform sequencer that internally uses a Game Boy emulator to synthesize the sound. It uses: SixtyFPS for the UI. RBoy

Jocelyn Turcotte 26 Dec 7, 2022
Intel 8080 cpu emulator by Rust

i8080 i8080 is a emulator for Intel 8080 cpu. 8080 Programmers Manual 8080 opcodes [dependencies] i8080 = { git = "https://github.com/mohanson/i8080"

Mohanson 83 Dec 27, 2022
Non cycle-accurate emulator of the 6502 cpu, written in pure rust

CPU 6502 A non cycle-accurate emulator implementing all legal 6502 opcodes. What does non cycle-accurate mean? Every instruction on the 6502 takes a n

Pietro 10 Jul 15, 2022
SCEMU The crates.io lib, x86 cpu and systems emulator focused mainly for anti-malware

SCEMU Usage Download the maps32.zip or maps64.zip from: https://github.com/sha0coder/scemu/releases/download/maps/maps32.zip https://github.com/sha0co

sha0coder 11 Dec 25, 2022
Emulator and debugger for LPRS1 ISA & CPU

About LPRSemu is a simple emulator and debugger for LPRS1 ISA & CPU. It supports loading programs from assembly text files, binary string representati

Filip Parag 3 Jan 8, 2023
Nes-emulator - A NES emulator made to learn the Rust programming language

nes-emulator Building $ rustc --version rustc 1.32.0 (9fda7c223 2019-01-16) $ cargo --version cargo 1.32.0 (8610973aa 2019-01-02) $ cargo build --rel

Michael Burge 225 Dec 23, 2022
Intel 8085 CPU emulation in Rust

PP8085 PP808 is a program that emulates the Intel 8085 Microprocessor architecure. The library is written in Rust and aims to mirror the operation of

Parth Pant 6 Jul 7, 2022
Commodore 64 emulator written in Rust

Rust64 - a C64 emulator written in Rust This is my attempt to study the Rust programming language and have fun at the same time. The goal is to presen

Krzysztof Kondrak 214 Dec 27, 2022
A Flash Player emulator written in Rust

website | demo | nightly builds | wiki Ruffle Ruffle is an Adobe Flash Player emulator written in the Rust programming language. Ruffle targets both t

Ruffle 11.2k Jan 8, 2023
A Gameboy Emulator in Rust

RBoy A Gameboy Color Emulator written in Rust Implemented CPU All instructions correct All timings correct Double speed mode GPU Normal mode Color mod

Mathijs van de Nes 512 Dec 23, 2022
Full featured Cross-platform GameBoy emulator by Rust. Forever boys!.

Gameboy Full featured Cross-platform GameBoy emulator. Forever boys!. You can start a game with the following command, here with a built-in game "Boxe

Mohanson 1.2k Jan 2, 2023
NES emulator written in Rust to learn Rust

OxideNES A NES emulator in Rust. CPU should be accurate, PPU is mostly accurate, timing between the 2 is off for some corner cases and hardware qui

null 37 Nov 7, 2022
An NES emulator written in Rust

Pinky Pinky is an NES emulator written in Rust completely from scratch based only on publicly available documentation. You can run it in your Web brow

Koute 709 Dec 23, 2022
NES emulator written in Rust

sprocketnes is an emulator for the Nintendo Entertainment System written in the Rust programming language. Its purpose is to serve as a technology dem

Patrick Walton 725 Dec 27, 2022
NES emulator in rust

NES emulator in Rust plastic is a NES emulator built from scratch using Rust. This is a personal project for fun and to experience emulating hardware

Amjad Alsharafi 27 Dec 15, 2022
ZX Spectrum emulator written in Rust

rustzx ZX Spectrum emulator which I writing in rust. I develop this project just for fun and for learning the basics of computer architecture. License

Vladislav Nikonov 162 Dec 27, 2022