zeta0134 / luagb Goto Github PK
View Code? Open in Web Editor NEWA gameboy emulator written in pure Lua. Work in progress.
License: BSD 3-Clause "New" or "Revised" License
A gameboy emulator written in pure Lua. Work in progress.
License: BSD 3-Clause "New" or "Revised" License
Using Love2D is nice and all, but I want to see this emulator being put to the test. CC/OC provide one of the harshest (IMO) Lua environments there is, with text graphics, suckass threading and more interpretation.
I don't know how would it be possible to port to ComputerCraft, but porting to OpenComputers would take less effort due to its Unicode support. And that means we can use Braille characters for bitmap graphics (the font draws them as pixels)+having 2 colours per cell/character that would resemble something like Commodore 64's colour limitations.
As for sound, there would be no sound. Unless we use Computronics' sound card that has primitive shapes but lacks DPCM wave capabilities.
If Lunatic86 (pure Lua x86 emulator) did it, you can do it too.
I looked at the APU code today, intending to track down an inconsistency between the percussion tracks on real hardware and the output of LuaGB. I guess I was remembering the modular approach that RusticNes uses which is lovely, and closely matches the behavior of real hardware. This should have been trivial to find and fix. Alas, my thought process was:
The basic structural problem is that, at the time, I was still thinking of waveforms in terms of notes and their corresponding frequency, and real hardware doesn't work that way. The gameboy's APU is actually very simple: the program code sets a clock timer, this is decremented at some fixed rate (512 KHz?) and when the timer reaches 0, it gets reset and some work is performed. Pulse waveforms are shifted, waveform counters increment, and noise is LFSR'd. There are slower 128 Hz and 64 Hz timers that manage volume and envelope sweeps, but the principal idea remains the same.
LuaGB presently uses some unholy mix of clock cycle arithmetic and an awkward timing catchup... thing on register writes. This somewhat closely approximates hardware behavior, but this falls short of accuracy by a few yards. It's especially noticeable in the noise channel, and reviewing the code today, I'm not sure the volume envelopes actually work in any capacity, especially at lower periods. Since so few games rely on the hardware envelopes (most re-set volume every frame) I might just never have noticed.
This issue is here to mark my shame. Please, future me, rewrite this mess. For our honor.
Games can turn off the LCD entirely, which blanks the screen to white and turns off any graphics related timings and interrupts until it is re-enabled. The Love2D shell presently relies on vBlank timing to determine how long to run the emulator for inbetween each host frame, and targets 60 Hz for the host timing. This mostly works, but fails when games disable the LCD, causing a single host frame to run for many hardware frames and desync the timing. This can cause considerable problems with audio sync depending on how long the game leaves the display powered off.
This is particularly noticeable in Pokemon Pinball, which blanks the display in this manner every time the ball moves from the top of the table to the bottom, or vice versa. Many retail Gameboy Color games do this during screen transitions to load new graphics more quickly, so it's a widespread bug.
The failing tests involve some conditions that should disable the channel that are unrelated to length counters expiring. (Go figure.) The relevant test code blargg is using is here:
set_test 13,"Disabling DAC should disable channel immediately"
call begin
delay_apu 2 ; clocks length to 2
call should_be_on
wchn 0,$00 ; if wave channel, this disables DAC
wchn 2,$07 ; if square/noise channel, this disables DAC
call should_be_off
set_test 14,"Disabled DAC should prevent enable at trigger"
call begin
wchn 0,$00 ; if wave channel, this disables DAC
wchn 2,$07 ; if square/noise channel, this disables DAC
wchn 4,$80 ; triggers channel but doesn't enable it
call should_be_off
set_test 15,"Enabling DAC shouldn't re-enable channel"
wchn 0,$80 ; if wave channel, this enables DAC
wchn 2,$10 ; if square/noise channel, this enables DAC
call begin
delay_apu 2
call should_be_on
wchn 0,$00 ; if wave channel, this disables DAC
wchn 2,$00 ; if square/noise channel, this disables DAC
call should_be_off
wchn 0,$80 ; if wave channel, this enables DAC
wchn 2,$10 ; if square/noise channel, this enables DAC
call should_be_off
I knew about the wave channel's DAC, so that's an easy fix. I did not know that volume = 0 on the square/noise channels had any effect; that's interesting!
The default DMG palettes give us a way to mimic the washed out green of the original gameboy, but Gameboy Color games are much less accurate. LuaGB's colors would charitably be described as "VGA Poppy Bright" and while this can look cheery and colorful for some games, it doesn't at all match what a real Gameboy Color would do.
It should be straightforward to run GBC colors through a conversion when the game writes to the palette registers, which should keep things performant. One of the better color correction strategies can be found in libretro, the result of Pokefan531's research: https://github.com/libretro/gambatte-libretro/blob/5ee8b1e9da0835be8e526e9e4ff73346eef20fd1/libgambatte/src/video_libretro.cpp#L195
I'd like to implement this as a user-configurable option. It might also be neat to research other color correction strategies, since the various Gameboy Advance models have different characteristics and those can (mostly) play GBC games.
The first thing anyone who's ever used a Gameboy emulator before notices when running LuaGB is that my default selections for the A and B buttons are reversed from the defaults of pretty much any other program. I don't intend to change those defaults, but it would be very nice to support remapping keys, so that users can adjust their preferences to taste.
Because this is a core feature, I'd like the user interface to be restricted to the capabilities of a DMG gameboy; ie, 160x144 and 4 shades of grey, similar to the file browser. A user shouldn't need to enter debug mode to change these settings.
With certain games, the debug tile window renders all white. Other debug areas (backgrounds, sprites, etc) and the game screen aren't affected:
As far as I can tell in my testing, affected games show no debug tiles throughout the entirety of their gameplay, regardless of which backgrounds are enabled or what the background palettes get set to.
The MMU for the core of this project currently uses a lua array lookup of bytes, and because of this, it would be super easy to add ffi support (if available - we can always fallback to default lua if it's not available)
local ffi = require "ffi"
if (ffi) then
function loadarray(narr)
return ffi.new("uint8_t[?]", narr
end
else -- no ffi
function loadarray(narr)
local t = {}
for i = 0, narr - 1 do
t[i] = 0
end
end
end
This would have no downsides and is super easy to implement! Was wondering what you thought of these kinds of optimizations? It'd work even if FFI wasn't available and it'd work better if it was.
The Oracle games have been excellent test cases for the new features of the Gameboy Color. Their intros in particular make heavy use of scanline updates and palette swap trickery, and getting them to render correctly requires the emulator to be quite accurate with regards to its timing and graphics modes.
Something is still not quite right though. The game is supposed to be triggering an hblank interrupt to switch to the lower set of black bars here, and that never happens, leading to a glitched result:
I suspect there's something inaccurate with either the timing of LuaGB's graphics modes, or the way it handles the LCD STAT interrupt, which is probably what the game is using to trigger an interrupt on each scanline where it needs to make a change.
I haven't tested this in any other games yet, but it probably will happen in all of the pokemon games.
Occasionally, some background tiles are flipped incorrectly when appearing onscreen, even though the debug view reading the same attribute table displays things correctly. A couple of examples, there may be more:
Curious observation: These tiles so far appear exclusively on the bottom row of the game screen, but are not always on the bottom row of their respective tilemaps. Might just be a coincidence between the test examples I've produced so far.
I suspect something's getting desynced between the VRAM writes the game is making and the display cache that the graphics module maintains. It feels like an off by one error, or some issue with a bitmask, but I have no idea where it is.
As suggested by @lukego in #13
For LuaJIT enabled platforms, it makes sense to trigger a cache flush when starting a new game, as each game will probably have varying performance characteristics. This should also help performance profiling be a little more consistent, as the speed in theory shouldn't be affected as much by past emulator state.
The currently failing test has to do with the current position of the Frame Sequencer, which is presently ignored on trigger. A hardware quirk causes extra length clocking in some cases, depending on how far along the FrameSequencer is at the time of trigger, probably to help improve the overall accuracy of the length counter.
filebrowser.lua:195: attempt to concatenate local 'cursor_item' (a nil value)
Traceback:
filebrowser.lua:195: in function 'select_at_cursor'
filebrowser.lua:220: in function 'keyreleased'
main.lua:391: in function <main.lua:383:>
[C]: in function 'xpcall'
This applies to the Windows version.
Hi. Thank you for making this. This is amazing.
I am currently making a hack of pokered in Lua, and am using an edit of mGBA with it. I really want to integrate this project into my code as another emulator backend but there are a few things stopping me right now:
The reason why I need C data structures is because I use pointer arithmetic to lookup values in ROM from banks and symbol tables and stuff like that.
I understand the want for it to be in PUC Lua but I think for development's (and speed's) sake it would be better to use ffi datastructures?
An option to speed up the execution would be to parse the Z80 machine code and turn it into lua code (horrible lua code but this doesn't matter), you could later on run this code directly with Lua.
This would allow for some transpilation optimizations and whatnot. This is not an easy job but could give better results in the far end. Maybe it would grant another project on itself.
I'm leaving this here so if someone is interested it may spark some discussion and ideas. It's not my intention to demand this feature, due to it's complexity.
Other than the disassembly panel and watching the processor registers directly, there's not really a good way to explore main memory. This is a glaring omission compared to other emulators, and should be pretty easy to implement, at least from the CPU's point of view.
It's an awesome project! I think it is possible to port to a embedded system! thank you
Dude, I hope you read this, because I would like to say, don't stop working on this. Lua is a the most USABLE code for making Homebrew applications. From what I see from your profile, you seem to be interested in Homebrew, I am attempting to develop some stuff as well. I have created one sucessful program I am quite proud of, this program could set a base for a TON of amazing new emulators. I don't think a emulator has been made in LUA yet, and something called Lua Player Plus will allow you to port this to a 3DS! This could be a base for a lot of new creations. I hope you make progress soon, because I would love to test this out.
Thanks ~GibFTW
Opening a ROM from the two file locations LÖVE has access to is well and good, but it'd be nice if you also supported drag-and-drop onto the window with love.filedropped
!
When viewing tile and map data in Gameboy Color games, the map display works correctly, but tile display is broken. I suspect the tile viewer is still relying on the DMG palette registers, which some games set to all white / do not initialize properly.
I can turn this into a feature request by implementing color palette selection, allowing all of tile memory to be viewed with one of the game-supplied palettes applied. A more reasonable default of course would be to grey-shade everything when no palette is selected.
The audio panel is visually interesting, but it's kinda barebones. Right now it just shows the output waveform of all four channels independently, and the final output. This is useful, but it doesn't tell the whole story. It would be very handy if it could also visualize things like:
It could be nice to be able to keep the menu buttons in game to be able to play on a tablet or a smartphone.
The project is amazing btw !
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.