this is really cool!
it totally seems to be working!
Games like smb1 and castlevania work very nicely.
smb2 even (sorta) works!
i'm really excited!
The approach of sniffing the scroll registers is _okay_, but i'm
a bit worried that it will not work properly with games that that
scroll _without_ using the scroll registers.
That said, i'm not going to think about that for now, and cross
that bridge later...
There are some hiccups that need to be ironed out in the current
implementation:
1) sampling the edge of the screen for the background isn't ideal.
- There tend to be artifacts (e.g: smb3) on the edges, so the bg
gets messed up. It might make more sense to sample ~8px into
the framebuffer.
- i'll need to rewrite my sampling implementation from using
SDL clipping, to manually doing manipulations on the
framebuffer
2) inspecting the scroll registers works great, except around 0.
- you'd think that the only times the scroll registers are
written to are when the scroll changes, right? That's not the
case though... many games (eg: smb1) will write the scoll value,
and then later write a couple of 0s. why? good question!
- two problems arise because of this:
a) it's tricky to filter out the dummy 0s from the true 0s
- i'm using a simple heuristic for now, and while it works,
it certaintly isn't robust...
b) games tend to update the scroll register by more than 1
unit at a time
- this is bad since it results in black-stripes of missing
background whenever this occurs.
3) still need to fix going backwards lol
Lastly, here are some things i'd like to do after I have a
somewhat robust system in place:
- Only record the Background layer, avoiding sprites entirely.
- this should get rid of some nasty artifacting when sprites
shuffle off-screen
- Detect when a scene-change occurs, and start a new "plane"
- eg: going through a door in SMB2 should discard the old tiles
and record a fresh set.
- I might be able to detect this in a few ways:
a) checking for CHR memory changes
b) checking for palette changes
c) finding some sort of "average" for a scene (e.g: histogram
of pixel colors throughout the screen)
- this one should work the best I think
- Save tilemaps for future playthroughs
- shouldn't be too hard...
- the tricky bit is saving the tiles w.r.t scenes...
i'm really excited to see how good wideNES turns out, given the
really cool initial results.
I had a thought: wouldn't it be cool to "widescreenify" NES games?
There are a bunch of different ways to approach this, but i'm
experimenting with a fairly general approach: watching the PPU's
scroll registers, and shifting the screen around accordingly...
This is just an initial test, but SMB is already showing some cool
results!
As far as I can tell, i'm the first to experiment with this idea,
which is exciting, because there isn't terribly much new ground to
break in the world of NES emulators!
I'd like to think this commit marks the end of the first phase
of ANESE's development. It's been a long road, but ANESE is
finally at a point where I'm mostly happy with it.
ANESE sure as hell isn't perfect, but hey, it's pretty good!
The core code is pretty clean, the UI code is... acceptable, but
most importantly, ANESE actually plays my favorite NES games!
There is still some work to be done before I'd be comfortable
giving ANESE a v1.0.0 release, but what I have here is still
pretty great.
Let's call this ANESE v0.9.0 :)
-------
So, what next?
Well, contrary to what I said in some earlier commits, I think i'll
continue to work on ANESE a little bit more!
Specifically, i'd like to rewrite the 6502 emulation.
My current implementation is... okay.
It's instruction-level cycle accurate, but I don't think that's
good enough. It really should be sub-instruction level accurate.
Odds are the added accuracy will fix _a lot_ of bugs.
Aside from accuracy though, I have another reason to do a rewrite...
As a Waterloo student, I have to do a Work Term Report on some
technical project i've worked on recently. ANESE is one such project.
Since the report isn't designed to be very long, I'd limit my scope
to just a small aspect of ANESE: the 6502 emulator.
Yes, I could just write the report on how I arrived at my current
implementation, but I think it would be cool to attempt a cleaner
rewrite, and compare and contrast the two versions.
So yeah, stay tuned! I might also post the writeup (once I get
around to it)
I'm pretty happy with how SDL_GUI cleaned up. It's now pretty thin,
with just a bit of nontrivial, ANESE related behavior, namely:
1) Loading a rom passed as an arg
2) The main run loop is not fully generic (queries modules)
Neither of those are that bad though, so i'm calling it for now.
I think that after this, i'll just have 2 more tasks left until I
tag this as ANESE release 1.0.
1) Comment the codebase some more.
It's pretty good as it stands, but I want to give it a once-over.
2) See if I can't just figure out how to use SDL_QueueAudio
Yes, Blargg's Sound_Queue works _fine_, but I'd feel better if I
could say that _all_ of ANESE's sound code is hand-written.
The windows build broke because of a stray #include <Windows.h>
defining some macros that conflict with my enums... I hotfixed it.
The Linux bug is more interesting, and has to do with a compileg
bug present in g++4. It's fixed on later versions though...
I think it's coming along pretty well. In my second pass, I just
need to push more behavior into the modules (that is currently
scattered around the gui.h/cc)
no more janky DEBUG_VARS! Now, there is a defined NES_Params object
that is passed (by const ref) to the NES on construction, which is
passed along to all components that need it, with each component
holding a const ref to the individual fields it cares about.
Plus, I could finally get rid of debug.h/cc, which has outlived
it's usefulness.
What the hell was I thinking, spreading each GUI subsystem across
a bunch of files?
Now, each subsysyem (the NES and the Menu) are organized properly,
with a single file containing their respective init, input
handling, update, output, and clenaup routines.
It's still kinda jank, but it's a lot easier to get things done.
cross-platform support man. it's tricky.
I didn't want to include more libraries, but I really didn't want to
deal with cross-platform pathfinding, or coming up with a custom
config format.
I still had to slap together a janky solution for getting a folder's
absolute path across platforms, but it seems to get the job done...
I fixed up the dmc some more, since I realized it sounded like
death (especially in Kirby's Adventure)
I also refactored and cleaned-up much of the APU. Lookin' good :)
Well, here we are. I implemented my own APU.
It's not great, far from it. Hell, I doubt interupts even remotely
work. I sure as hell won't be passing any APU tests...
But hey, it works in most games!
---
This was my last major task with ANESE.
While there are a _lot_ of things that I could still improve, I
think it might be time for me to ramp down, and start looking at
some other projects to work on. ANESE has taken up a huge chunk of
my time lately...
In the near future, I might push a few more commits to ANESE,
cleaning up some code here, adding some more comments there. Maybe
i'll actually improve the UI a bit.
right, I did a lot here:
1) implemented the DMC. It was a bit tricky, but not as bad as I
had initially thought. I should implement CPU stall better, but w/e
2) I figured out why my pulses sounded like shit! It's because I
accidentally used sweep.val instead of sweep_val, i.e: assigning to
the union on every sweep clock! I fixed that, adn renamed sweep.val
to sweep._val so I don't make that mistake in the future.
3) Why even use `short` samples? It's 2018, we all have FPUs!
---
I also experimented with switching to SDL_QueueAudio instead of
the current Sounds_Queue.h thing I from Blargg, but there seems to
be a gradual desync issue when I use it...
Also, I found that bumping the sample-rate up greatly improved the
audio-quality, espeically on pulses. iirc, it's because I am doing
sampling in a shitty way, when I should be sampling more around key
inflection points.
See http://www.slack.net/~ant/bl-synth/ for what I mean...
(that's how Blargg's nes_snd_emu does it, and it sounds awesome)
as someone who hasn't taken a signals processing course, figuring
out how to get these things working was a real pain.
Luckilly, Wikipedia is aweomse, and straight-up provides an
algorithm for both lopass and hipass filters. All it needed was a
bit of modification, and bam, easy as that!
----
As a sidenote, as i've been developing the APU, i've compared it to
some of the other APUs out there (including Blarrg's), and it
definately sounds shittier.
Maybe it's because i don't have DMC implemented yet? Unlikely...
Odds are i'll have to look into actual waveform synthesis if I want
it to sound better, which will be fun-on-the-bun.
I had the length-counter conditions messed up, which made things
sound pretty bad. After correcting that, the audio started to sound
a lot better.
Pulse1 and Pulse2 were relatively straightforward to implement,
although it's a bit tricky understanding the wording on the wiki.
Just Triangle and DMC to go!
(oh, and I should probably implement those high-pass and low-pass
filters too)
Would you look at that! It actually wasn't _that_ bad once I read
through the docs some more. Turns out that I don't _really_ need to
deal with complicated waveform sysnthesis, since the nesdev wiki
gives a handy-dandy lookup table for what the NES should output
given the volumes of all the channels!
I'd imagine getting pulse1, pulse2, and triangle working shouldn't
be that hard. DMC might be trickier though...
Oh, and I still need to hammer on APU IRQ timings.
I have no idea what i'm doing lol.
I'm starting with the noise channel, since I think that might be the
easiest to do?
I _think_ I implemented it properly, since sniffing the output gives
reasonable volumes, but I don't really understand how to generate
sound waves from it yet...
I pushed a lot of common behavior up to the Mapper base class,
the most substantial of which is the bank-chunking logic.
Doing so did lead me to yak-shave a bit, since I needed to add
proper inheritence support to the Serialization library. It's not
_completely_ transparent, but it's pretty good. Essentially, since
C++ doesn't have a built-in "super" keyword to access the parent
class, you have to manually specify the parent class.
I didn't implement multiple-inheritence support though, since that
isn't relevant to me right now.
That said, the serialization library is really nifty, so (like I
mentioned earlier) i'd like to spin it off into it's own repo.
I also took this chance to add power_cycle() and reset() methods to
the mapper interface, since I realized that some games would die
on reset due to registers not being cleared properly.
So yeah, feels gud man.
it's not working though, and i'm not entirely sure why...
I doubt i'll spend more time looking into this right now though,
since it's a relatively minor thing. I was hoping it would
Just Work (TM), but alas, it looks to be a bit more involved...
Welp, it was worth a shot.
(oh, and if you can't tell, i'm just procrastinating starting to
reimplement the APU)
tinyfiledialogs was a good band-aid, but it was always kind-of a
janky solution. Mind-you, this file-browser I threw together ain't
the nicest either, but at least it's within the main SDL window.
Now that the menu is in place, it's possible to switch between
games without restarting! Also, opening the file-picker pauses
ANESE, so that's good to finally have.
Thanks a bunch to cute_headers for a neat little cross-platform
directory traversal library, and also SDL_inprint for a quick-and-
dirty embedded font for use in the menu. Not having to bring-in
SDL_ttf is a huge load off my shoulders.
Oh, and i've noticed that `gui.cc` has started to get bloated, so
now I need to get around to cleaning it up weeeeee.
It wasn't that hard to do per-se, it's more about me finally
getting around to doing it. main.cc is hella clean, just making a
new SDL_GUI instance, and letting it RAII all of the SDL stuff.
Also, now that the new project directory structure is in place, it
shouldn't be that hard to add a LibRetro core! Fun!
It's not the prettiest code, and it sure as hell isn't how the
hardware does it, but by lord it does what I need it to do!
Yep, I pretty much copied the existing sprite fetching code from
one part of my PPU, and slapped it in spr_fetch(), with
cycle-accurate timings.
This is awesome, since I can remove the ugly "passing the PPU to
the mapper" shenanigans I wrote to get it working. I can actaully
use the correct MMC3 timing behavior, i.e: watching PPU A12 for
a rising edge.
Plus, if I switch to a subcycle-accurate CPU emulator, it _should_
automagically fix flickering in Kirby's Dreamland!
Oh, i'm almost forgetting the most important bit: Punch Out!!
It works now!!
That was the last game I really cared about getting up and running,
which means that I'm not going to be focussing on mappers for the
next little while, shifting gears instead towards accuracy stuff.
Basically, there are 2 things that I really want to get around to:
1) subinstruction cycle-accuracy - i.e: rewrite the CPU
2) write my own APU implementation
Of these two, I think i'll tackle 2) first, because if I don't do
it, i'll never be fully satisfied with myself. How can I say I
wrote a NES emulator if I haven't even written a major chunk of it!
So yeah, good stuff!
It's kinda jank, but hey, it works?
The serious issue is that it relies on the SDL2 dylib installed
from homebrew, and if that verion ever gets bumped, RIP.
hey, look at that. I have CI now :D
One of these days i'll look into how to package up macOS builds,
and update my cmake scripts to prevent a console from popping up
on windows.
One major thing I changed is that `nes.cc` no longer calls `new` to
instantiate components, and instead simply contains them. This is
nice for 2 reasons: 1) it is more faithful the the hardware
metaphor i'm going for, and 2) it lets me get a good idea of ANESE
Core's memory footprint, which is neat.
I also finally cleaned up the PPU's nametable mirroring, and while
it looks a bit uglier, at actual operations should be more readable
for those who know what to look for.
fucking windows REEEEEEEEEEEEEEEEEEEEEEEE
fopen() silently converts line-endings on windows, and since I did
not explicitly say I was wiring binary data, it solently bloated my
sav size.
Literally fixed by adding a single char: "w" -> "wb"
Also cleaned up some things that were causing compiler warnings.
Nothing mission-critical, but its nice to keep things clean.
MMC2 is fancy since it snoops reads from the PPU to do bank-
switching on-the-fly, with 0 CPU intervention.
Unfortuantely, this means that for it to work properly, I need to
make my PPU fetching code accurate.
So yeah, if I ever fix the PPU, it should make Punch Out!! work :)
Save-states now also save the APU (which wasn't actually that hard
to do lol), and a better overview of Mapper state. I pushed the
`update_banks()` function up to the base mapper implementation,
and made it overridable, as by doing so I can override the
deserialize function on the mapper-level to call `update_banks`
after deserialization.
After hammering out the initial implementation, I took a look-back
and realized there was a lot that I could have done better. So,
with a fresh-mind, I cleaned up how serialization works under the
hood a bunch. It's a lot cleaner now, and AFAIK, it doesn't rely
on [that much] undefined behavior.
There are a few more things to iron out with it before i'm totally
happy with how it works, but i'm pretty proud of it right now :)
Guess what? There is a known issue with MSVC regarding aggregate
initialization! Yep, that's right, my standards-compliant code
didn't bloody work on MSVC because of a COMPILER ERROR.
God damn.
Well, I fixed that, and now save-states work.
Unfortuantely, it seems to save/load battery-backed RAM improperly,
which is something i'll need to dig into.
Fuck windows.
This serialization framework sure was something!
It is by far the jankiest, ugliest, nastiest C++ i've written,
using a bunch of dirty pointer-hackery and casting to make it work
as "seamlessly" as it does, but god-damn if the end result isn't
marvelous.
Serializing arbitary C++ classes is hella simple now! Just extend
it as "Serializable" and slap-on the appropriate field definitions.
Now, there are a couple rough edges...
First of all, I don't handle Serialization inheritence that well.
Well... at all.
Only data serialized by the last-class in an inheritence chain gets
serialized, which works well-enough for Mapper extending
Serializable (since it doesn't actually have any data to save at
the base level), but things might break down the line.
Since this serialization library is pretty neat on its own, I
think I might spin-it-off into it's own project :)
Try and run Zelda, it should generate a .sav once ANESE closes, and
it should automatically read it once ANESE is reloaded with the same
file :)
So yeah, serialization.
It's an important part of any emulator.
It makes saving unsavable games possible.
It opens the door to a whole mess of neat stuff (rewind OwO)
The question becomes, what's a good way of doing it?
While there are probably much better solutions out there, I want my
solution to be fairly simple and lightweight.
The gist of the system is that anything that can be serialized will
extend the Serializable class. Though some clever use of
inheritance and macros, serializing the state of an object will be
as straigtforward as defining which fields should be serialized
(in the constructor, using some macros), and then calling
serialize() and deserialize(data) appropriately :D
I'm not planning on doing anything too fancy, basically just some
structured memcpy's, but it aughta work okay. Hell, i'm not even
labelling chunks, so if I ever change the order in which something
serializes it's members, old saves won't work anymore haha.
At least it's portable... ish? I've been using sizeof with fixed-
width types, so saves _should_ transfer across machines.
(assuming they have the same endianess)
Oh, and I still haven't figured out how i'm going to handle
serializing data-structures that have serializable members, (which
is kind-of important when serializing the NES / some of the fancier
mappers)
Off the top of my head, it might make sense to adopt some sort of
tree-like approach... but idk yet.