deltabeard / peanut-gb Goto Github PK
View Code? Open in Web Editor NEWA Game Boy (DMG) emulator single header library written in C99. Performance is prioritised over accuracy.
Home Page: https://projects.deltabeard.com/peanutgb/
A Game Boy (DMG) emulator single header library written in C99. Performance is prioritised over accuracy.
Home Page: https://projects.deltabeard.com/peanutgb/
Double check that the palettes used in Peanut-SDL are correct, since the colour for white is not the expected #FFFFFF.
Users use different keyboards, so the hard-coded button mappings make it difficult for them to test and play Peanut-SDL.
Currently the joypad input requires setting values in gb_t itself. This should be set by calling a function in peanut-gb which will then set the correct values in gb_t.
The framebuffer array will be removed in the PPU rewrite.
Please comment whether Peanut-GB was able to play a game in a playable state or not.
Any issues with playable games must be listed with a comment.
Unplayable games must have a comment describing why it isn't playable.
Playable:
Unplayable:
Bug started in commit c22ea85, where basic serial transmission was implemented.
This commit had fixed a crash when attempting to use the printer in the game, but now you can't enter a Pokemon Center.
Implementing correct timing for serial transmission may fix this issue.
Some systems are faster when they use specific integer types. For variables where the width does not matter, using uint_fast#_t
instead since it should provide the most efficient code for the platform.
Line 3021 in dad7977
Additionally, uint32_t
could become uint_fast32_t
in the following function:
Line 3136 in dad7977
A smaller width could be used where small size is preferable over performance, as in:
Line 3194 in dad7977
Where the code is executed very infrequently.
Instead of using two large switch statements, there should be an array of palette maps.
Insertion sort can be faster for small data.
For a long time now, I have been incorrectly adding "_t" to the end of struct names, not realising that "_t" is typically for typdefs and that they are reserved anyway.
Therefore, all defined "_t" variables in this repository must be changed to "_s".
The equation used is 4194304 / (8192 / 8) = 4096, where 8 is due to transmitting one byte at a time instead of one bit. It is therefore not a magic number as mentioned in https://github.com/deltabeard/peanut-gb/blob/11d0fdf6bde8d04cee690f523d0dd3d839ea73ab/gameboy.h#L82 .
The magic number 40 currently used will have to be looked into. I'm not convinced it is correct since it actually increases the transfer rate of the serial connection.
An increase in performance may be realised by calculating the time until an interrupt is expected as shown in gregtour/gameboy#5 (comment) .
__gb_step_cpu() may be refactored into separate inlined functions in order to allow gb_run_frame() to wrap the main interpreter in a for loop, whilst keeping the current functionality of __gb_step_cpu() for debugging purposes.
Any change in performance should be recorded in BENCHMARK.md, Other optimisations mentioned in the comment above should also be considered.
Pokemon Red and Blue (possibly Yellow) will freeze when white out or Pokemon Center heal if the Peanut-GB SDL2 example is compiled without APU emulation (ENABLE_SOUND=0).
Peanut-GB should include barebones audio emulation, or require an APU emulation at build time.
This ugly remnant of debugging must be fully documented.
This could be due to incorrect use of TMA and TAC registers which are used differently in GBS players.
Music comparisons:
gold_music_compare.zip
The Lawnmower Man for Game Boy reportedly will not start unless joypad interrupts are implemented.
To reproduce:
Expect: Display advances from title screen to conversation
Actual: Title screen does not disappear
(If you do issue joypad interrupts, but you don't vary the LY when they're issued, you'll get a different error message.)
When the program turns on rendering by changing bit 7 of LCDC ($FF40) from 0 to 1, the PPU goes straight to the start of rendering (scanline 0) without sending the vsync pulse to the LCD. This causes the LCD to display a white screen for one frame. (On monochrome systems, this is the same whiter-than-white shade displayed when rendering is off.) Some games, such as Pokémon Pinball, use this blank frame to prepare things before the first visible frame, such as the sprite display list to be DMA copied to OAM during the first vblank.
Though recent BGB emulates this quirk, mGBA, WasmBoy, and Peanut-GB do not. This causes 1 frame of corruption to appear in Pokémon Pinball and numerous other games after a screen transition. Many games on KiGB's compatibility list are there because of this quirk. Affected games may include A Boy and His Blob, Boxing by Tonkin House, Captain Knick-Knack, HyperDunk, Hyper Lode Runner, and Xiang Pu -- Dong Hai Dao Chang Suo.
I've attached a test ROM for this quirk, which repeatedly turns the LCD on for one frame before turning it off.
Lots of references to "gameboy.h" should be changed to clarify new naming.
Implement peripherals that may be connected to the Game Boy serial port. Then allow the front-end to select which peripheral is connected.
Example below:
enum serial_peripherals {
SERIAL_NOT_CONNECTED, /* Emulate no serial device connected (Default). */
SERIAL_PRINTER, /* Emulate printer connected. */
SERIAL_EXTERNAL /* Send serial output to front-end (eg. for multiplayer). */
};
/* Must be set before switching to printer peripheral. */
int gb_serial_printer_init((void printer_output)(gb_t *gb, uint8_t *buffer));
/* To set the connected peripheral. Returns error if peripheral not initialised. */
int gb_serial_set_peripheral(enum serial_peripherals);
Line 76 in 1834938
Also old APU defines.
I added CGB support here:
https://github.com/froggestspirit/Peanut-GB
I only really tested this with Pokemon Crystal, but seemed to work fine. I wasn't sure if you might want to merge it, or copy parts of it over, I know there's some formatting differences, and also the way I handled sram.
I should also note, this might only be working in the SDL2 example for now, since some changes to the C file were necessary there for rendering the palettes.
This must be rephrased as it doesn't make much sense: https://github.com/deltabeard/peanut-gb/blob/723358274003442374f211930390a785351b3e2b/examples/sdl2/peanut_sdl.c#L576
Some checks, like the following NULL check, will either always be true or false depending on if the user application is implemented correctly. When using peanut-gb with LCD enabled, this NULL check should never be true, so it's a bit of a waste.
Line 1113 in dad7977
The least we can do is use an unlikely(x)
macro here, or we could wrap it in an assert.
Peanut-GB should be profiled, and unlikely/likely macros should be used throughout where there is likely to be significant performance gains.
Create a gb_init_input() function that allows the front-end to configure how to update the button presses.
I think this will be better for making animations and videos than dumping every single frame in an uncompressed bitmap.
Requirements:
Optional:
Hi,
after quite some debugging I found out that Tetris won't work as long as the serial support is enabled. If you remove the serial interrupt and transfer code, Tetris will fully work again.
However, I haven't figured out the exact cause.
On some compilers lcd_mode enum is assumed int
(I dont know why?) and not unsigned int
. Therefore the bitfield should be large enough to encompass the sign bit
Lines 376 to 382 in 1ad02b5
lcd_mode : 2
should be lcd_mode : 3
?
Currently I get warnings like this
warning: implicit truncation from 'int' to bit-field changes value from 3 to -1 [-Wbitfield-constant-conversion] gb->lcd_mode = LCD_TRANSFER;
and the screen is not rendered
If the front-end does not support serial, the serial transmit function currently required to be implemented in the front-end must still return 0xFF (which is what the Game Boy sees when no device is connected to the serial port).
If serial connection is supported by the front end, then a gb_serial_init() function may be called to imply support. Otherwise, an internal function to peanut-gb may emulate no device connected by default.
Peanut-GB/examples/sdl2/Makefile
Line 31 in 3586dd9
Add a feature in the SDL2 example to only refresh the lines of the screen that have changed since the previous frame. This can be used as a reference for a performance improvement on platforms where communicating with an LCD is time costly.
A hashing algorithm that is fast for 160 bytes of data should be selected.
In this case, multiple hash algorithms could be added, and one could be selected for use at runtime. Some platforms, like the RP2040, have integrated CRC32 hashing. I think it will be interesting to see how well something like CRC32 will do against a stronger hash algorithm like wyhash or xxHash.
When external clock is selected, the SB register is not modified when no external peripheral or console is attached.
A new function should be written to allow the frontend to turn various settings on and off. These may include:
sha1sum: 397ad2ff25627b83e02c71b54c72bb4deb39e0c0
Line 485 in 1834938
If bit 7 of a sprite's attributes ($FE03, $FE07, $FE0B, ..., $FE9F) is true, the sprite's pixels are supposed to be drawn behind any background pixels that are not color 0. The comparison with color 0 is supposed to happen before BGP is applied.
To reproduce:
Expect: Libbet smoothly falls into the hole
Actual: A black square appears on the white cell below Libbet, which would otherwise have been hidden behind the background.
Use SDL_GetRendererInfo()
to find out what the native texture format is for the used driver, then convert the RGB555 values to whatever format is detected.
Using a texture format that is native to the system should improve performance on systems that do not natively support RGB555.
The current implementation expects an immediate response from the frontend when a byte is transmitted over a serial connection. This will pause emulation until the frontend returns from the gb_serial_transfer() function.
Therefore, I propose a system when the emulator calls a gb_serial_tx() frontend function which transmits a byte, and then calls a gb_serial_rx() when the emulator expects a byte to be received on the serial connection.
Since the serial link on a DMG has a frequency of 8192Hz, the frontend function will have just under 1ms to transmit and receive a byte. A microcontroller with an SPI peripheral will be able to handle the TX/RX whilst the emulator is still running.
I'm still unsure whether doing this is a good idea. As having a single frontend function for this reduces complexity. However, I don't think having the emulator pause whilst the frontend sends and receives data is good.
I've been messing with this on a microcontroller, and was getting maybe around 80% speed (going by the crude audio I added). When the program hits a halt, the code has it run a bunch of filler nops until it hits the next interrupt. I've been testing a few things to skip past this, currently I changed the gb->counters to count down, so when it hits a nop, it can take the lowest counter and set the inst_cycles to the next multiple of 4 (4 is the minimum so it doesn't hang on the LCD timer being 0). This gives a small speedup.
Extra tasks:
* This may be useful for reducing load on embedded systems. For example, if LCD drawing is taking too long due to full screen refresh, the sampling rate of sound may be reduced for 100ms.
It would be great if floating point values wouldn't have to be used, as some embedded hardware do not have a floating point unit (FPU).
Samples should be in unsigned 16 bit format, as this is best supported on embedded hardware. On systems that use 12 bit registers for DAC or PWM (PWM may be used in replacement of a DAC if one isn't available), it will be easy to bit shift the 16 bit sample. I don't see why floating point samples would be any good here.
Obscure behaviour that is detrimental to performance may not be considered.
Hi,
I've ported this emulator to a ESP32 based embedded system.
While the main functions work fine, I'm having some trouble with keypad inputs.
Super Mario Land: input works fine
Tetris: no input possible
this is where I got my roms: [Edit: Removed]
Line 2 in f501adc
Or remove fast forward mode.
Should describe what functions are expected to be called by the frontend.
In some places, the code exceeds the 80-char limit massively. Some inconsistencies exist too.
I would like to use a code formatter like clang-format to automatically format my code. The amount of code modified should be kept minimal, so clang-format should be configured to use this current code style as much as possible. I don't want a massive thousand-line change commit for code style changes.
Things like case statements shouldn't be indented as they are currently, in order to increase the amount of space on a line.
SDL2 provides an allocated buffer with its length to the audio callback function. Therefore, peanut_apu does not require any internal buffer. The buffer and the size of which should be passed to the functions called by audio_update(), where the SDL2 buffer replaces "samples" and the length of the buffer replaces "nsamples".
I hope to remove all memory allocation functions and things like memmove() too.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.