Compare commits

...

50 Commits
v073 ... v076

Author SHA1 Message Date
Tim Allen
64072325c4 Update to v076 release.
byuu says:

Most notable in this release is that sound support has been added to my
own Super Game Boy emulation. The GUI toolkit, phoenix, has also
received a complete rewrite; with the most visible change there being
that windows are now resizable.

Changelog (since v075):
* added sound emulation to Game Boy core
* fixed Super Game Boy save state
* support added HexEdit widget to Windows and Qt targets; debugger can
  now be compiled on all platforms
* entering fullscreen now auto-hides mouse; and mouse capture is toggled
  otherwise by F12 key
* fullscreen command and geometry caching works much better on GTK+ and
  Qt targets
* phoenix rewritten from scratch; now supports resizable layout
  containers
* phoenix/Windows no longer relies on buggy SetParent API to reparent
  widgets
2011-02-27 20:11:01 +11:00
Tim Allen
017f9926fc Update to v075r16 release.
byuu says:

This has my latest API enhancements, but there are some known issues:
- resize on Windows seems to not repaint the buttons properly in rare
  cases. I may just need to revert to flickering resize.
- GTK+ reports the wrong menu height, off by two pixels, prior to the
  window being realized (made visible)
  - this results in the main window moving up two pixels after each run
    of bsnes

The menu height bug was actually there previously, it was just that Qt
and GTK+ were computing the frame margins incorrectly (ignoring the menu
bar) before.

On the bright side, ui/settings/input.cpp has been improved by way of
the new multi-layout support. The window is no longer forced to an
awkward 640 pixels wide, as the mouse axes/buttons can overlap now. The
code is also simpler since I am using the Layout::setVisible command to
toggle groups on and off instead of doing it for each and every control.
2011-02-27 20:05:10 +11:00
Tim Allen
c31543ea58 Update to v075r15 release.
byuu says:

phoenix/GTK+ rewrite completed. All three targets should now be 100%
operational with full resize support.
2011-02-24 20:27:21 +11:00
Tim Allen
7c3aaf12b0 Update to v075r14 release.
byuu says:

Adds the new phoenix/Windows. Testing would once again be appreciated,
as this is basically a rewrite of the entire core of the GUI.
2011-02-24 20:25:20 +11:00
Tim Allen
3fad0a0105 Update to v075r13 release.
byuu says (in the thread about rewriting Phoenix):

- added phoenix/reference; a dummy implementation that contains the 22KB
  of static boilerplate code needed to start a new target
- OS::setDefaultFont removed; problem is that objects in the global
  scope are constructed before you can call that function
- added Window::setWidgetFont, which is applied if your widgets have no
  custom font set already upon attaching them to the window with
  Window::setLayout, more understandable behavior
- renamed ListBox to ListView

byuu says (in the v075 WIP thread):

Found the source of lag on cartridge load. ListView::modify() was not
locking Qt messages, so it was causing a CheatEditor::refresh (copies
16MB of memory each call) for all 128 cheat items.

Final issue now is that nested submenus (menus inside of menus) are not
applying Window::setMenuFont yet.
2011-02-16 23:35:40 +11:00
Tim Allen
72a2967eeb Update to v075r12 release.
byuu says:

phoenix has been completely rewritten from scratch, and bsnes/ui + bsnes/ui-gameboy have been updated to use the new API. Debugger works too. Currently, only phoenix/Qt is completed, and there are two known issues:

1: font sizes of menu items are wrong, I can fix this easily enough
2: there's some sort of multi-second lag when loading games, not sure
   what's happening there yet

The new phoenix isn't exactly complete yet, still making some key
changes, and then I'll start on phoenix/Windows and phoenix/GTK+.

The most noticeable difference is that you don't have to give all of the
header paths and PHOENIX_PLATFORM defines when compiling individual GUI
object files. It's only needed for phoenix.cpp itself. The overall
structure of the phoenix source folder is much saner as well for
sync.sh.

I'm really surprised things are working as well as they are for
a two-day power rewrite of an entire phoenix target. The other targets
won't be as bad insofar as the core stuff is completed this time. And
thank god for that, I was about ready to kill myself after writing
dozens of lines like this:

    HorizontalSlider::HorizontalSlider() : state(*new State),
    base_from_member<pHorizontalSlider&>(*new pHorizontalSlider(*this)),
    Widget(base_from_member<pHorizontalSlider&>::value),
    p(base_from_member<pHorizontalSlider&>::value) {}

But each platform does have some new, unique problems. phoenix/GTK+ was
acting screwy prior to the rewrite, and will most likely still have
issues. Even more important, one of the major points of this rewrite was
having the new phoenix/core cache widget settings/data, so that I can
destroy and recreate widgets rather than relying on SetParent. This
means that simple copying of the old phoenix/Windows won't work, and
this new method is significantly more involved.
2011-02-15 23:22:37 +11:00
Tim Allen
a8ee35633c Update to v075r11 release.
byuu says:

Rewrote the way menus are attached, they act like layouts/widgets now.

All three phoenix targets once again work with both radio menu items and
radio widgets. Both GTK+ and Qt have built-in group controls right
inside the widgets, so I don't have to keep my own groups around
anymore. They do act screwy at widget creation though, have to jump
through some hoops to get it to work right. All I can say is, definitely
set all child widgets to the parent before trying to check any of them.

My long-term goal for the main window is to honor the fullscreen video
setting as a generic setting, and let the window scale auto-fit the best
possible size that matches your scale preference into the output window,
centered just like fullscreen. For now, I've just set it to a fixed
window size until I finish working on phoenix. The scale X settings will
just be to snap the window to an exact size in case you don't want any
black borders, they won't be radio items and the bsnes-geometry.cfg file
will save width/height information as well.

Simplified the sizing requirements for creating layouts and updated all
bsnes windows to support the new system. Layouts also expose their
minimum width/height values, which I use to create perfectly sized
windows on all three platforms. This will fix cut-off heights on the
last Windows WIP. Qt is being annoying though and forcing a minimum
window size of 300,100 despite me telling it to use a smaller window
size. Always have to fight with Qt, I swear to god.
2011-02-10 21:08:12 +11:00
Tim Allen
7dda70baa4 Update to v075r10 release.
byuu says:

phoenix/Windows and phoenix/Qt are mostly fully operational now. All
platforms support dynamic layout resizing. I tried WM_GETMINMAXINFO
(thanks, OV2), but it was acting kind of choppy on resize, and it would
get confused and go crazy if you snapped one direction to the minimum
height but not another, so for now I'm leaving it off.

phoenix/GTK+ will be missing some functionality in regards to window
geometry. The other two have a more coherent strategy now: geometry() is
the client area, and setGeometry moves the client area to where you ask
for. This makes truly centering your client area trivial.
frameGeometry() includes the borders, menu and status. There is no
setFrameGeometry(), not sure if I really even want that, but it could be
useful so who knows. All targets also support non-resizable windows.

X11 is of course horrendously poor with frame sizes, Qt and GTK+ don't
even pretend to simulate them, so they say the frame is 0x0 pixels in
size until your widget is fully realized and visible to the end user. So
for now, to get window positioning right, I have to wait until the
window appears and then reposition the window again, causing a slight
jump. My plan is to build some persistent caching support directly into
phoenix. From here, I can just have the window snap the very first time
you run your very first phoenix app. I'll then determine the frame size
information, and use that to create future windows. Once they spawn,
I'll recheck and update the frame size info in case it has changed (eg
user changed themes.) Saving settings into .config/phoenix will allow me
to avoid having to snap the window every time on first startup. If the
config file is missing or unwritable, too bad, happens every time then.

I'm thinking about renaming onResize to onSize, and getting rid of
Window::create(). Rather make it spawn like every other control in its
constructor.
2011-02-07 20:20:07 +11:00
Tim Allen
2c61ce2522 Update to v075r09 release.
byuu says:

Ported phoenix/Windows and phoenix/GTK+ over to the new system. There
are some problems that need to be addressed:

- Windows ComboBox height setting needs widget creation height to
  properly scale itself (make Widget::setGeometry virtual and override
  ComboBox::setGeometry)
- Windows Canvas is completely broken
- GTK+ Canvas is slow as shit compared to Qt Canvas, probably nothing
  I can do about it, have to do a very costly conversion because GTK+ is
  stupid and uses BGR like Nintendo
- GTK+ listboxes are fucking insanely complicated to set up. Currently
  I just split the second-half of creation to the setHeaderText call,
  but when you don't call that, things explode
  - I'm probably going to have to completely destroy and recreate
    listboxes when changing the header text / column count
- Qt resize code is still impossible to get right, it's not letting me
  size a window > 2/3rds of the screen size (it's in their docs)
  - I swear, Qt is the most painful API in the world to move/size
    windows with
- now that Window is separate, it really needs geometry() and
  frameGeometry() as the two are quite different
- I need a way to toggle window resizability for fixed layouts, Qt is
  once again going to be a nightmare as it lacks a way to do this other
  than fixed layouts
- GTK+ currently explodes on bsnes, millions of console messages,
  wonderful
- plenty more I'm forgetting

One bit of really cool/good news though: I made
Fixed/Horizontal/Vertical layouts external to phoenix itself. The code
is included for all targets so that it's always there and compiled into
one object, but the great news is that you can easily write your own
layout widgets and they'll work on all toolkits instantly.

That resize issue with bsnes was so simple it's annoying: my FixedLayout
container was repositioning on geometry updates. Made it only do it once
at creation like it should.

bsnes now has a fancy resize, grow the window and get black borders,
shrink it and the video shrinks with it. I plan to make it fancier with
constraint settings (center, scale, stretch). Basically I want to turn
the fullscreen setting into a general setting that also applies to
windowed scaling. I will probably turn the video scale X sizes into
regular items instead of radio boxes, so you can easily reset to a fixed
size whenever you want. Update bsnes to remember width,height geometry
as well and it should be quite nice.
2011-02-07 20:18:01 +11:00
Tim Allen
266495b475 Update to v075r08 release.
byuu says:

Eleven hours of work. Window is now a base type (inherits from Object,
not Widget), same for Layout. FixedLayout still inherits from Layout.
Added HorizontalLayout and VerticalLayout types, that can append each
other to themselves to create box layouts. Layout margins are supported,
spacing is specified inline (I find this a much better way to fine-grain
spacing than Qt's single setSpacing function), and alignment is handled
strictly via padding widgets (insert a zero-sized label and it will
automatically grow to consume all extra space.)

Overall, my box packing model is slightly less powerful than Qt's, but
it is a good deal simpler and and easier to use in 90% of cases. The one
limitation I hit was with my input settings window, I'm not currently
able to embed two different layouts and toggle one on and the other off
to show only either { mouse x-axis, y-axis } or { mouse left, middle,
right }, so they instead just space out differently and I had to grow
the input window width a bit to compensate.

Resizing works great, pretty cool seeing that this is the first time
I've ever written my own resizer. I had to fight with Qt for several
hours to the point of potentially developing an aneurysm, but I finally
got it to properly handle geometry and sizing stuff. Some weird issue
with the bsnes viewport widget, I tell it to resize and for some reason
it doesn't. Cheap hack, I just make it constantly resize every video
refresh and it eventually takes. Wish I knew what was up with that.

All of bsnes now uses dynamic layouts sans the main window, so you can
resize them however you like.

This is still all Qt-only, I'm afraid. The other two ports are
in-progress.
2011-02-07 20:15:43 +11:00
Tim Allen
133d568f76 Update to v075r07 release.
byuu says:

This has the phoenix changes applied. Instead of widgets attaching
directly to windows, you now attach them to layouts, which can then be
attached to windows. Layouts are widgets themselves, so adding layouts
to layouts is trivial. It also allows for multi-widget show/hide, etc.

Right now there is only FixedLayout, but of course the plan is to
support a BoxLayout, that lets you add HorizontalLayout and
VerticalLayout containers to it, thus enabling auto-resize and simpler
form layout.

So far only phoenix/Qt is 100% moved over. phoenix/GTK+ has about 1/3rd
ported, and phoenix/Windows only has one control ported over as
a proof-of-concept.

On the user side, bsnes, bgameboy, snespurify and curse have been moved
to this new layout system. All of bsnes works great with it, as far as
I can tell. Fullscreen, debugger, etc are good.
2011-02-07 20:14:14 +11:00
Tim Allen
b433838e9f Update to v075r06 release.
byuu says:

Removed the floating-point volume adjustments from the wave channel and
the left/right speaker mixers. Also, against my better judgment I'm
backing out of left/right computation when they are both turned off.
This basically makes non-stereo games run faster, but will make stereo
games appear to run slower. I don't like it when end-users experience
mystery slowdowns.

Anyway, it appears that the audio calculation is really fucking
demanding. Knocks FPS from 800 down to 300. I thought it might be libco,
so I took it out and it only went up to 305fps o.O

There is also some sort of problem with bsnes/Super Game Boy audio. The
latency is really great when you first start, but it seems to drift
apart over time until it is well over 500ms, and then it either pops or
fades back to very low, sub-50ms latency again. The way I handle mixing
is that the coprocessor audio samples go into a resampler to the native
SNES rate, and fed to an output buffer. SNES audio samples go there
untouched. When there is a sample in each, I add them together and
average the result (I still don't understand why we divide by two since
these are signed integers), and output it immediately. It's just-in-time
sampling, so as long as DSP v Coprocessor do not drift very far, it
should have very low latency. And I make the CPU sync DSP and
Coprocessor once per scanline, which is something like 15 samples or so.
2011-02-03 22:17:35 +11:00
Tim Allen
a3abe8ebaa Update to v075r05 release.
byuu says:

Added Game Boy sound emulation, all four channels.
It's really, really, really bad. Plenty of bugs, I don't even know what
the fuck a high-pass filter is so that isn't there. Hermite resampling
from 4MHz down to 44KHz. But it's tolerable.
I don't understand what sweep is for at all, and I'm sure I have that
insane recursive reload behavior wrong.

This is pretty much my own design. I referenced blargg's gb snd emu,
blargg's older gb apu ref, Cydrak's APU core, that lousy gbdev wiki
article, the completely and utterly worthless pandocs, and received
nothing but bad and wrong information that just wasted my time from

But I managed to pull it off. It's also painfully slow, like 250fps on
my machine slow. Countless optimizations are possible.
2011-02-02 21:38:28 +11:00
Tim Allen
f88ef9e9a2 Update to v075r04 release.
byuu says:

Changelog:
- hooked up everything necessary for Game Boy sound emulation ...
- bgameboy and bsnes/SGB input 4MHz frequency, and output 44.1KHz
  frequency (produces soft static for now, to verify it is working)
- rewrote all of gameboy/apu, it now has a 4MHz worker thread, and
  separate classes/folders for each channel+master, and serializes

So it's basically all I can do without actual emulation code or
human-readable documentation/example code.
2011-02-02 21:37:31 +11:00
Tim Allen
a136378a7b Update to v075r03 release.
byuu says:

Changelog:
- added full HexEditor widget to phoenix/Qt (has dynamic scrollbar like
  phoenix/GTK, but does not yet support page up/down scrolling)
- optimized debugger to look great with either phoenix/GTK or phoenix/Qt
- fixed phoenix/Qt fullscreen mode (had to allow resizing of the layout,
  and resize the container)
- fixed phoenix/Qt Window::setBackgroundColor() bug that was making
  statusbar invisible
- entering fullscreen now captures mouse, leaving fullscreen releases it
  - so by default, no cursor in fullscreen mode now
- F12 key was assigned the task of toggling mouse capture,
  Tools->Capture Mouse was removed
- above change allows toggling mouse capture in fullscreen if you like

It wasn't my idea, but toggling the mouse capture in fullscreen also hiding the mouse cursor is what I call genius design. Two birds with one stone, and very intuitive.

Also, the default GUI on Linux for bsnes and bgameboy is now Qt, instead
of GTK+. I did this because Qt's fullscreen is far more stable, and
I fixed up the remaining bugs anyway.
2011-02-02 21:35:15 +11:00
Tim Allen
012cdd4b14 Update to v075r02 release.
byuu says:

Changelog:
- added ui-libsnes directory back into source archive; make archive-all
  includes it now
- added basic HexEditor widget to phoenix/Windows
2011-02-02 21:33:35 +11:00
Tim Allen
eecc085e42 Update to v075r01 release.
byuu says:

Changelog:
- fixed Super Game Boy save state support
- both SNES and GameBoy only initialize serialize size on cartridge load
  once now, just like I've already done with memory mapping
- added nall/public_cast.hpp for fun ... don't worry, I'm never actually
  going to use it in production code :D
2011-01-29 20:48:44 +11:00
Tim Allen
6b4104867f Update to v075 release.
byuu says:

This release brings improved Super Game Boy emulation, the final SHA256
hashes for the DSP-(1,1B,2,3,4) and ST-(0010,0011) coprocessors, user
interface improvements, and major internal code restructuring.

Changelog (since v074):
- completely rewrote memory sub-system to support 1-byte granularity in
  XML mapping
- removed Memory inheritance and MMIO class completely, any address can
  be mapped to any function now
- SuperFX: removed SuperFXBus : Bus, now implemented manually
- SA-1: removed SA1Bus : Bus, now implemented manually
- entire bus mapping is now static, happens once on cartridge load
- as a result, read/write handlers now handle MMC mapping; slower
  average case, far faster worst case
- namespace memory is no more, RAM arrays are stored inside the chips
  they are owned by now
- GameBoy: improved CPU HALT emulation, fixes Zelda: Link's Awakening
  scrolling
- GameBoy: added serial emulation (cannot connect to another GB yet),
  fixes Shin Megami Tensei - Devichil
- GameBoy: improved LCD STAT emulation, fixes Sagaia
- ui: added fullscreen support (F11 key), video settings allows for
  three scale settings
- ui: fixed brightness, contrast, gamma, audio volume, input frequency
  values on program startup
- ui: since Qt is dead, config file becomes bsnes.cfg once again
- Super Game Boy: you can now load the BIOS without a game inserted to
  see a pretty white box
- ui-gameboy: can be built without SNES components now
- libsnes: now a UI target, compile with 'make ui=ui-libsnes'
- libsnes: added WRAM, APURAM, VRAM, OAM, CGRAM access (cheat search,
  etc)
- source: removed launcher/, as the Qt port is now gone
- source: Makefile restructuring to better support new ui targets
- source: lots of other internal code cleanup work
2011-01-27 19:52:34 +11:00
Tim Allen
53fe43afd8 Update to v074r11 release.
byuu says:

Changelog:
- debugger compiles on all three profiles
- libsnes compiles on all three platforms (no API changes to libsnes)
- memory.cpp : namespace memory removed (wram -> cpu, apuram -> smp,
  vram, oam, cgram -> ppu)
- sa1.cpp : namespace memory removed (SA-1 specific functions merged
  inline to SA1::bus_read,write)
- GameBoy: added serial link support with interrupts and proper 8192hz
  timing, but obviously it acts as if no other GB is connected to it
- GameBoy: added STAT OAM interrupt, and better STAT d1,d0 mode values
- UI: since Qt is dead, I've renamed the config files back to bsnes.cfg
  and bsnes-geometry.cfg
- SA1: IRAM was not syncing to CPU on SA-1 side
- PPU/Accuracy and PPU/Performance needed Sprite oam renamed to Sprite
  sprite; so that I could add uint8 oam[544]
  - makes more sense anyway, OAM = object attribute memory, obj or
    sprite are better names for Sprite rendering class
- more cleanup
2011-01-24 20:03:17 +11:00
Tim Allen
054bdd4094 Update to v074r10 release.
byuu says:

Major WIP, countless changes. I really went to town on cleaning up the
source today with all kinds of new ideas. I'll post the ones I remember,
use diff -ru to get the rest.

What I like the most is my new within template:

    template<unsigned lo, unsigned hi>
    alwaysinline bool within(unsigned addr) {
      static const unsigned mask = ~(hi ^ lo);
      return (addr & mask) == lo;
    }

Before, you would see code like this:

    if((addr & 0xe0e000) == 0x206000) {  //$20-3f:6000-7fff

The comment is basically necessary, and you have to trust that the mask
is right, or do the math yourself.

Now, it looks like this:

    if(within<0x20, 0x3f, 0x6000, 0x7fff>(addr)) {

That's the same as within<0x206000, 0x3f7fff>, I just made an
SNES-variant to more closely simulate my XML mapping style:
20-3f:6000-7fff.

Now obviously this has limitations, it only works in base-2 and it can't
manage some tricky edge cases like (addr & 0x408000) == 0x008000 for
00-3f|80-bf:8000-ffff. But for the most part, I'll be using this where
I can. The Game Boy is fully ported over to it (via the MBCs), but the
SNES only has the BS-X town cartridge moved over so far. SuperFX and
SA-1 at the very least could benefit.

Next up, since the memory map is now static, there's really no reason to
remap the entire thing at power-on and reset. So it is now set up at
cartridge load and that's it. I moved the CPU/PPU/WRAM mapping out of
memory.cpp and into their respective processors. A bit of duplication
only because there are multiple processor cores for the different
profiles, but I'm not worried about that. This is also going to be
necessary to fix the debugger.

Next, Coprocessor::enable() actually does what I initially intended it
to now: it is called once to turn a chip on after cartridge load. It's
not called on power cycle anymore. This should help fix power-cycle on
my serial simulation code, and was needed to map the bus exactly one
time. Although most stuff is mapped through XML, some chips still need
some manual hooks for monitoring and such (eg S-DD1.)

Next, I've started killing off memory::, it was initially an
over-reaction to the question of where to put APURAM (in the SMP or
DSP?). The idea was to have this namespace that contained all memory for
everything. But it was very annoying and tedious, and various chips
ignored the convention anyway like ST-0011 RAM, which couldn't work
anyway since it is natively uint16 and not uint8. Cx4 will need 24-bit
RAM eventually, too. There's 8->24-bit functions in there now, because
the HLE code is hideous.

So far, all the cartridge.cpp memory:: types have been destroyed.
memory::cartrom, memory::cartram become cartridge.rom and cartridge.ram.
memory::cartrtc was moved into the SRTC and SPC7110 classes directly.
memory::bsxflash was moved into BSXFlash. memory::bsxram and
memory::bsxpram were moved into BSXCartridge (the town cartridge).
memory::st[AB](rom|ram) were moved into a new area,
snes/chip/sufamiturbo. The snes/chip moniker really doesn't work so
well, since it also has base units, and the serial communications stuff
which is through the controller port, but oh well, now it also has the
base structure for the Sufami Turbo cartridge too. So now we have
sufamiturbo.slotA.rom, sufamiturbo.slotB.ram, etc.

Next, the ST-0010/ST-0011 actually save the data RAM to disk. This
wasn't at all compatible with my old system, and I didn't want to keep
adding memory types to check inside the main UI cartridge RAM loading
and saving routines.

So I built a NonVolatileRAM vector inside SNES::Cartridge, and any chip
that has memory it wants to save and load from disk can append onto it
: data, size, id ("srm", "rtc", "nec", etc) and slot (0 = cartridge,
1 = slot A, 2 = slot B)

To load and save memory, we just do a simple: foreach(memory,
SNES::cartridge.nvram) load/saveMemory(memory).

As a result, you can now keep your save games in F1 Race of Champions II
and Hayazashi Nidan Morita Shougi. Technically I think Metal Combat
should work this way as well, having the RAM being part of the chip
itself, but for now that chip just writes directly into cartridge.ram,
so it also technically saves to disk for now.

To avoid a potential conflict with a manipulated memory map, BS-X SRAM
and PSRAM are now .bss and .bsp, and not .srm and .psr. Honestly I don't
like .srm as an extension either, but it doesn't bother me enough to
break save RAM compatibility with other emulators, so don't worry about
that changing.

I finally killed off MappedRAM initializing size to ~0 (-1U). A size of
zero means there is no memory there just the same. This was an old
holdover for handling MMIO mapping, if I recall correctly. Something
about a size of zero on MMIO-Memory objects causing it to wrap the
address, so ~0 would let it map direct addresses ... or something.
Whatever, that's not needed at all anymore.

BSXBase becomes BSXSatellaview, and I've defaulted the device to being
attached since it won't affect non-BSX games anyway. Eventually the GUI
needs to make that an option. BSXCart becomes BSXCartridge. BSXFlash
remains unchanged.

I probably need to make Coprocessor::disable() functions now to free up
memory on unload, but it shouldn't hurt anything the way it is.

libsnes is most definitely broken to all hell and back now, and the
debugger is still shot. I suppose we'll need some tricky code to work
with the old ID system, and we'll need to add some more IDs for the new
memory types.
2011-01-24 19:59:45 +11:00
Tim Allen
ecf96726f9 Update to v074r09 release.
byuu says:

This WIP fixes up the last bit from the old memory mapping system, the
BS-X Satellaview town cartridge now has a static memory mapper that is
dynamically decoded based on register settings. Tested with BS Town + BS
Zelda, and I can get in-game just fine. The mapping code is actually
a lot more readable this way, too.
2011-01-22 19:18:03 +11:00
Tim Allen
cab5917806 Update to v074r08 release.
byuu says:

The nall::function binding for the memory map apparently breaks when the
debugger is enabled, as PPU:: becomes PPUdebugger::, etc; and C++ isn't
smart enough to upconvert for us.
Not sure how I am going to work around that yet ...

Changelog:
- improved GameBoy::CPU::Halt emulation, fixes Legend of Zelda intro
  water+world-map scrolling at the same time
- added GameBoy::APU skeleton, and hooked up MMIO read/write for all
  registers
- modified nall integer->string functions
2011-01-22 19:15:49 +11:00
Tim Allen
c833b69087 Update to v074r07 release.
byuu says:

Fullscreen works on all ports, but Qt is in general pretty wonky at the
moment. Don't really care right now.

F11 toggles fullscreen, there is no menu or status in fullscreen, and it
auto-scales to the highest even multiple that it can.
It copies all other settings (filter, shader, aspect correction, video
region, vsync, async) from your windowed mode settings.
You cannot enable the menu or status bars, because those are a major
pain in the ass to support with both GTK+ and Qt returning nonsense
geometries.
Combine with each platform treating whether or not to handle menus
and/or statuses as part of the geometry or not, and it's a recipe for
pain.
It is pseudo-fullscreen for now, but if we have some spiffy code in the
future to set a true fullscreen that works on Linux (and hopefully OS
X), I'll add a true-FS option to ruby later.
2011-01-18 21:20:05 +11:00
Tim Allen
1a065bafb1 Update to v074r06 release.
byuu says:

This WIP uses the 16MB+64MB memory tables for 1:1 mapping of the S-CPU
bus.
Minimum RAM requirement goes up to 128MB, dare I be bold here and
recommend you have 256MB.

I also hooked up the basic bindings for making pseudo-fullscreen windows
in phoenix/Windows and phoenix/GTK+, but both have some serious issues.
- GTK+ won't resize my form container, even though it's the same code
  I used successfully in bsnes v039 and prior with hiro
- Windows scale selection breaks the faux-fullscreen effects
- I am intending to write off the menu/status bars and just auto-size
  the video to fill the screen, nice and simple
2011-01-18 21:17:48 +11:00
Tim Allen
ea077a7d96 Update to v074r05 release.
byuu says:

Oh good, that turned out to be a lot easier than I expected.
Almost all of the work was already done yesterday in porting the SA-1
over to a static map.
2011-01-17 00:22:51 +11:00
Tim Allen
a937f9b79b Update to v074r04 release.
byuu says:

SuperFXBus was inheriting from Bus, in other words using a copy of the
same memory mapping subsystem.
I've removed that and made it a simple mask, since only ROM+RAM are
mapped there.
2011-01-17 00:17:45 +11:00
Tim Allen
2d73086569 Update to v074r03 release.
byuu says:

You guys are going to hate the hell out of this one. It's twenty hours
of non-stop work, no exaggeration at all. Started at 4AM, just wrapped
up now at 8PM.
I rewrote the entire memory subsystem.

Old system:
65536 pages that map 256 bytes each
Mapping a new page overwrites old page
Granularity capped at 256 bytes minimum, requiring ST-001x to map
    60:0000-00ff instead of 60:0000,0001
Classes inherit from MMIO and Memory, forcing only one mappable function
    per class, and fixed names
MMIO sub-mapper inside memory: 00-3f:2000-5fff for one-byte granularity
Can dynamically change the map at run-time, MMC register settings
    perform dynamic remapping

New system:
XML mapping is still based around banklo-bankhi:addrlo-addrhi, as that
    shapes almost everything on the SNES very well
Internally, 2048 pages that map 8192 bytes each
Pages are vectors, scans O(n) from last to first (O(log n) would not
    help, n is never > 3)
Can multi-cast writes, but not reads [for the obvious reason of: which
    read do you return?]
Can map reads and writes separately
Granularity of one for entire 24-bit address range, no need for MMIO
    - whatever is in XML is exactly what you get
Read/Write tables bind function callbacks, so I can have any number of
    functions with any names from any classes with no inheritance (no
    more uPD7725DR, uPD7725SR helpers, etc)
Less memory usage overall due to less tables [ I tried 16 million tables
    and it used 2GB of RAM >_o ]
Cannot dynamically change the map at run-time, MMC read/write functions
    perform address translation [worse average case speed, better worst
    case speed]

Now the hate me part, functors can't beat virtual functions for speed.
There are speed penalties involved:
-4.5% on average games
-11% on SuperFX games (SFX has its own bus)
-15% on SA-1 games (SA-1 has two buses)
Of course the two that need the speed the most get the biggest hits.

I'm afraid there's really not a lot of wiggle room to boost speed back
up.
I suppose one bright spot is that we can much more easily try out
entirely new mapping systems now, since the dynamic portions have been
eliminated.
2011-01-15 15:30:29 +11:00
Tim Allen
5810e69be3 Update to v074r02 release.
byuu says:

Changelog:
- updated/fixed st0011.bin SHA256 sum
- removed launcher/
- building ui-gameboy only builds the Game Boy core, not the SNES core
- binary name output is bgameboy for ui-gameboy
- make install/make uninstall work as expected with the extra data files

[Editor's note - those last three changes seem to have been made in
v074r01]
2011-01-13 21:08:27 +11:00
Tim Allen
a198e555dc Update to v074r01 release.
byuu says:

Changelog:
- fixed libsnes to compile again, the GB RTC constant is there but
  doesn't do anything just yet (no serialize support in GameBoy core)
- libsnes: added SNES_MEMORY_(WRAM,APURAM,VRAM,OAM,CGRAM) -- really only
  for the first one, it allows libsnes users to implement their own
  cheat search
- you can now load the SGB BIOS without a game!! please be sure to enjoy
  the blinking cartridge icon emulation :D
- necdsp (uPD7725,96050) - simplified code a bit: removed persistent
  regs.idb, simplified jumps, merged exec() with main loop, etc.
- nall::function - fixed an initialization bug when copy-constructing
  objects
- nall::vector - use calloc instead of malloc to help safeguard against
  uninitialized class data (potentially hides errors, but better than
  crashing in production)
2011-01-13 21:07:04 +11:00
Tim Allen
20afa076ef Update to v074 release.
byuu says (since v073):

This release adds full low-level emulation of the NEC uPD96050
coprocessor, used by the ST-0010 (F1 Race of Champions II) and the
ST-0011 (Hayazashi Nidan Morita Shougi). The former was already playable
with HLE, but lacked timing emulation. The latter has never been
playable through emulation before now. But as with SD Gundam GX before,
you really weren't missing much.

[...]

Also new in this release is my own Game Boy emulator. It is being used
to provide native Super Game Boy support, built directly into bsnes.
This core is released under the GPLv2, but I am willing to grant a more
permissive license for other SNES emulators, if anyone is interested.

Of course I cannot compete with the quality of gambatte, and certainly
not from only a weeks' worth of work. Currently, there is no Game
Boy-side sound output and there are quite a few bugs remaining in its
emulation core. I would appreciate any help on this, the Game Boy is not
my forte. So yes, we are taking a step back today, so that we may take
two steps forward in the future.

[...]

Lastly, the debugger is still Linux-only, but it is now stable enough to
be considered usable. Check it out if you like, compile with -DDEBUGGER
to enable it.
2011-01-11 21:30:47 +11:00
Tim Allen
92da6bde26 Update to v073r07 release.
byuu says:

I give up for now. This WIP doesn't really improve uPD any, but it does
have major fixes to the debugger.
Debugger is still Linux-only for want of a HexEditor widget, but the
Linux one is now usable.
Before it'd trace and search for breakpoint at sub-1fps. Fixed it to not
break out of core for simple things and it'll run at near-fullspeed for
BPs, half-speed for tracing to file (lots of I/O.)
2011-01-10 23:21:57 +11:00
Tim Allen
73113da41e Update to v073r06 release.
byuu says:

Above WIP will automatically map in the ST-001x chips, no need for an
XML file.
F1 Race of Champions II (J) is fully playable, at least. It's forced to
15MHz like the ST-0011 for now.

The core (snes/chip/necdsp/core) is somewhat unstable at the moment,
some debugging hooks commented out and some hacks / ideas floating
around in there. Disassembler can't handle long jumps yet.
2011-01-10 23:19:01 +11:00
Tim Allen
a7ffc31282 Update to v073r05 release.
Added uPD96050 emulation, but it's not working right now. I'm totally
stumped. The ST-0010 player cars aren't moving right, and it crashes
sometimes on exit like it's going out of bounds.

I had decided to try and avoid duplicating the 20KB uPD7725 core, so
I made a new folder:
snes/chip/necdsp
snes/chip/necdsp/upd7725
snes/chip/necdsp/upd96050

The latter two derive from the former, and they specify the PC, RP and
DP sizes, which allocates appropriate memory.
I use simple runtime-masked registers (to avoid template hell on every
uPDcore function), and then I derive from that to complete each actual
DSP. The uPD7725 seems to work great, but the uPD96050 is fucked and
I don't know why :(
Help would be appreciated, it must be something simple because I am
certain the cars were working before.

Kinda wish I didn't scrap the old code now :(
2011-01-08 21:20:59 +11:00
Tim Allen
1c6a8543cd Update to v073r04 release.
I've merged the bgameboy GUI into the bsnes project, as it's only ~3KB
and it means I don't have to keep manually editing and copying two
projects around. You can set ui := ui-gameboy to build bgameboy as
a standalone, and ui := ui to build the bsnes you are used to. I will be
polishing it to allow changing the binary output name, not building the
SNES portions when not needed, etc in time.

So that'll end bgameboy standalone releases, at least for now. If it
ever gets important enough to split back out again I can, but I prefer
it this way. bgameboy was never meant to be mainstream anyway, just
a component for bsnes.
2011-01-08 21:08:57 +11:00
Tim Allen
4dbce5a0b2 Update to v073r03 release.
byuu says:

Changelog:
- much tighter SGB integration, but this is still a work-in-progress
  - memory::gb(rom,ram,rtc) is gone, uses GameBoy:: memory structures
    directly (a big gain, no need to copy memory to save and load)
  - UI-based cartridge loading works with GameBoy:: directly as well
    - libsnes will need to be updated internally to reflect this
  - games can save and load (even before bgameboy can, hah)
  - save states hooked up, but they crash the DMG. I don't know why, as
    if it was hard enough saving states with libco, try doing it for an
    emulator inside an emulator >_<
- last remnants of old SGB stuff removed, <sueprgameboy> XML converted
  to <icd2>
- looks like the XML list idea is looking pretty useless for
  SNES::Cartridge now that bgameboy handles its own XML mapping
2011-01-08 21:06:09 +11:00
Tim Allen
3f960374ad Update to v073r02 release.
byuu says:

Changelog:
- removed bsnes/supergameboy (libgameboy -> libgambatte binding)
- added direct ICD2-R coprocessor emulation
- linked in bgameboy to the ICD2-R module
- Makefile removes -Isnes, all files adjusted from <name.hpp> to
  <snes/name.hpp> [relic from asnes split]

You can now play Super Game Boy games with the core bsnes library.
2011-01-08 21:01:51 +11:00
Tim Allen
d8a386031f Update to release v000r11.
byuu says:

Changelog:
- adds XML memory mapping to cartridge loading;
  nall/gameboy/cartridge.hpp will bullshit one for now
- still need to add proper MMM01 detection (menu code goes at end of ROM
  apparently)
- added save state support, which works here (but not inside bsnes, go
  figure), to aid in debugging. Expect constant changes for now a long
  while, it's way too early to add states
2011-01-07 22:11:56 +11:00
Tim Allen
f0796e546e Update to release v000r10.
byuu says:

Changelog:
- fixed LYC interrupt at LY=0 (fixes Makai Toushi SaGa)
- fixed MBC3 ROM bank mapping (fixes Harvest Moon GBC)
- added Super Game Boy MLT_REQ support to JOYP, needed for ICD2-R emulation
- temporarily changed System::run() to execute only four cycles before
  exiting for bsnes, will make two versions later
- uses actual boot ROMs, has DMG+SGB1 for now. Need SGB2, don't care
  about CGB. Defaults to SGB1, no way to select just yet.  DMG 4-second
  wait is annoying. Does not force games to act like SGB on bgameboy
  itself, because that has no ICD2 and fails the required MLT_REQ check
2011-01-06 21:16:07 +11:00
Tim Allen
2d49a4408d Update to release v000r09.
byuu says:

Changelog:
- added skeleton HuC1 (gets to a menu)
- added skeleton HuC3 (gets to a menu, this one is supposedly a lot more
  complex (IR, RTC, etc.))
- added MMM01 emulation
2011-01-05 20:59:22 +11:00
Tim Allen
a7ffbd784b Update to release v000r08.
byuu says:

Changelog:
- rewrote sprite rendering, grabs first ten sprites, draws them in
  revere order of a: X-index, b: OAM appearance order
- simplified tile decoding to use less variables
- added MBC3 emulation (for now, RTC is always enabled)
- STOP can be broken via joypad IF, this may not be correct though (it
  may trigger even without P10-13 being modified)
- cleaned up all MBC emulation to use masks instead of ranges
- MBC2 uses 512-byte table now, ignores high 4-bits. Easier this way I guess
- tools menu temporarily has a console tracer enable option
- some other stuff

No real visible improvements :(
2011-01-04 21:42:27 +11:00
Tim Allen
afdb3c4d20 Update to release v000r07.
byuu says:

Changelog:
- fixed sprite Vflip check
- fixed up window rendering (well, mostly, works great in Megaman II but
  not so great in Makaitoushi SaGa)
- added MBC2, MBC5 (already had MBC0, MBC1)
- removed reset, hooked up power cycle and Vsync toggle
- some other stuff

Makaitoushi SaGa locks on the main menu after some graphical glitches on
the title screen, damn.
Shin Megami Tensei - Devichil Black Book locks up immediately, hitting
HALT opcodes all the time, damn again.

Megaman II should be fully playable now.
Contra 3 is really close, but goes crazy on the turtle boss fight.
2011-01-03 15:28:36 +11:00
Tim Allen
ebd6a52811 Update to release v000r06.
byuu says:

Added MBC1 emulation, although battery RAM doesn't save or load to disk
yet.
Made up a fake MBC0 which is really just saying 'no MBC', for consistent
handling of all MBCs.
Added bumpers to stop ROM/RAM out of bounds accesses.
Added STAT interrupts for LY coincidence, Vblank and Hblank (not for OAM
access yet, I don't know the timings.)
Fixed timer interrupt [Jonas Quinn]
Made all interrupts call a CPU function instead of just setting a flag
for better control (to allow below addition.)
Added HALT and STOP emulation, the latter permanently locks the Game Boy
for now. The former breaks on interrupts.
Rewrote all the rendering code to suck 50% less, though it's still
absolutely miserable and scanline-based.
Added pixel-level horizontal scrolling to BGs.
Fixed OBJ rendering error that was making them render upside down (I was
flipping to compensate before.)
Added OBJ 8x16 mode.
Added OBJ priority support.
Added window (but it's broken to all hell on Mega Man II.)
2011-01-02 15:46:54 +11:00
Tim Allen
42a9f9cfa4 Update to release v000r05.
byuu says:

Fixed all of the previously mentioned problems by myself and Jonas
Quinn.
Fixed up JOYP and hooked up JOYP interrupts, they work on JOYP writes as
well if your selection makes the low four bits change to != 0xF.
Added basic sprite emulation, very very very lousy but it works for
Tetris.
Fixed DAA, fuck that opcode. Fixes blargg's CPU tests 1 and 11 (for some
odd reason.) Only test 2 is failing, on the "EI" test. Maybe it relies
on STAT interrupts?
Did some other stuff.

Tetris is now 100% fully playable. But that renderer is an abomination.
Soooooo simplistic and missing so many edge cases.
But holy shit, a fully playable commercial game in three days. I would
have killed to have made that progress when I started on bsnes.
2010-12-31 16:43:47 +11:00
Tim Allen
2330ed6e8c Update to release v000r04.
byuu says:

[screenshot of the Tetris title screen]

Less than fourty-eight hours after starting on this. I guess you weren't
kidding, Exophase.
2010-12-30 18:18:47 +11:00
Tim Allen
71780949b0 Update to release v000r03.
byuu says:

All 512 instructions implemented. To make debugging flags easier and to
reduce the code size, I made parent routines for all the bit-logic that
sets flags. This bumped up the speed to 3,045fps. So about 51x faster
than a real Game Boy.

I suspect the frame rate to plummet rapidly as I emulate more stuff and
try and get timings more accurate. Wild ballpark, I'd guess 300fps or
so. Not based on anything, just a made up number. I have no idea what it
will end up being.

So I still need:
* decode MMIO reads/writes
* add CPU interrupt support
* add LCD emulation (probably the hardest part)
* add PCM emulation (well, this would likely be the hardest; but it's
  not essential right now)
* debug the holy living shit out of the CPU core. Anyone want to help
  now by looking it over? :D
2010-12-30 18:18:23 +11:00
Tim Allen
1c3c7fe0a7 Update to release v000r02.
byuu says:

314 of 512 opcodes implemented, can execute the first 67,450
instructions of Tetris.

I also added an MMIO bus, ala bsnes, so that I can map and access
individual registers with a single indirection.
2010-12-29 22:03:42 +11:00
Tim Allen
e0a9f1cf2c Update to release v000r01.
byuu says:

Hooked up a scheduler to enter/exit the CPU core wherever I want. Added
basic 4*1024*1024hz clock, and about eleven or so opcodes. Creating the
disassembler as I encounter each new opcode, not skipping ahead to do
all 'like other' opcodes, eg if I add 'dec b', I don't then add 'dec c'
until I encounter it.
2010-12-28 17:03:02 +11:00
Tim Allen
246d6aaf08 Add .gitignore files to populate obj/ and out/ 2010-12-28 12:57:58 +11:00
Tim Allen
da7d9f2662 Initial commit of bgameboy v000.
The source tarball also included empty obj/ and out/ directories which
git does not support.

byuu says:

Project started, so basically everything is new.

It's basically a rough skeleton that mimics bsnes project structure.
Eventually the src/gameboy folder will be copied into bsnes-official and
used by the chip/supergameboy core.
The middleware layer (supergameboy/interface) will be merged into a new
chip/icd2 folder that will represent direct Super Game Boy emulation in
the future.
At least, if all goes according to plan.

There is a simple GUI that can load ROMs, but do nothing after it. It's
not hooked up to ruby yet.
There is a basic system class and interface to expose the
video/audio/input functions.
There is a basic memory bus that doesn't support any MBCs yet.
There is a CPU skeleton that only handles easy read/write access to the
CPU registers (AF is a really fucked up register.)
The core is not hooked up to libco yet, but I intend for it to be, so
that I can run the CPU + LCD how I like.
If it turns out the LCD+audio is easily enslavable, then I'll probably
drop libco and just run it like a regular emulator, using a thread
wrapper around it in bsnes only. We'll see.

The CPU doesn't actually support any opcodes, and loading a ROM won't
actually execute anything.
2010-12-28 12:53:15 +11:00
Tim Allen
e0a5452e8d Update to v073r01 release.
byuu says:

While perhaps not perfect, pretty good is better than nothing ... I've
added emulation of auto-joypad poll timing.
Going off ikari_01's confirmation of what we suspected, that the strobe
happens every 256 clocks, I've set up emulation as follows:
Upon reset, our clock counter is reset to zero.
At the start of each frame, our poll counter is reset to zero.
Every 256 clocks, we call the step_auto_joypad_poll() function.
If we are at V=225/240+ (based on overscan setting), we check the poll
counter.
At zero, we poll the actual controller and set the joypad polling flag
in $4212.d0 to 1.
From zero through fifteen, we read in one bit for each controller and
shift it into the register.
At sixteen, we turn off the joypad polling flag.
The 256-clock divider allows the start point of polling for each frame
to fluctuate wildly like real hardware.
I count regardless of auto joypad enable, as per $4212.d0's behavior;
but only poll when it's actually enabled.
I do not consume any actual time from this polling. I honestly don't
know if I even should, or if it manages to do it in the background.
If it should consume time, then this most likely happens between opcode
edges and we'll have to adjust the code a good bit.

All commercial games should continue to work fine, but this will likely
break some hacks/translations not tested on hardware.
Without the timing emulation, reading $4218-421f before V=~228 would
basically give you the valid input controller values of the previous
frame.
Now, like hardware, it should give you a state that is part previous
frame, part current frame shifted into it. Button positions won't be
reliable and will shift every 256 clocks.

I've also removed the Qt GUI, and renamed ui-phoenix to just ui. This
removes 400kb of source code (phoenix is a lean 130kb), and drops the
archive size from 564KB to 475KB. Combined with the DSP HLE, and we've
knocked off ~570KB of source cruft from the entire project. I am looking
forward to not having to specify which GUI is included anymore.
2010-12-27 18:29:57 +11:00
1200 changed files with 85411 additions and 150160 deletions

View File

@@ -1,14 +1,15 @@
include nall/Makefile
snes := snes
gameboy := gameboy
profile := accuracy
ui := ui-phoenix
ui := ui
# compiler
c := $(compiler) -std=gnu99
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
flags := -O3 -fomit-frame-pointer -I. -I$(snes)
flags := -O3 -fomit-frame-pointer -I.
link :=
objects :=
objects := libco
# profile-guided instrumentation
# flags += -fprofile-generate
@@ -45,38 +46,12 @@ compile = \
all: build;
include $(snes)/Makefile
obj/libco.o: libco/libco.c libco/*
include $(ui)/Makefile
objects := $(patsubst %,obj/%.o,$(objects))
# targets
build: ui_build $(objects)
ifeq ($(platform),osx)
test -d ../bsnes.app || mkdir -p ../bsnes.app/Contents/MacOS
$(strip $(cpp) -o ../bsnes.app/Contents/MacOS/bsnes $(objects) $(link))
else
$(strip $(cpp) -o out/bsnes $(objects) $(link))
endif
install:
ifeq ($(platform),x)
install -D -m 755 out/bsnes $(DESTDIR)$(prefix)/bin/bsnes
install -D -m 644 data/bsnes.png $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png
install -D -m 644 data/bsnes.desktop $(DESTDIR)$(prefix)/share/applications/bsnes.desktop
test -d ~/.bsnes || mkdir ~/.bsnes
cp data/cheats.xml ~/.bsnes/cheats.xml
chmod 777 ~/.bsnes ~/.bsnes/cheats.xml
endif
uninstall:
ifeq ($(platform),x)
rm $(DESTDIR)$(prefix)/bin/bsnes
rm $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png
rm $(DESTDIR)$(prefix)/share/applications/bsnes.desktop
endif
clean: ui_clean
clean:
-@$(call delete,obj/*.o)
-@$(call delete,obj/*.a)
-@$(call delete,obj/*.so)
@@ -90,6 +65,6 @@ clean: ui_clean
-@$(call delete,*.manifest)
archive-all:
tar -cjf bsnes.tar.bz2 data launcher libco nall obj out phoenix ruby snes ui-phoenix ui-qt Makefile cc.bat clean.bat sync.sh
tar -cjf bsnes.tar.bz2 data gameboy libco nall obj out phoenix ruby snes ui ui-gameboy ui-libsnes Makefile cc.bat clean.bat sync.sh
help:;

File diff suppressed because it is too large Load Diff

12
bsnes/gameboy/Makefile Executable file
View File

@@ -0,0 +1,12 @@
gameboy_objects := gameboy-system gameboy-scheduler
gameboy_objects += gameboy-memory gameboy-cartridge
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
objects += $(gameboy_objects)
obj/gameboy-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/)
obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(gameboy)/scheduler/)
obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/memory/)
obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/)
obj/gameboy-apu.o: $(gameboy)/apu/apu.cpp $(call rwildcard,$(gameboy)/apu/)
obj/gameboy-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/)

105
bsnes/gameboy/apu/apu.cpp Executable file
View File

@@ -0,0 +1,105 @@
#include <gameboy/gameboy.hpp>
#define APU_CPP
namespace GameBoy {
#include "square1/square1.cpp"
#include "square2/square2.cpp"
#include "wave/wave.cpp"
#include "noise/noise.cpp"
#include "master/master.cpp"
#include "serialization.cpp"
APU apu;
void APU::Main() {
apu.main();
}
void APU::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(sequencer_base == 0) { //512hz
if(sequencer_step == 0 || sequencer_step == 2 || sequencer_step == 4 || sequencer_step == 6) { //256hz
square1.clock_length();
square2.clock_length();
wave.clock_length();
noise.clock_length();
}
if(sequencer_step == 2 || sequencer_step == 6) { //128hz
square1.clock_sweep();
}
if(sequencer_step == 7) { //64hz
square1.clock_envelope();
square2.clock_envelope();
noise.clock_envelope();
}
sequencer_step++;
}
sequencer_base++;
square1.run();
square2.run();
wave.run();
noise.run();
master.run();
system.interface->audio_sample(master.center, master.left, master.right);
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
}
}
void APU::power() {
create(Main, 4194304);
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
foreach(n, mmio_data) n = 0x00;
sequencer_base = 0;
sequencer_step = 0;
square1.power();
square2.power();
wave.power();
noise.power();
master.power();
}
uint8 APU::mmio_read(uint16 addr) {
static const uint8 table[48] = {
0x80, 0x3f, 0x00, 0xff, 0xbf, //square1
0xff, 0x3f, 0x00, 0xff, 0xbf, //square2
0x7f, 0xff, 0x9f, 0xff, 0xbf, //wave
0xff, 0xff, 0x00, 0x00, 0xbf, //noise
0x00, 0x00, 0x70, //master
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //unmapped
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
};
if(addr == 0xff26) {
uint8 data = master.enable << 7;
if(square1.counter && square1.length) data |= 0x01;
if(square2.counter && square2.length) data |= 0x02;
if( wave.counter && wave.length) data |= 0x04;
if( noise.counter && noise.length) data |= 0x08;
return data | table[addr - 0xff10];
}
if(addr >= 0xff10 && addr <= 0xff3f) return mmio_data[addr - 0xff10] | table[addr - 0xff10];
return 0xff;
}
void APU::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0xff10 && addr <= 0xff3f) mmio_data[addr - 0xff10] = data;
if(addr >= 0xff10 && addr <= 0xff14) return square1.write (addr - 0xff10, data);
if(addr >= 0xff15 && addr <= 0xff19) return square2.write (addr - 0xff15, data);
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write (addr - 0xff1a, data);
if(addr >= 0xff1f && addr <= 0xff23) return noise.write (addr - 0xff1f, data);
if(addr >= 0xff24 && addr <= 0xff26) return master.write (addr - 0xff24, data);
if(addr >= 0xff30 && addr <= 0xff3f) return wave.write_pattern(addr - 0xff30, data);
}
}

28
bsnes/gameboy/apu/apu.hpp Executable file
View File

@@ -0,0 +1,28 @@
struct APU : Processor, MMIO {
#include "square1/square1.hpp"
#include "square2/square2.hpp"
#include "wave/wave.hpp"
#include "noise/noise.hpp"
#include "master/master.hpp"
uint8 mmio_data[48];
uint13 sequencer_base;
uint3 sequencer_step;
Square1 square1;
Square2 square2;
Wave wave;
Noise noise;
Master master;
static void Main();
void main();
void power();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void serialize(serializer&);
};
extern APU apu;

View File

@@ -0,0 +1,132 @@
#ifdef APU_CPP
void APU::Master::run() {
if(enable == false) {
center = 0;
left = 0;
right = 0;
return;
}
signed sample = 0, channels;
sample += apu.square1.output;
sample += apu.square2.output;
sample += apu.wave.output;
sample += apu.noise.output;
sample >>= 2;
center = sclamp<16>(sample);
if(left_enable == false && right_enable == false) {
left = center;
right = center;
return;
}
sample = 0;
channels = 0;
if(channel1_left_enable) { sample += apu.square1.output; channels++; }
if(channel2_left_enable) { sample += apu.square2.output; channels++; }
if(channel3_left_enable) { sample += apu.wave.output; channels++; }
if(channel4_left_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
left = sclamp<16>(sample);
switch(left_volume) {
case 0: left >>= 3; break; // 12.5%
case 1: left >>= 2; break; // 25.0%
case 2: left = (left >> 2) + (left >> 3); break; // 37.5%
case 3: left >>= 1; break; // 50.0%
case 4: left = (left >> 1) + (left >> 3); break; // 62.5%
case 5: left -= (left >> 2); break; // 75.0%
case 6: left -= (left >> 3); break; // 87.5%
//case 7: break; //100.0%
}
if(left_enable == false) left = 0;
sample = 0;
channels = 0;
if(channel1_right_enable) { sample += apu.square1.output; channels++; }
if(channel2_right_enable) { sample += apu.square2.output; channels++; }
if(channel3_right_enable) { sample += apu.wave.output; channels++; }
if(channel4_right_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
right = sclamp<16>(sample);
switch(right_volume) {
case 0: right >>= 3; break; // 12.5%
case 1: right >>= 2; break; // 25.0%
case 2: right = (right >> 2) + (right >> 3); break; // 37.5%
case 3: right >>= 1; break; // 50.0%
case 4: right = (right >> 1) + (right >> 3); break; // 62.5%
case 5: right -= (right >> 2); break; // 75.0%
case 6: right -= (right >> 3); break; // 87.5%
//case 7: break; //100.0%
}
if(right_enable == false) right = 0;
}
void APU::Master::write(unsigned r, uint8 data) {
if(r == 0) {
left_enable = data & 0x80;
left_volume = (data >> 4) & 7;
right_enable = data & 0x08;
right_volume = (data >> 0) & 7;
}
if(r == 1) {
channel4_left_enable = data & 0x80;
channel3_left_enable = data & 0x40;
channel2_left_enable = data & 0x20;
channel1_left_enable = data & 0x10;
channel4_right_enable = data & 0x08;
channel3_right_enable = data & 0x04;
channel2_right_enable = data & 0x02;
channel1_right_enable = data & 0x01;
}
if(r == 2) {
enable = data & 0x80;
}
}
void APU::Master::power() {
left_enable = 0;
left_volume = 0;
right_enable = 0;
right_volume = 0;
channel4_left_enable = 0;
channel3_left_enable = 0;
channel2_left_enable = 0;
channel1_left_enable = 0;
channel4_right_enable = 0;
channel3_right_enable = 0;
channel2_right_enable = 0;
channel1_right_enable = 0;
enable = 0;
center = 0;
left = 0;
right = 0;
}
void APU::Master::serialize(serializer &s) {
s.integer(left_enable);
s.integer(left_volume);
s.integer(right_enable);
s.integer(right_volume);
s.integer(channel4_left_enable);
s.integer(channel3_left_enable);
s.integer(channel2_left_enable);
s.integer(channel1_left_enable);
s.integer(channel4_right_enable);
s.integer(channel3_right_enable);
s.integer(channel2_right_enable);
s.integer(channel1_right_enable);
s.integer(enable);
s.integer(center);
s.integer(left);
s.integer(right);
}
#endif

View File

@@ -0,0 +1,24 @@
struct Master {
bool left_enable;
unsigned left_volume;
bool right_enable;
unsigned right_volume;
bool channel4_left_enable;
bool channel3_left_enable;
bool channel2_left_enable;
bool channel1_left_enable;
bool channel4_right_enable;
bool channel3_right_enable;
bool channel2_right_enable;
bool channel1_right_enable;
bool enable;
int16 center;
int16 left;
int16 right;
void run();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

102
bsnes/gameboy/apu/noise/noise.cpp Executable file
View File

@@ -0,0 +1,102 @@
#ifdef APU_CPP
void APU::Noise::run() {
if(period && --period == 0) {
period = divisor << frequency;
if(frequency < 14) {
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
lfsr = (lfsr >> 1) ^ (bit << 14);
if(narrow_lfsr) lfsr |= (bit << 6);
}
}
uint4 sample = (lfsr & 1) ? 0 : volume;
if(counter && length == 0) sample = 0;
output = (sample * 4369) - 32768;
}
void APU::Noise::clock_length() {
if(counter && length) length--;
}
void APU::Noise::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
}
void APU::Noise::write(unsigned r, uint8 data) {
if(r == 1) {
initial_length = 64 - (data & 0x3f);
length = initial_length;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
}
if(r == 3) {
frequency = data >> 4;
narrow_lfsr = data & 0x08;
divisor = (data & 0x07) << 4;
if(divisor == 0) divisor = 8;
period = divisor << frequency;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
if(initialize) {
lfsr = ~0U;
length = initial_length;
envelope_period = envelope_frequency;
volume = envelope_volume;
}
}
}
void APU::Noise::power() {
initial_length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
frequency = 0;
narrow_lfsr = 0;
divisor = 0;
counter = 0;
output = 0;
length = 0;
envelope_period = 0;
volume = 0;
period = 0;
lfsr = 0;
}
void APU::Noise::serialize(serializer &s) {
s.integer(initial_length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(narrow_lfsr);
s.integer(divisor);
s.integer(counter);
s.integer(output);
s.integer(length);
s.integer(envelope_period);
s.integer(volume);
s.integer(period);
s.integer(lfsr);
}
#endif

View File

@@ -0,0 +1,24 @@
struct Noise {
unsigned initial_length;
unsigned envelope_volume;
bool envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
bool narrow_lfsr;
unsigned divisor;
bool counter;
int16 output;
unsigned length;
unsigned envelope_period;
unsigned volume;
unsigned period;
uint15 lfsr;
void run();
void clock_length();
void clock_envelope();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

View File

@@ -0,0 +1,15 @@
#ifdef APU_CPP
void APU::serialize(serializer &s) {
s.array(mmio_data);
s.integer(sequencer_base);
s.integer(sequencer_step);
square1.serialize(s);
square2.serialize(s);
wave.serialize(s);
noise.serialize(s);
master.serialize(s);
}
#endif

View File

@@ -0,0 +1,146 @@
#ifdef APU_CPP
void APU::Square1::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
phase = (phase + 1) & 7;
switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_
case 1: duty_output = (phase >= 6); break; //______--
case 2: duty_output = (phase >= 4); break; //____----
case 3: duty_output = (phase <= 5); break; //------__
}
}
uint4 sample = (duty_output ? volume : 0);
if(counter && length == 0) sample = 0;
output = (sample * 4369) - 32768;
}
void APU::Square1::sweep() {
if(enable == false) return;
signed offset = frequency_shadow >> sweep_shift;
if(sweep_direction) offset = -offset;
frequency_shadow += offset;
if(frequency_shadow < 0) {
frequency_shadow = 0;
} else if(frequency_shadow > 2047) {
frequency_shadow = 2048;
enable = false;
}
if(frequency_shadow <= 2047 && sweep_shift) {
frequency = frequency_shadow;
period = 4 * (2048 - frequency);
}
}
void APU::Square1::clock_length() {
if(counter && length) length--;
}
void APU::Square1::clock_sweep() {
if(sweep_frequency && sweep_period && --sweep_period == 0) {
sweep_period = sweep_frequency;
sweep();
}
}
void APU::Square1::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
}
void APU::Square1::write(unsigned r, uint8 data) {
if(r == 0) {
sweep_frequency = (data >> 4) & 7;
sweep_direction = data & 0x08;
sweep_shift = data & 0x07;
}
if(r == 1) {
duty = data >> 6;
length = data & 0x3f;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
}
if(r == 3) {
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
envelope_period = envelope_frequency;
volume = envelope_volume;
frequency_shadow = frequency;
sweep_period = sweep_frequency;
enable = sweep_period || sweep_shift;
if(sweep_shift) sweep();
}
}
period = 4 * (2048 - frequency);
}
void APU::Square1::power() {
sweep_frequency = 0;
sweep_direction = 0;
sweep_shift = 0;
duty = 0;
length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
frequency = 0;
counter = 0;
output = 0;
duty_output = 0;
phase = 0;
period = 0;
envelope_period = 0;
sweep_period = 0;
frequency_shadow = 0;
enable = 0;
volume = 0;
}
void APU::Square1::serialize(serializer &s) {
s.integer(sweep_frequency);
s.integer(sweep_direction);
s.integer(sweep_shift);
s.integer(duty);
s.integer(length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(counter);
s.integer(output);
s.integer(duty_output);
s.integer(phase);
s.integer(period);
s.integer(envelope_period);
s.integer(sweep_period);
s.integer(frequency_shadow);
s.integer(enable);
s.integer(volume);
}
#endif

View File

@@ -0,0 +1,31 @@
struct Square1 {
unsigned sweep_frequency;
unsigned sweep_direction;
unsigned sweep_shift;
unsigned duty;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
unsigned counter;
int16 output;
bool duty_output;
unsigned phase;
unsigned period;
unsigned envelope_period;
unsigned sweep_period;
signed frequency_shadow;
bool enable;
unsigned volume;
void run();
void sweep();
void clock_length();
void clock_sweep();
void clock_envelope();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

View File

@@ -0,0 +1,97 @@
#ifdef APU_CPP
void APU::Square2::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
phase = (phase + 1) & 7;
switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_
case 1: duty_output = (phase >= 6); break; //______--
case 2: duty_output = (phase >= 4); break; //____----
case 3: duty_output = (phase <= 5); break; //------__
}
}
uint4 sample = (duty_output ? volume : 0);
if(counter && length == 0) sample = 0;
output = (sample * 4369) - 32768;
}
void APU::Square2::clock_length() {
if(counter && length) length--;
}
void APU::Square2::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
}
void APU::Square2::write(unsigned r, uint8 data) {
if(r == 1) {
duty = data >> 6;
length = data & 0x3f;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
}
if(r == 3) {
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
envelope_period = envelope_frequency;
volume = envelope_volume;
}
}
period = 4 * (2048 - frequency);
}
void APU::Square2::power() {
duty = 0;
length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
frequency = 0;
counter = 0;
output = 0;
duty_output = 0;
phase = 0;
period = 0;
envelope_period = 0;
volume = 0;
}
void APU::Square2::serialize(serializer &s) {
s.integer(duty);
s.integer(length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(counter);
s.integer(output);
s.integer(duty_output);
s.integer(phase);
s.integer(period);
s.integer(envelope_period);
s.integer(volume);
}
#endif

View File

@@ -0,0 +1,23 @@
struct Square2 {
unsigned duty;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
unsigned counter;
int16 output;
bool duty_output;
unsigned phase;
unsigned period;
unsigned envelope_period;
unsigned volume;
void run();
void clock_length();
void clock_envelope();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

103
bsnes/gameboy/apu/wave/wave.cpp Executable file
View File

@@ -0,0 +1,103 @@
#ifdef APU_CPP
void APU::Wave::run() {
if(period && --period == 0) {
period = 2 * (2048 - frequency);
pattern_offset = (pattern_offset + 1) & 31;
pattern_sample = pattern[pattern_offset];
}
uint4 sample = pattern_sample;
if(counter && length == 0) sample = 0;
if(enable == false) sample = 0;
output = (sample * 4369) - 32768;
output >>= volume;
}
void APU::Wave::clock_length() {
if(counter && length) length--;
}
void APU::Wave::write(unsigned r, uint8 data) {
if(r == 0) {
dac_enable = data & 0x80;
if(dac_enable == false) enable = false;
}
if(r == 1) {
initial_length = 256 - data;
length = initial_length;
}
if(r == 2) {
switch((data >> 5) & 3) {
case 0: volume = 16; break; // 0%
case 1: volume = 0; break; //100%
case 2: volume = 1; break; // 50%
case 3: volume = 2; break; // 25%
}
}
if(r == 3) {
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize && dac_enable) {
enable = true;
pattern_offset = 0;
length = initial_length;
}
}
period = 2 * (2048 - frequency);
}
void APU::Wave::write_pattern(unsigned p, uint8 data) {
p <<= 1;
pattern[p + 0] = (data >> 4) & 15;
pattern[p + 1] = (data >> 0) & 15;
}
void APU::Wave::power() {
dac_enable = 0;
initial_length = 0;
volume = 0;
frequency = 0;
counter = 0;
random_cyclic r;
foreach(n, pattern) n = r() & 15;
output = 0;
enable = 0;
length = 0;
period = 0;
pattern_offset = 0;
pattern_sample = 0;
}
void APU::Wave::serialize(serializer &s) {
s.integer(dac_enable);
s.integer(initial_length);
s.integer(volume);
s.integer(frequency);
s.integer(counter);
s.array(pattern);
s.integer(output);
s.integer(enable);
s.integer(length);
s.integer(period);
s.integer(pattern_offset);
s.integer(pattern_sample);
}
#endif

22
bsnes/gameboy/apu/wave/wave.hpp Executable file
View File

@@ -0,0 +1,22 @@
struct Wave {
bool dac_enable;
unsigned initial_length;
unsigned volume;
unsigned frequency;
bool counter;
uint8 pattern[32];
int16 output;
bool enable;
unsigned length;
unsigned period;
unsigned pattern_offset;
unsigned pattern_sample;
void run();
void clock_length();
void write(unsigned r, uint8 data);
void write_pattern(unsigned p, uint8 data);
void power();
void serialize(serializer&);
};

View File

@@ -0,0 +1,156 @@
#include <gameboy/gameboy.hpp>
#include <nall/crc32.hpp>
#define CARTRIDGE_CPP
namespace GameBoy {
#include "mbc0/mbc0.cpp"
#include "mbc1/mbc1.cpp"
#include "mbc2/mbc2.cpp"
#include "mbc3/mbc3.cpp"
#include "mbc5/mbc5.cpp"
#include "mmm01/mmm01.cpp"
#include "huc1/huc1.cpp"
#include "huc3/huc3.cpp"
#include "serialization.cpp"
Cartridge cartridge;
void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
if(size == 0) size = 32768;
romdata = allocate<uint8>(romsize = size, 0xff);
if(data) memcpy(romdata, data, size);
//uint32_t crc = crc32_calculate(data, size);
//print("CRC32 = ", hex<4>(crc), "\n");
info.mapper = Mapper::Unknown;
info.ram = false;
info.battery = false;
info.rtc = false;
info.rumble = false;
info.romsize = 0;
info.ramsize = 0;
xml_element document = xml_parse(xml);
foreach(head, document.element) {
if(head.name == "cartridge") {
foreach(attr, head.attribute) {
if(attr.name == "mapper") {
if(attr.content == "none") info.mapper = Mapper::MBC0;
if(attr.content == "MBC1") info.mapper = Mapper::MBC1;
if(attr.content == "MBC2") info.mapper = Mapper::MBC2;
if(attr.content == "MBC3") info.mapper = Mapper::MBC3;
if(attr.content == "MBC5") info.mapper = Mapper::MBC5;
if(attr.content == "MMM01") info.mapper = Mapper::MMM01;
if(attr.content == "HuC1") info.mapper = Mapper::HuC1;
if(attr.content == "HuC3") info.mapper = Mapper::HuC3;
}
if(attr.name == "rtc") info.rtc = (attr.content == "true" ? true : false);
if(attr.name == "rumble") info.rumble = (attr.content == "true" ? true : false);
}
foreach(elem, head.element) {
if(elem.name == "rom") {
foreach(attr, elem.attribute) {
if(attr.name == "size") info.romsize = hex(attr.content);
}
}
if(elem.name == "ram") {
info.ram = true;
foreach(attr, elem.attribute) {
if(attr.name == "size") info.ramsize = hex(attr.content);
if(attr.name == "battery") info.battery = (attr.content == "true" ? true : false);
}
}
}
}
}
switch(info.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
case Mapper::MBC1: mapper = &mbc1; break;
case Mapper::MBC2: mapper = &mbc2; break;
case Mapper::MBC3: mapper = &mbc3; break;
case Mapper::MBC5: mapper = &mbc5; break;
case Mapper::MMM01: mapper = &mmm01; break;
case Mapper::HuC1: mapper = &huc1; break;
case Mapper::HuC3: mapper = &huc3; break;
}
ramdata = new uint8_t[ramsize = info.ramsize]();
system.load();
loaded = true;
}
void Cartridge::unload() {
if(loaded == false) return;
if(romdata) { delete[] romdata; romdata = 0; }
if(ramdata) { delete[] ramdata; ramdata = 0; }
loaded = false;
}
uint8 Cartridge::rom_read(unsigned addr) {
if(addr >= romsize) addr %= romsize;
return romdata[addr];
}
void Cartridge::rom_write(unsigned addr, uint8 data) {
if(addr >= romsize) addr %= romsize;
romdata[addr] = data;
}
uint8 Cartridge::ram_read(unsigned addr) {
if(ramsize == 0) return 0x00;
if(addr >= ramsize) addr %= ramsize;
return ramdata[addr];
}
void Cartridge::ram_write(unsigned addr, uint8 data) {
if(ramsize == 0) return;
if(addr >= ramsize) addr %= ramsize;
ramdata[addr] = data;
}
uint8 Cartridge::mmio_read(uint16 addr) {
if(bootrom_enable && within<0x0000, 0x00ff>(addr)) return System::BootROM::sgb[addr];
return mapper->mmio_read(addr);
}
void Cartridge::mmio_write(uint16 addr, uint8 data) {
if(bootrom_enable && addr == 0xff50) bootrom_enable = false;
mapper->mmio_write(addr, data);
}
void Cartridge::power() {
bootrom_enable = true;
mbc0.power();
mbc1.power();
mbc2.power();
mbc3.power();
mbc5.power();
mmm01.power();
huc1.power();
huc3.power();
for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this;
for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this;
bus.mmio[0xff50] = this;
}
Cartridge::Cartridge() {
loaded = false;
romdata = 0;
ramdata = 0;
}
Cartridge::~Cartridge() {
unload();
}
}

View File

@@ -0,0 +1,65 @@
struct Cartridge : MMIO, property<Cartridge> {
#include "mbc0/mbc0.hpp"
#include "mbc1/mbc1.hpp"
#include "mbc2/mbc2.hpp"
#include "mbc3/mbc3.hpp"
#include "mbc5/mbc5.hpp"
#include "mmm01/mmm01.hpp"
#include "huc1/huc1.hpp"
#include "huc3/huc3.hpp"
enum Mapper : unsigned {
MBC0,
MBC1,
MBC2,
MBC3,
MBC5,
MMM01,
HuC1,
HuC3,
Unknown,
};
struct Information {
string xml;
Mapper mapper;
bool ram;
bool battery;
bool rtc;
bool rumble;
unsigned romsize;
unsigned ramsize;
} info;
readonly<bool> loaded;
uint8_t *romdata;
unsigned romsize;
uint8_t *ramdata;
unsigned ramsize;
MMIO *mapper;
bool bootrom_enable;
void load(const string &xml, const uint8_t *data, unsigned size);
void unload();
uint8 rom_read(unsigned addr);
void rom_write(unsigned addr, uint8 data);
uint8 ram_read(unsigned addr);
void ram_write(unsigned addr, uint8 data);
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
void serialize(serializer&);
Cartridge();
~Cartridge();
};
extern Cartridge cartridge;

View File

@@ -0,0 +1,53 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::HuC1::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
return 0x00;
}
return 0x00;
}
void Cartridge::HuC1::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x3fff>(addr)) {
rom_select = data;
return;
}
if(within<0x4000, 0x5fff>(addr)) {
ram_select = data;
return;
}
if(within<0x6000, 0x7fff>(addr)) {
//unknown purpose
return;
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
return;
}
}
void Cartridge::HuC1::power() {
ram_enable = false;
rom_select = 0x01;
ram_select = 0x00;
}
#endif

View File

@@ -0,0 +1,9 @@
struct HuC1 : MMIO {
bool ram_enable; //0000-1fff
uint8 rom_select; //2000-3fff
uint8 ram_select; //4000-5fff
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
} huc1;

View File

@@ -0,0 +1,53 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::HuC3::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
return 0x00;
}
return 0x00;
}
void Cartridge::HuC3::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x3fff>(addr)) {
rom_select = data;
return;
}
if(within<0x4000, 0x5fff>(addr)) {
ram_select = data;
return;
}
if(within<0x6000, 0x7fff>(addr)) {
//unknown purpose
return;
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
return;
}
}
void Cartridge::HuC3::power() {
ram_enable = false;
rom_select = 0x01;
ram_select = 0x00;
}
#endif

View File

@@ -0,0 +1,9 @@
struct HuC3 : MMIO {
bool ram_enable; //0000-1fff
uint8 rom_select; //2000-3fff
uint8 ram_select; //4000-5fff
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
} huc3;

View File

@@ -0,0 +1,25 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC0::mmio_read(uint16 addr) {
if(within<0x0000, 0x7fff>(addr)) {
return cartridge.rom_read(addr);
}
if(within<0xa000, 0xbfff>(addr)) {
return cartridge.ram_read(addr & 0x1fff);
}
return 0x00;
}
void Cartridge::MBC0::mmio_write(uint16 addr, uint8 data) {
if(within<0xa000, 0xbfff>(addr)) {
cartridge.ram_write(addr & 0x1fff, data);
return;
}
}
void Cartridge::MBC0::power() {
}
#endif

View File

@@ -0,0 +1,5 @@
struct MBC0 : MMIO {
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
} mbc0;

View File

@@ -0,0 +1,70 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC1::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
if(mode_select == 0) {
return cartridge.rom_read((ram_select << 19) | (rom_select << 14) | (addr & 0x3fff));
} else {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) {
if(mode_select == 0) {
return cartridge.ram_read(addr & 0x1fff);
} else {
return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
}
}
return 0x00;
}
return 0x00;
}
void Cartridge::MBC1::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x3fff>(addr)) {
rom_select = (data & 0x1f) + ((data & 0x1f) == 0);
return;
}
if(within<0x4000, 0x5fff>(addr)) {
ram_select = data & 0x03;
return;
}
if(within<0x6000, 0x7fff>(addr)) {
mode_select = data & 0x01;
return;
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) {
if(mode_select == 0) {
cartridge.ram_write(addr & 0x1fff, data);
} else {
cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
}
}
return;
}
}
void Cartridge::MBC1::power() {
ram_enable = false;
rom_select = 0x01;
ram_select = 0x00;
mode_select = 0;
}
#endif

View File

@@ -0,0 +1,10 @@
struct MBC1 : MMIO {
bool ram_enable; //0000-1fff
uint8 rom_select; //2000-3fff
uint8 ram_select; //4000-5fff
bool mode_select; //6000-7fff
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
} mbc1;

View File

@@ -0,0 +1,42 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC2::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(within<0xa000, 0xa1ff>(addr)) {
if(ram_enable) return cartridge.ram_read(addr & 0x1ff);
return 0x00;
}
return 0x00;
}
void Cartridge::MBC2::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
if(!(addr & 0x0100)) ram_enable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x3fff>(addr)) {
if( (addr & 0x0100)) rom_select = (data & 0x0f) + ((data & 0x0f) == 0);
return;
}
if(within<0xa000, 0xa1ff>(addr)) {
if(ram_enable) cartridge.ram_write(addr & 0x1ff, data & 0x0f);
return;
}
}
void Cartridge::MBC2::power() {
ram_enable = false;
rom_select = 0x01;
}
#endif

View File

@@ -0,0 +1,8 @@
struct MBC2 : MMIO {
bool ram_enable; //0000-1fff
uint8 rom_select; //2000-3fff
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
} mbc2;

View File

@@ -0,0 +1,120 @@
#ifdef CARTRIDGE_CPP
void Cartridge::MBC3::second() {
if(rtc_halt == false) {
if(++rtc_second >= 60) {
rtc_second = 0;
if(++rtc_minute >= 60) {
rtc_minute = 0;
if(++rtc_hour >= 24) {
rtc_hour = 0;
if(++rtc_day >= 512) {
rtc_day = 0;
rtc_day_carry = true;
}
}
}
}
}
}
uint8 Cartridge::MBC3::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) {
if(ram_select >= 0x00 && ram_select <= 0x03) {
return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
}
if(ram_select == 0x08) return rtc_latch_second;
if(ram_select == 0x09) return rtc_latch_minute;
if(ram_select == 0x0a) return rtc_latch_hour;
if(ram_select == 0x0b) return rtc_latch_day;
if(ram_select == 0x0c) return (rtc_latch_day_carry << 7) | (rtc_latch_day >> 8);
}
return 0x00;
}
return 0x00;
}
void Cartridge::MBC3::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x3fff>(addr)) {
rom_select = (data & 0x7f) + ((data & 0x7f) == 0);
return;
}
if(within<0x4000, 0x5fff>(addr)) {
ram_select = data;
return;
}
if(within<0x6000, 0x7fff>(addr)) {
if(rtc_latch == 0 && data == 1) {
rtc_latch_second = rtc_second;
rtc_latch_minute = rtc_minute;
rtc_latch_hour = rtc_hour;
rtc_latch_day = rtc_day;
rtc_latch_day_carry = rtc_day_carry;
}
rtc_latch = data;
return;
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) {
if(ram_select >= 0x00 && ram_select <= 0x03) {
cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
} else if(ram_select == 0x08) {
if(data >= 60) data = 0;
rtc_second = data;
} else if(ram_select == 0x09) {
if(data >= 60) data = 0;
rtc_minute = data;
} else if(ram_select == 0x0a) {
if(data >= 24) data = 0;
rtc_hour = data;
} else if(ram_select == 0x0b) {
rtc_day = (rtc_day & 0x0100) | data;
} else if(ram_select == 0x0c) {
rtc_day = ((data & 1) << 8) | (rtc_day & 0xff);
rtc_halt = data & 0x40;
rtc_day_carry = data & 0x80;
}
}
return;
}
}
void Cartridge::MBC3::power() {
ram_enable = false;
rom_select = 0x01;
ram_select = 0x00;
rtc_latch = 0;
rtc_halt = true;
rtc_second = 0;
rtc_minute = 0;
rtc_hour = 0;
rtc_day = 0;
rtc_day_carry = false;
rtc_latch_second = 0;
rtc_latch_minute = 0;
rtc_latch_hour = 0;
rtc_latch_day = 0;
rtc_latch_day_carry = false;
}
#endif

View File

@@ -0,0 +1,24 @@
struct MBC3 : MMIO {
bool ram_enable; //0000-1fff
uint8 rom_select; //2000-3fff
uint8 ram_select; //4000-5fff
bool rtc_latch; //6000-7fff
bool rtc_halt;
unsigned rtc_second;
unsigned rtc_minute;
unsigned rtc_hour;
unsigned rtc_day;
bool rtc_day_carry;
unsigned rtc_latch_second;
unsigned rtc_latch_minute;
unsigned rtc_latch_hour;
unsigned rtc_latch_day;
unsigned rtc_latch_day_carry;
void second();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
} mbc3;

View File

@@ -0,0 +1,53 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC5::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
return 0x00;
}
return 0x00;
}
void Cartridge::MBC5::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x2fff>(addr)) {
rom_select = (rom_select & 0x0100) | data;
return;
}
if(within<0x3000, 0x3fff>(addr)) {
rom_select = ((data & 1) << 8) | (rom_select & 0x00ff);
return;
}
if(within<0x4000, 0x5fff>(addr)) {
ram_select = data & 0x0f;
return;
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
return;
}
}
void Cartridge::MBC5::power() {
ram_enable = false;
rom_select = 0x001;
ram_select = 0x00;
}
#endif

View File

@@ -0,0 +1,9 @@
struct MBC5 : MMIO {
bool ram_enable; //0000-1fff
uint16 rom_select; //2000-2fff + 3000-3fff
uint8 ram_select; //4000-5fff
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
} mbc5;

View File

@@ -0,0 +1,65 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MMM01::mmio_read(uint16 addr) {
if(within<0x0000, 0x7fff>(addr)) {
if(rom_mode == 0) return cartridge.rom_read(addr);
}
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(0x8000 + (rom_base << 14) + (addr & 0x3fff));
}
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read(0x8000 + (rom_base << 14) + (rom_select << 14) + (addr & 0x3fff));
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) return cartridge.ram_read((ram_select << 13) + (addr & 0x1fff));
return 0x00;
}
return 0x00;
}
void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
if(rom_mode == 0) {
rom_mode = 1;
} else {
ram_enable = (data & 0x0f) == 0x0a;
}
}
if(within<0x2000, 0x3fff>(addr)) {
if(rom_mode == 0) {
rom_base = data & 0x3f;
} else {
rom_select = data;
}
}
if(within<0x4000, 0x5fff>(addr)) {
if(rom_mode == 1) {
ram_select = data;
}
}
if(within<0x6000, 0x7fff>(addr)) {
//unknown purpose
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) cartridge.ram_write((ram_select << 13) + (addr & 0x1fff), data);
}
}
void Cartridge::MMM01::power() {
rom_mode = 0;
rom_base = 0x00;
ram_enable = false;
rom_select = 0x01;
ram_select = 0x00;
}
#endif

View File

@@ -0,0 +1,12 @@
struct MMM01 : MMIO {
bool rom_mode;
uint8 rom_base;
bool ram_enable;
uint8 rom_select;
uint8 ram_select;
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
} mmm01;

View File

@@ -0,0 +1,53 @@
#ifdef CARTRIDGE_CPP
void Cartridge::serialize(serializer &s) {
if(info.battery) s.array(ramdata, ramsize);
s.integer(bootrom_enable);
s.integer(mbc1.ram_enable);
s.integer(mbc1.rom_select);
s.integer(mbc1.ram_select);
s.integer(mbc1.mode_select);
s.integer(mbc2.ram_enable);
s.integer(mbc2.rom_select);
s.integer(mbc3.ram_enable);
s.integer(mbc3.rom_select);
s.integer(mbc3.ram_select);
s.integer(mbc3.rtc_latch);
s.integer(mbc3.rtc_halt);
s.integer(mbc3.rtc_second);
s.integer(mbc3.rtc_minute);
s.integer(mbc3.rtc_hour);
s.integer(mbc3.rtc_day);
s.integer(mbc3.rtc_day_carry);
s.integer(mbc3.rtc_latch_second);
s.integer(mbc3.rtc_latch_minute);
s.integer(mbc3.rtc_latch_hour);
s.integer(mbc3.rtc_latch_day);
s.integer(mbc3.rtc_latch_day_carry);
s.integer(mbc5.ram_enable);
s.integer(mbc5.rom_select);
s.integer(mbc5.ram_select);
s.integer(mmm01.rom_mode);
s.integer(mmm01.rom_base);
s.integer(mmm01.ram_enable);
s.integer(mmm01.rom_select);
s.integer(mmm01.ram_select);
s.integer(huc1.ram_enable);
s.integer(huc1.rom_select);
s.integer(huc1.ram_select);
s.integer(huc3.ram_enable);
s.integer(huc3.rom_select);
s.integer(huc3.ram_select);
}
#endif

674
bsnes/gameboy/cpu/core/core.cpp Executable file
View File

@@ -0,0 +1,674 @@
#ifdef CPU_CPP
#include "table.cpp"
#include "disassembler.cpp"
void CPU::op_xx() {
}
void CPU::op_cb() {
uint8 opcode = op_read(r[PC]++);
(this->*opcode_table_cb[opcode])();
}
//8-bit load commands
template<unsigned x, unsigned y> void CPU::op_ld_r_r() {
r[x] = r[y];
}
template<unsigned x> void CPU::op_ld_r_n() {
r[x] = op_read(r[PC]++);
}
template<unsigned x> void CPU::op_ld_r_hl() {
r[x] = op_read(r[HL]);
}
template<unsigned x> void CPU::op_ld_hl_r() {
op_write(r[HL], r[x]);
}
void CPU::op_ld_hl_n() {
op_write(r[HL], op_read(r[PC]++));
}
template<unsigned x> void CPU::op_ld_a_rr() {
r[A] = op_read(r[x]);
}
void CPU::op_ld_a_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
r[A] = op_read((hi << 8) | (lo << 0));
}
template<unsigned x> void CPU::op_ld_rr_a() {
op_write(r[x], r[A]);
}
void CPU::op_ld_nn_a() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
op_write((hi << 8) | (lo << 0), r[A]);
}
void CPU::op_ld_a_ffn() {
r[A] = op_read(0xff00 + op_read(r[PC]++));
}
void CPU::op_ld_ffn_a() {
op_write(0xff00 + op_read(r[PC]++), r[A]);
}
void CPU::op_ld_a_ffc() {
r[A] = op_read(0xff00 + r[C]);
}
void CPU::op_ld_ffc_a() {
op_write(0xff00 + r[C], r[A]);
}
void CPU::op_ldi_hl_a() {
op_write(r[HL], r[A]);
r[HL]++;
}
void CPU::op_ldi_a_hl() {
r[A] = op_read(r[HL]);
r[HL]++;
}
void CPU::op_ldd_hl_a() {
op_write(r[HL], r[A]);
r[HL]--;
}
void CPU::op_ldd_a_hl() {
r[A] = op_read(r[HL]);
r[HL]--;
}
//16-bit load commands
template<unsigned x> void CPU::op_ld_rr_nn() {
r[x] = op_read(r[PC]++) << 0;
r[x] |= op_read(r[PC]++) << 8;
}
void CPU::op_ld_nn_sp() {
uint16 addr = op_read(r[PC]++) << 0;
addr |= op_read(r[PC]++) << 8;
op_write(addr + 0, r[SP] >> 0);
op_write(addr + 1, r[SP] >> 8);
}
void CPU::op_ld_sp_hl() {
r[SP] = r[HL];
op_io();
}
template<unsigned x> void CPU::op_push_rr() {
op_write(--r[SP], r[x] >> 8);
op_write(--r[SP], r[x] >> 0);
op_io();
}
template<unsigned x> void CPU::op_pop_rr() {
r[x] = op_read(r[SP]++) << 0;
r[x] |= op_read(r[SP]++) << 8;
}
//8-bit arithmetic commands
void CPU::opi_add_a(uint8 x) {
uint16 rh = r[A] + x;
uint16 rl = (r[A] & 0x0f) + (x & 0x0f);
r[A] = rh;
r.f.z = (uint8)rh == 0;
r.f.n = 0;
r.f.h = rl > 0x0f;
r.f.c = rh > 0xff;
}
template<unsigned x> void CPU::op_add_a_r() { opi_add_a(r[x]); }
void CPU::op_add_a_n() { opi_add_a(op_read(r[PC]++)); }
void CPU::op_add_a_hl() { opi_add_a(op_read(r[HL])); }
void CPU::opi_adc_a(uint8 x) {
uint16 rh = r[A] + x + r.f.c;
uint16 rl = (r[A] & 0x0f) + (x & 0x0f) + r.f.c;
r[A] = rh;
r.f.z = (uint8)rh == 0;
r.f.n = 0;
r.f.h = rl > 0x0f;
r.f.c = rh > 0xff;
}
template<unsigned x> void CPU::op_adc_a_r() { opi_adc_a(r[x]); }
void CPU::op_adc_a_n() { opi_adc_a(op_read(r[PC]++)); }
void CPU::op_adc_a_hl() { opi_adc_a(op_read(r[HL])); }
void CPU::opi_sub_a(uint8 x) {
uint16 rh = r[A] - x;
uint16 rl = (r[A] & 0x0f) - (x & 0x0f);
r[A] = rh;
r.f.z = (uint8)rh == 0;
r.f.n = 1;
r.f.h = rl > 0x0f;
r.f.c = rh > 0xff;
}
template<unsigned x> void CPU::op_sub_a_r() { opi_sub_a(r[x]); }
void CPU::op_sub_a_n() { opi_sub_a(op_read(r[PC]++)); }
void CPU::op_sub_a_hl() { opi_sub_a(op_read(r[HL])); }
void CPU::opi_sbc_a(uint8 x) {
uint16 rh = r[A] - x - r.f.c;
uint16 rl = (r[A] & 0x0f) - (x & 0x0f) - r.f.c;
r[A] = rh;
r.f.z = (uint8)rh == 0;
r.f.n = 1;
r.f.h = rl > 0x0f;
r.f.c = rh > 0xff;
}
template<unsigned x> void CPU::op_sbc_a_r() { opi_sbc_a(r[x]); }
void CPU::op_sbc_a_n() { opi_sbc_a(op_read(r[PC]++)); }
void CPU::op_sbc_a_hl() { opi_sbc_a(op_read(r[HL])); }
void CPU::opi_and_a(uint8 x) {
r[A] &= x;
r.f.z = r[A] == 0;
r.f.n = 0;
r.f.h = 1;
r.f.c = 0;
}
template<unsigned x> void CPU::op_and_a_r() { opi_and_a(r[x]); }
void CPU::op_and_a_n() { opi_and_a(op_read(r[PC]++)); }
void CPU::op_and_a_hl() { opi_and_a(op_read(r[HL])); }
void CPU::opi_xor_a(uint8 x) {
r[A] ^= x;
r.f.z = r[A] == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = 0;
}
template<unsigned x> void CPU::op_xor_a_r() { opi_xor_a(r[x]); }
void CPU::op_xor_a_n() { opi_xor_a(op_read(r[PC]++)); }
void CPU::op_xor_a_hl() { opi_xor_a(op_read(r[HL])); }
void CPU::opi_or_a(uint8 x) {
r[A] |= x;
r.f.z = r[A] == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = 0;
}
template<unsigned x> void CPU::op_or_a_r() { opi_or_a(r[x]); }
void CPU::op_or_a_n() { opi_or_a(op_read(r[PC]++)); }
void CPU::op_or_a_hl() { opi_or_a(op_read(r[HL])); }
void CPU::opi_cp_a(uint8 x) {
uint16 rh = r[A] - x;
uint16 rl = (r[A] & 0x0f) - (x & 0x0f);
r.f.z = (uint8)rh == 0;
r.f.n = 1;
r.f.h = rl > 0x0f;
r.f.c = rh > 0xff;
}
template<unsigned x> void CPU::op_cp_a_r() { opi_cp_a(r[x]); }
void CPU::op_cp_a_n() { opi_cp_a(op_read(r[PC]++)); }
void CPU::op_cp_a_hl() { opi_cp_a(op_read(r[HL])); }
template<unsigned x> void CPU::op_inc_r() {
r[x]++;
r.f.z = r[x] == 0;
r.f.n = 0;
r.f.h = (r[x] & 0x0f) == 0x00;
}
void CPU::op_inc_hl() {
uint8 n = op_read(r[HL]);
op_write(r[HL], ++n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = (n & 0x0f) == 0x00;
}
template<unsigned x> void CPU::op_dec_r() {
r[x]--;
r.f.z = r[x] == 0;
r.f.n = 1;
r.f.h = (r[x] & 0x0f) == 0x0f;
}
void CPU::op_dec_hl() {
uint8 n = op_read(r[HL]);
op_write(r[HL], --n);
r.f.z = n == 0;
r.f.n = 1;
r.f.h = (n & 0x0f) == 0x0f;
}
void CPU::op_daa() {
uint16 a = r[A];
if(r.f.n == 0) {
if(r.f.h || (a & 0x0f) > 0x09) a += 0x06;
if(r.f.c || (a ) > 0x9f) a += 0x60;
} else {
if(r.f.h) {
a -= 0x06;
if(r.f.c == 0) a &= 0xff;
}
if(r.f.c) a -= 0x60;
}
r[A] = a;
r.f.z = r[A] == 0;
r.f.h = 0;
r.f.c |= a & 0x100;
}
void CPU::op_cpl() {
r[A] ^= 0xff;
r.f.n = 1;
r.f.h = 1;
}
//16-bit arithmetic commands
template<unsigned x> void CPU::op_add_hl_rr() {
op_io();
uint32 rb = (r[HL] + r[x]);
uint32 rn = (r[HL] & 0xfff) + (r[x] & 0xfff);
r[HL] = rb;
r.f.n = 0;
r.f.h = rn > 0x0fff;
r.f.c = rb > 0xffff;
}
template<unsigned x> void CPU::op_inc_rr() {
op_io();
r[x]++;
}
template<unsigned x> void CPU::op_dec_rr() {
op_io();
r[x]--;
}
void CPU::op_add_sp_n() {
op_io();
op_io();
signed n = (int8)op_read(r[PC]++);
r.f.z = 0;
r.f.n = 0;
r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f;
r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff;
r[SP] += n;
}
void CPU::op_ld_hl_sp_n() {
op_io();
signed n = (int8)op_read(r[PC]++);
r.f.z = 0;
r.f.n = 0;
r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f;
r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff;
r[HL] = r[SP] + n;
}
//rotate/shift commands
void CPU::op_rlca() {
r[A] = (r[A] << 1) | (r[A] >> 7);
r.f.z = 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = r[A] & 0x01;
}
void CPU::op_rla() {
bool c = r[A] & 0x80;
r[A] = (r[A] << 1) | (r.f.c << 0);
r.f.z = 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
void CPU::op_rrca() {
r[A] = (r[A] >> 1) | (r[A] << 7);
r.f.z = 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = r[A] & 0x80;
}
void CPU::op_rra() {
bool c = r[A] & 0x01;
r[A] = (r[A] >> 1) | (r.f.c << 7);
r.f.z = 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
template<unsigned x> void CPU::op_rlc_r() {
r[x] = (r[x] << 1) | (r[x] >> 7);
r.f.z = r[x] == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = r[x] & 0x01;
}
void CPU::op_rlc_hl() {
uint8 n = op_read(r[HL]);
n = (n << 1) | (n >> 7);
op_write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = n & 0x01;
}
template<unsigned x> void CPU::op_rl_r() {
bool c = r[x] & 0x80;
r[x] = (r[x] << 1) | (r.f.c << 0);
r.f.z = r[x] == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
void CPU::op_rl_hl() {
uint8 n = op_read(r[HL]);
bool c = n & 0x80;
n = (n << 1) | (r.f.c << 0);
op_write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
template<unsigned x> void CPU::op_rrc_r() {
r[x] = (r[x] >> 1) | (r[x] << 7);
r.f.z = r[x] == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = r[x] & 0x80;
}
void CPU::op_rrc_hl() {
uint8 n = op_read(r[HL]);
n = (n >> 1) | (n << 7);
op_write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = n & 0x80;
}
template<unsigned x> void CPU::op_rr_r() {
bool c = r[x] & 0x01;
r[x] = (r[x] >> 1) | (r.f.c << 7);
r.f.z = r[x] == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
void CPU::op_rr_hl() {
uint8 n = op_read(r[HL]);
bool c = n & 0x01;
n = (n >> 1) | (r.f.c << 7);
op_write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
template<unsigned x> void CPU::op_sla_r() {
bool c = r[x] & 0x80;
r[x] <<= 1;
r.f.z = r[x] == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
void CPU::op_sla_hl() {
uint8 n = op_read(r[HL]);
bool c = n & 0x80;
n <<= 1;
op_write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
template<unsigned x> void CPU::op_swap_r() {
r[x] = (r[x] << 4) | (r[x] >> 4);
r.f.z = r[x] == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = 0;
}
void CPU::op_swap_hl() {
uint8 n = op_read(r[HL]);
n = (n << 4) | (n >> 4);
op_write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = 0;
}
template<unsigned x> void CPU::op_sra_r() {
bool c = r[x] & 0x01;
r[x] = (int8)r[x] >> 1;
r.f.z = r[x] == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
void CPU::op_sra_hl() {
uint8 n = op_read(r[HL]);
bool c = n & 0x01;
n = (int8)n >> 1;
op_write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
template<unsigned x> void CPU::op_srl_r() {
bool c = r[x] & 0x01;
r[x] >>= 1;
r.f.z = r[x] == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
void CPU::op_srl_hl() {
uint8 n = op_read(r[HL]);
bool c = n & 0x01;
n >>= 1;
op_write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
//single-bit commands
template<unsigned b, unsigned x> void CPU::op_bit_n_r() {
r.f.z = (r[x] & (1 << b)) == 0;
r.f.n = 0;
r.f.h = 1;
}
template<unsigned b> void CPU::op_bit_n_hl() {
uint8 n = op_read(r[HL]);
r.f.z = (n & (1 << b)) == 0;
r.f.n = 0;
r.f.h = 1;
}
template<unsigned b, unsigned x> void CPU::op_set_n_r() {
r[x] |= 1 << b;
}
template<unsigned b> void CPU::op_set_n_hl() {
uint8 n = op_read(r[HL]);
n |= 1 << b;
op_write(r[HL], n);
}
template<unsigned b, unsigned x> void CPU::op_res_n_r() {
r[x] &= ~(1 << b);
}
template<unsigned b> void CPU::op_res_n_hl() {
uint8 n = op_read(r[HL]);
n &= ~(1 << b);
op_write(r[HL], n);
}
//control commands
void CPU::op_ccf() {
r.f.n = 0;
r.f.h = 0;
r.f.c = !r.f.c;
}
void CPU::op_scf() {
r.f.n = 0;
r.f.h = 0;
r.f.c = 1;
}
void CPU::op_nop() {
}
void CPU::op_halt() {
status.halt = true;
while(status.halt == true) op_io();
}
void CPU::op_stop() {
status.stop = true;
while(status.stop == true) op_io();
}
void CPU::op_di() {
status.ime = 0;
}
void CPU::op_ei() {
status.ime = 1;
}
//jump commands
void CPU::op_jp_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
r[PC] = (hi << 8) | (lo << 0);
op_io();
}
void CPU::op_jp_hl() {
r[PC] = r[HL];
}
template<unsigned x, bool y> void CPU::op_jp_f_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
if(r.f[x] == y) {
r[PC] = (hi << 8) | (lo << 0);
op_io();
}
}
void CPU::op_jr_n() {
int8 n = op_read(r[PC]++);
r[PC] += n;
op_io();
}
template<unsigned x, bool y> void CPU::op_jr_f_n() {
int8 n = op_read(r[PC]++);
if(r.f[x] == y) {
r[PC] += n;
op_io();
}
}
void CPU::op_call_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0);
r[PC] = (hi << 8) | (lo << 0);
op_io();
}
template<unsigned x, bool y> void CPU::op_call_f_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
if(r.f[x] == y) {
op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0);
r[PC] = (hi << 8) | (lo << 0);
op_io();
}
}
void CPU::op_ret() {
uint8 lo = op_read(r[SP]++);
uint8 hi = op_read(r[SP]++);
r[PC] = (hi << 8) | (lo << 0);
op_io();
}
template<unsigned x, bool y> void CPU::op_ret_f() {
op_io();
if(r.f[x] == y) {
uint8 lo = op_read(r[SP]++);
uint8 hi = op_read(r[SP]++);
r[PC] = (hi << 8) | (lo << 0);
op_io();
}
}
void CPU::op_reti() {
uint8 lo = op_read(r[SP]++);
uint8 hi = op_read(r[SP]++);
r[PC] = (hi << 8) | (lo << 0);
op_io();
status.ime = 1;
}
template<unsigned n> void CPU::op_rst_n() {
op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0);
r[PC] = n;
op_io();
}
#endif

145
bsnes/gameboy/cpu/core/core.hpp Executable file
View File

@@ -0,0 +1,145 @@
#include "registers.hpp"
void (CPU::*opcode_table[256])();
void (CPU::*opcode_table_cb[256])();
void initialize_opcode_table();
void op_xx();
void op_cb();
//8-bit load commands
template<unsigned x, unsigned y> void op_ld_r_r();
template<unsigned x> void op_ld_r_n();
template<unsigned x> void op_ld_r_hl();
template<unsigned x> void op_ld_hl_r();
void op_ld_hl_n();
template<unsigned x> void op_ld_a_rr();
void op_ld_a_nn();
template<unsigned x> void op_ld_rr_a();
void op_ld_nn_a();
void op_ld_a_ffn();
void op_ld_ffn_a();
void op_ld_a_ffc();
void op_ld_ffc_a();
void op_ldi_hl_a();
void op_ldi_a_hl();
void op_ldd_hl_a();
void op_ldd_a_hl();
//16-bit load commands
template<unsigned x> void op_ld_rr_nn();
void op_ld_nn_sp();
void op_ld_sp_hl();
template<unsigned x> void op_push_rr();
template<unsigned x> void op_pop_rr();
//8-bit arithmetic commands
void opi_add_a(uint8 x);
template<unsigned x> void op_add_a_r();
void op_add_a_n();
void op_add_a_hl();
void opi_adc_a(uint8 x);
template<unsigned x> void op_adc_a_r();
void op_adc_a_n();
void op_adc_a_hl();
void opi_sub_a(uint8 x);
template<unsigned x> void op_sub_a_r();
void op_sub_a_n();
void op_sub_a_hl();
void opi_sbc_a(uint8 x);
template<unsigned x> void op_sbc_a_r();
void op_sbc_a_n();
void op_sbc_a_hl();
void opi_and_a(uint8 x);
template<unsigned x> void op_and_a_r();
void op_and_a_n();
void op_and_a_hl();
void opi_xor_a(uint8 x);
template<unsigned x> void op_xor_a_r();
void op_xor_a_n();
void op_xor_a_hl();
void opi_or_a(uint8 x);
template<unsigned x> void op_or_a_r();
void op_or_a_n();
void op_or_a_hl();
void opi_cp_a(uint8 x);
template<unsigned x> void op_cp_a_r();
void op_cp_a_n();
void op_cp_a_hl();
template<unsigned x> void op_inc_r();
void op_inc_hl();
template<unsigned x> void op_dec_r();
void op_dec_hl();
void op_daa();
void op_cpl();
//16-bit arithmetic commands
template<unsigned x> void op_add_hl_rr();
template<unsigned x> void op_inc_rr();
template<unsigned x> void op_dec_rr();
void op_add_sp_n();
void op_ld_hl_sp_n();
//rotate/shift commands
void op_rlca();
void op_rla();
void op_rrca();
void op_rra();
template<unsigned x> void op_rlc_r();
void op_rlc_hl();
template<unsigned x> void op_rl_r();
void op_rl_hl();
template<unsigned x> void op_rrc_r();
void op_rrc_hl();
template<unsigned x> void op_rr_r();
void op_rr_hl();
template<unsigned x> void op_sla_r();
void op_sla_hl();
template<unsigned x> void op_swap_r();
void op_swap_hl();
template<unsigned x> void op_sra_r();
void op_sra_hl();
template<unsigned x> void op_srl_r();
void op_srl_hl();
//single-bit commands
template<unsigned b, unsigned x> void op_bit_n_r();
template<unsigned b> void op_bit_n_hl();
template<unsigned b, unsigned x> void op_set_n_r();
template<unsigned b> void op_set_n_hl();
template<unsigned b, unsigned x> void op_res_n_r();
template<unsigned b> void op_res_n_hl();
//control commands
void op_ccf();
void op_scf();
void op_nop();
void op_halt();
void op_stop();
void op_di();
void op_ei();
//jump commands
void op_jp_nn();
void op_jp_hl();
template<unsigned x, bool y> void op_jp_f_nn();
void op_jr_n();
template<unsigned x, bool y> void op_jr_f_n();
void op_call_nn();
template<unsigned x, bool y> void op_call_f_nn();
void op_ret();
template<unsigned x, bool y> void op_ret_f();
void op_reti();
template<unsigned n> void op_rst_n();
//disassembler.cpp
string disassemble(uint16 pc);
string disassemble_opcode(uint16 pc);
string disassemble_opcode_cb(uint16 pc);

View File

@@ -0,0 +1,560 @@
#ifdef CPU_CPP
string CPU::disassemble(uint16 pc) {
char output[80];
memset(output, ' ', sizeof output);
output[79] = 0;
string opcode = disassemble_opcode(pc);
string registers = {
" AF:", hex<4>(r[AF]),
" BC:", hex<4>(r[BC]),
" DE:", hex<4>(r[DE]),
" HL:", hex<4>(r[HL]),
" SP:", hex<4>(r[SP])
};
memcpy(output + 0, hex<4>(pc), 4);
memcpy(output + 6, opcode, opcode.length());
memcpy(output + 23, registers, registers.length());
output[63] = 0;
return output;
}
string CPU::disassemble_opcode(uint16 pc) {
uint8 opcode = bus.read(pc);
uint8 p0 = bus.read(pc + 1);
uint8 p1 = bus.read(pc + 2);
uint8 p2 = bus.read(pc + 3);
switch(opcode) {
case 0x00: return { "nop" };
case 0x01: return { "ld bc,$", hex<2>(p1), hex<2>(p0) };
case 0x02: return { "ld (bc),a" };
case 0x03: return { "inc bc" };
case 0x04: return { "inc b" };
case 0x05: return { "dec b" };
case 0x06: return { "ld b,$", hex<2>(p0) };
case 0x07: return { "rlc a" };
case 0x08: return { "ld ($", hex<2>(p1), hex<2>(p0), "),sp" };
case 0x09: return { "add hl,bc" };
case 0x0a: return { "ld a,(bc)" };
case 0x0b: return { "dec bc" };
case 0x0c: return { "inc c" };
case 0x0d: return { "dec c" };
case 0x0e: return { "ld c,$", hex<2>(p0) };
case 0x0f: return { "rrc a" };
case 0x10: return { "stop" };
case 0x11: return { "ld de,$", hex<2>(p1), hex<2>(p0) };
case 0x12: return { "ld (de),a" };
case 0x13: return { "inc de" };
case 0x14: return { "inc d" };
case 0x15: return { "dec d" };
case 0x16: return { "ld d,$", hex<2>(p0) };
case 0x17: return { "rl a" };
case 0x18: return { "jr $", hex<4>(r[PC] + 2 + (int8)p0) };
case 0x19: return { "add hl,de" };
case 0x1a: return { "ld a,(de)" };
case 0x1b: return { "dec de" };
case 0x1c: return { "inc e" };
case 0x1d: return { "dec e" };
case 0x1e: return { "ld e,$", hex<2>(p0) };
case 0x1f: return { "rr a" };
case 0x20: return { "jr nz,$", hex<4>(r[PC] + 2 + (int8)p0) };
case 0x21: return { "ld hl,$", hex<2>(p1), hex<2>(p0) };
case 0x22: return { "ldi (hl),a" };
case 0x23: return { "inc hl" };
case 0x24: return { "inc h" };
case 0x25: return { "dec h" };
case 0x26: return { "ld h,$", hex<2>(p0) };
case 0x27: return { "daa" };
case 0x28: return { "jr z,$", hex<4>(r[PC] + 2 + (int8)p0) };
case 0x29: return { "add hl,hl" };
case 0x2a: return { "ldi a,(hl)" };
case 0x2b: return { "dec hl" };
case 0x2c: return { "inc l" };
case 0x2d: return { "dec l" };
case 0x2e: return { "ld l,$", hex<2>(p0) };
case 0x2f: return { "cpl" };
case 0x30: return { "jr nc,$", hex<4>(r[PC] + 2 + (int8)p0) };
case 0x31: return { "ld sp,$", hex<2>(p1), hex<2>(p0) };
case 0x32: return { "ldd (hl),a" };
case 0x33: return { "inc sp" };
case 0x34: return { "inc (hl)" };
case 0x35: return { "dec (hl)" };
case 0x36: return { "ld (hl),$", hex<2>(p0) };
case 0x37: return { "scf" };
case 0x38: return { "jr c,$", hex<4>(r[PC] + 2 + (int8)p0) };
case 0x39: return { "add hl,sp" };
case 0x3a: return { "ldd a,(hl)" };
case 0x3b: return { "dec sp" };
case 0x3c: return { "inc a" };
case 0x3d: return { "dec a" };
case 0x3e: return { "ld a,$", hex<2>(p0) };
case 0x3f: return { "ccf" };
case 0x40: return { "ld b,b" };
case 0x41: return { "ld b,c" };
case 0x42: return { "ld b,d" };
case 0x43: return { "ld b,e" };
case 0x44: return { "ld b,h" };
case 0x45: return { "ld b,l" };
case 0x46: return { "ld b,(hl)" };
case 0x47: return { "ld b,a" };
case 0x48: return { "ld c,b" };
case 0x49: return { "ld c,c" };
case 0x4a: return { "ld c,d" };
case 0x4b: return { "ld c,e" };
case 0x4c: return { "ld c,h" };
case 0x4d: return { "ld c,l" };
case 0x4e: return { "ld c,(hl)" };
case 0x4f: return { "ld c,a" };
case 0x50: return { "ld d,b" };
case 0x51: return { "ld d,c" };
case 0x52: return { "ld d,d" };
case 0x53: return { "ld d,e" };
case 0x54: return { "ld d,h" };
case 0x55: return { "ld d,l" };
case 0x56: return { "ld d,(hl)" };
case 0x57: return { "ld d,a" };
case 0x58: return { "ld e,b" };
case 0x59: return { "ld e,c" };
case 0x5a: return { "ld e,d" };
case 0x5b: return { "ld e,e" };
case 0x5c: return { "ld e,h" };
case 0x5d: return { "ld e,l" };
case 0x5e: return { "ld e,(hl)" };
case 0x5f: return { "ld e,a" };
case 0x60: return { "ld h,b" };
case 0x61: return { "ld h,c" };
case 0x62: return { "ld h,d" };
case 0x63: return { "ld h,e" };
case 0x64: return { "ld h,h" };
case 0x65: return { "ld h,l" };
case 0x66: return { "ld h,(hl)" };
case 0x67: return { "ld h,a" };
case 0x68: return { "ld l,b" };
case 0x69: return { "ld l,c" };
case 0x6a: return { "ld l,d" };
case 0x6b: return { "ld l,e" };
case 0x6c: return { "ld l,h" };
case 0x6d: return { "ld l,l" };
case 0x6e: return { "ld l,(hl)" };
case 0x6f: return { "ld l,a" };
case 0x70: return { "ld (hl),b" };
case 0x71: return { "ld (hl),c" };
case 0x72: return { "ld (hl),d" };
case 0x73: return { "ld (hl),e" };
case 0x74: return { "ld (hl),h" };
case 0x75: return { "ld (hl),l" };
case 0x76: return { "halt" };
case 0x77: return { "ld (hl),a" };
case 0x78: return { "ld a,b" };
case 0x79: return { "ld a,c" };
case 0x7a: return { "ld a,d" };
case 0x7b: return { "ld a,e" };
case 0x7c: return { "ld a,h" };
case 0x7d: return { "ld a,l" };
case 0x7e: return { "ld a,(hl)" };
case 0x7f: return { "ld a,a" };
case 0x80: return { "add a,b" };
case 0x81: return { "add a,c" };
case 0x82: return { "add a,d" };
case 0x83: return { "add a,e" };
case 0x84: return { "add a,h" };
case 0x85: return { "add a,l" };
case 0x86: return { "add a,(hl)" };
case 0x87: return { "add a,a" };
case 0x88: return { "adc a,b" };
case 0x89: return { "adc a,c" };
case 0x8a: return { "adc a,d" };
case 0x8b: return { "adc a,e" };
case 0x8c: return { "adc a,h" };
case 0x8d: return { "adc a,l" };
case 0x8e: return { "adc a,(hl)" };
case 0x8f: return { "adc a,a" };
case 0x90: return { "sub a,b" };
case 0x91: return { "sub a,c" };
case 0x92: return { "sub a,d" };
case 0x93: return { "sub a,e" };
case 0x94: return { "sub a,h" };
case 0x95: return { "sub a,l" };
case 0x96: return { "sub a,(hl)" };
case 0x97: return { "sub a,a" };
case 0x98: return { "sbc a,b" };
case 0x99: return { "sbc a,c" };
case 0x9a: return { "sbc a,d" };
case 0x9b: return { "sbc a,e" };
case 0x9c: return { "sbc a,h" };
case 0x9d: return { "sbc a,l" };
case 0x9e: return { "sbc a,(hl)" };
case 0x9f: return { "sbc a,a" };
case 0xa0: return { "and a,b" };
case 0xa1: return { "and a,c" };
case 0xa2: return { "and a,d" };
case 0xa3: return { "and a,e" };
case 0xa4: return { "and a,h" };
case 0xa5: return { "and a,l" };
case 0xa6: return { "and a,(hl)" };
case 0xa7: return { "and a,a" };
case 0xa8: return { "xor a,b" };
case 0xa9: return { "xor a,c" };
case 0xaa: return { "xor a,d" };
case 0xab: return { "xor a,e" };
case 0xac: return { "xor a,h" };
case 0xad: return { "xor a,l" };
case 0xae: return { "xor a,(hl)" };
case 0xaf: return { "xor a,a" };
case 0xb0: return { "or a,b" };
case 0xb1: return { "or a,c" };
case 0xb2: return { "or a,d" };
case 0xb3: return { "or a,e" };
case 0xb4: return { "or a,h" };
case 0xb5: return { "or a,l" };
case 0xb6: return { "or a,(hl)" };
case 0xb7: return { "or a,a" };
case 0xb8: return { "cp a,b" };
case 0xb9: return { "cp a,c" };
case 0xba: return { "cp a,d" };
case 0xbb: return { "cp a,e" };
case 0xbc: return { "cp a,h" };
case 0xbd: return { "cp a,l" };
case 0xbe: return { "cp a,(hl)" };
case 0xbf: return { "cp a,a" };
case 0xc0: return { "ret nz" };
case 0xc1: return { "pop bc" };
case 0xc2: return { "jp nz,$", hex<2>(p1), hex<2>(p0) };
case 0xc3: return { "jp $", hex<2>(p1), hex<2>(p0) };
case 0xc4: return { "call nz,$", hex<2>(p1), hex<2>(p0) };
case 0xc5: return { "push bc" };
case 0xc6: return { "add a,$", hex<2>(p0) };
case 0xc7: return { "rst $0000" };
case 0xc8: return { "ret z" };
case 0xc9: return { "ret" };
case 0xca: return { "jp z,$", hex<2>(p1), hex<2>(p0) };
case 0xcb: return disassemble_opcode_cb(pc + 1);
case 0xcc: return { "call z,$", hex<2>(p1), hex<2>(p0) };
case 0xcd: return { "call $", hex<2>(p1), hex<2>(p0) };
case 0xce: return { "adc a,$", hex<2>(p0) };
case 0xcf: return { "rst $0008" };
case 0xd0: return { "ret nc" };
case 0xd1: return { "pop de" };
case 0xd2: return { "jp nc,$", hex<2>(p1), hex<2>(p0) };
case 0xd3: return { "xx" };
case 0xd4: return { "call nc,$", hex<2>(p1), hex<2>(p0) };
case 0xd5: return { "push de" };
case 0xd6: return { "sub a,$", hex<2>(p0) };
case 0xd7: return { "rst $0010" };
case 0xd8: return { "ret c" };
case 0xd9: return { "reti" };
case 0xda: return { "jp c,$", hex<2>(p1), hex<2>(p0) };
case 0xdb: return { "xx" };
case 0xdc: return { "call c,$", hex<2>(p1), hex<2>(p0) };
case 0xdd: return { "xx" };
case 0xde: return { "sbc a,$", hex<2>(p0) };
case 0xdf: return { "rst $0018" };
case 0xe0: return { "ld ($ff", hex<2>(p0), "),a" };
case 0xe1: return { "pop hl" };
case 0xe2: return { "ld ($ff00+c),a" };
case 0xe3: return { "xx" };
case 0xe4: return { "xx" };
case 0xe5: return { "push hl" };
case 0xe6: return { "and a,$", hex<2>(p0) };
case 0xe7: return { "rst $0020" };
case 0xe8: return { "add sp,$", hex<4>((int8)p0) };
case 0xe9: return { "jp hl" };
case 0xea: return { "ld ($", hex<2>(p1), hex<2>(p0), "),a" };
case 0xeb: return { "xx" };
case 0xec: return { "xx" };
case 0xed: return { "xx" };
case 0xee: return { "xor a,$", hex<2>(p0) };
case 0xef: return { "rst $0028" };
case 0xf0: return { "ld a,($ff", hex<2>(p0), ")" };
case 0xf1: return { "pop af" };
case 0xf2: return { "ld a,($ff00+c)" };
case 0xf3: return { "di" };
case 0xf4: return { "xx" };
case 0xf5: return { "push af" };
case 0xf6: return { "or a,$", hex<2>(p0) };
case 0xf7: return { "rst $0030" };
case 0xf8: return { "ld hl,sp+$", hex<4>((int8)p0) };
case 0xf9: return { "ld sp,hl" };
case 0xfa: return { "ld a,($", hex<2>(p1), hex<2>(p0), ")" };
case 0xfb: return { "ei" };
case 0xfc: return { "xx" };
case 0xfd: return { "xx" };
case 0xfe: return { "cp a,$", hex<2>(p0) };
case 0xff: return { "rst $0038" };
}
return "";
}
string CPU::disassemble_opcode_cb(uint16 pc) {
uint8 opcode = bus.read(pc);
uint8 p0 = bus.read(pc + 1);
uint8 p1 = bus.read(pc + 2);
uint8 p2 = bus.read(pc + 3);
switch(opcode) {
case 0x00: return { "rlc b" };
case 0x01: return { "rlc c" };
case 0x02: return { "rlc d" };
case 0x03: return { "rlc e" };
case 0x04: return { "rlc h" };
case 0x05: return { "rlc l" };
case 0x06: return { "rlc (hl)" };
case 0x07: return { "rlc a" };
case 0x08: return { "rrc b" };
case 0x09: return { "rrc c" };
case 0x0a: return { "rrc d" };
case 0x0b: return { "rrc e" };
case 0x0c: return { "rrc h" };
case 0x0d: return { "rrc l" };
case 0x0e: return { "rrc (hl)" };
case 0x0f: return { "rrc a" };
case 0x10: return { "rl b" };
case 0x11: return { "rl c" };
case 0x12: return { "rl d" };
case 0x13: return { "rl e" };
case 0x14: return { "rl h" };
case 0x15: return { "rl l" };
case 0x16: return { "rl (hl)" };
case 0x17: return { "rl a" };
case 0x18: return { "rr b" };
case 0x19: return { "rr c" };
case 0x1a: return { "rr d" };
case 0x1b: return { "rr e" };
case 0x1c: return { "rr h" };
case 0x1d: return { "rr l" };
case 0x1e: return { "rr (hl)" };
case 0x1f: return { "rr a" };
case 0x20: return { "sla b" };
case 0x21: return { "sla c" };
case 0x22: return { "sla d" };
case 0x23: return { "sla e" };
case 0x24: return { "sla h" };
case 0x25: return { "sla l" };
case 0x26: return { "sla (hl)" };
case 0x27: return { "sla a" };
case 0x28: return { "sra b" };
case 0x29: return { "sra c" };
case 0x2a: return { "sra d" };
case 0x2b: return { "sra e" };
case 0x2c: return { "sra h" };
case 0x2d: return { "sra l" };
case 0x2e: return { "sra (hl)" };
case 0x2f: return { "sra a" };
case 0x30: return { "swap b" };
case 0x31: return { "swap c" };
case 0x32: return { "swap d" };
case 0x33: return { "swap e" };
case 0x34: return { "swap h" };
case 0x35: return { "swap l" };
case 0x36: return { "swap (hl)" };
case 0x37: return { "swap a" };
case 0x38: return { "srl b" };
case 0x39: return { "srl c" };
case 0x3a: return { "srl d" };
case 0x3b: return { "srl e" };
case 0x3c: return { "srl h" };
case 0x3d: return { "srl l" };
case 0x3e: return { "srl (hl)" };
case 0x3f: return { "srl a" };
case 0x40: return { "bit 0,b" };
case 0x41: return { "bit 0,c" };
case 0x42: return { "bit 0,d" };
case 0x43: return { "bit 0,e" };
case 0x44: return { "bit 0,h" };
case 0x45: return { "bit 0,l" };
case 0x46: return { "bit 0,(hl)" };
case 0x47: return { "bit 0,a" };
case 0x48: return { "bit 1,b" };
case 0x49: return { "bit 1,c" };
case 0x4a: return { "bit 1,d" };
case 0x4b: return { "bit 1,e" };
case 0x4c: return { "bit 1,h" };
case 0x4d: return { "bit 1,l" };
case 0x4e: return { "bit 1,(hl)" };
case 0x4f: return { "bit 1,a" };
case 0x50: return { "bit 2,b" };
case 0x51: return { "bit 2,c" };
case 0x52: return { "bit 2,d" };
case 0x53: return { "bit 2,e" };
case 0x54: return { "bit 2,h" };
case 0x55: return { "bit 2,l" };
case 0x56: return { "bit 2,(hl)" };
case 0x57: return { "bit 2,a" };
case 0x58: return { "bit 3,b" };
case 0x59: return { "bit 3,c" };
case 0x5a: return { "bit 3,d" };
case 0x5b: return { "bit 3,e" };
case 0x5c: return { "bit 3,h" };
case 0x5d: return { "bit 3,l" };
case 0x5e: return { "bit 3,(hl)" };
case 0x5f: return { "bit 3,a" };
case 0x60: return { "bit 4,b" };
case 0x61: return { "bit 4,c" };
case 0x62: return { "bit 4,d" };
case 0x63: return { "bit 4,e" };
case 0x64: return { "bit 4,h" };
case 0x65: return { "bit 4,l" };
case 0x66: return { "bit 4,(hl)" };
case 0x67: return { "bit 4,a" };
case 0x68: return { "bit 5,b" };
case 0x69: return { "bit 5,c" };
case 0x6a: return { "bit 5,d" };
case 0x6b: return { "bit 5,e" };
case 0x6c: return { "bit 5,h" };
case 0x6d: return { "bit 5,l" };
case 0x6e: return { "bit 5,(hl)" };
case 0x6f: return { "bit 5,a" };
case 0x70: return { "bit 6,b" };
case 0x71: return { "bit 6,c" };
case 0x72: return { "bit 6,d" };
case 0x73: return { "bit 6,e" };
case 0x74: return { "bit 6,h" };
case 0x75: return { "bit 6,l" };
case 0x76: return { "bit 6,(hl)" };
case 0x77: return { "bit 6,a" };
case 0x78: return { "bit 7,b" };
case 0x79: return { "bit 7,c" };
case 0x7a: return { "bit 7,d" };
case 0x7b: return { "bit 7,e" };
case 0x7c: return { "bit 7,h" };
case 0x7d: return { "bit 7,l" };
case 0x7e: return { "bit 7,(hl)" };
case 0x7f: return { "bit 7,a" };
case 0x80: return { "res 0,b" };
case 0x81: return { "res 0,c" };
case 0x82: return { "res 0,d" };
case 0x83: return { "res 0,e" };
case 0x84: return { "res 0,h" };
case 0x85: return { "res 0,l" };
case 0x86: return { "res 0,(hl)" };
case 0x87: return { "res 0,a" };
case 0x88: return { "res 1,b" };
case 0x89: return { "res 1,c" };
case 0x8a: return { "res 1,d" };
case 0x8b: return { "res 1,e" };
case 0x8c: return { "res 1,h" };
case 0x8d: return { "res 1,l" };
case 0x8e: return { "res 1,(hl)" };
case 0x8f: return { "res 1,a" };
case 0x90: return { "res 2,b" };
case 0x91: return { "res 2,c" };
case 0x92: return { "res 2,d" };
case 0x93: return { "res 2,e" };
case 0x94: return { "res 2,h" };
case 0x95: return { "res 2,l" };
case 0x96: return { "res 2,(hl)" };
case 0x97: return { "res 2,a" };
case 0x98: return { "res 3,b" };
case 0x99: return { "res 3,c" };
case 0x9a: return { "res 3,d" };
case 0x9b: return { "res 3,e" };
case 0x9c: return { "res 3,h" };
case 0x9d: return { "res 3,l" };
case 0x9e: return { "res 3,(hl)" };
case 0x9f: return { "res 3,a" };
case 0xa0: return { "res 4,b" };
case 0xa1: return { "res 4,c" };
case 0xa2: return { "res 4,d" };
case 0xa3: return { "res 4,e" };
case 0xa4: return { "res 4,h" };
case 0xa5: return { "res 4,l" };
case 0xa6: return { "res 4,(hl)" };
case 0xa7: return { "res 4,a" };
case 0xa8: return { "res 5,b" };
case 0xa9: return { "res 5,c" };
case 0xaa: return { "res 5,d" };
case 0xab: return { "res 5,e" };
case 0xac: return { "res 5,h" };
case 0xad: return { "res 5,l" };
case 0xae: return { "res 5,(hl)" };
case 0xaf: return { "res 5,a" };
case 0xb0: return { "res 6,b" };
case 0xb1: return { "res 6,c" };
case 0xb2: return { "res 6,d" };
case 0xb3: return { "res 6,e" };
case 0xb4: return { "res 6,h" };
case 0xb5: return { "res 6,l" };
case 0xb6: return { "res 6,(hl)" };
case 0xb7: return { "res 6,a" };
case 0xb8: return { "res 7,b" };
case 0xb9: return { "res 7,c" };
case 0xba: return { "res 7,d" };
case 0xbb: return { "res 7,e" };
case 0xbc: return { "res 7,h" };
case 0xbd: return { "res 7,l" };
case 0xbe: return { "res 7,(hl)" };
case 0xbf: return { "res 7,a" };
case 0xc0: return { "set 0,b" };
case 0xc1: return { "set 0,c" };
case 0xc2: return { "set 0,d" };
case 0xc3: return { "set 0,e" };
case 0xc4: return { "set 0,h" };
case 0xc5: return { "set 0,l" };
case 0xc6: return { "set 0,(hl)" };
case 0xc7: return { "set 0,a" };
case 0xc8: return { "set 1,b" };
case 0xc9: return { "set 1,c" };
case 0xca: return { "set 1,d" };
case 0xcb: return { "set 1,e" };
case 0xcc: return { "set 1,h" };
case 0xcd: return { "set 1,l" };
case 0xce: return { "set 1,(hl)" };
case 0xcf: return { "set 1,a" };
case 0xd0: return { "set 2,b" };
case 0xd1: return { "set 2,c" };
case 0xd2: return { "set 2,d" };
case 0xd3: return { "set 2,e" };
case 0xd4: return { "set 2,h" };
case 0xd5: return { "set 2,l" };
case 0xd6: return { "set 2,(hl)" };
case 0xd7: return { "set 2,a" };
case 0xd8: return { "set 3,b" };
case 0xd9: return { "set 3,c" };
case 0xda: return { "set 3,d" };
case 0xdb: return { "set 3,e" };
case 0xdc: return { "set 3,h" };
case 0xdd: return { "set 3,l" };
case 0xde: return { "set 3,(hl)" };
case 0xdf: return { "set 3,a" };
case 0xe0: return { "set 4,b" };
case 0xe1: return { "set 4,c" };
case 0xe2: return { "set 4,d" };
case 0xe3: return { "set 4,e" };
case 0xe4: return { "set 4,h" };
case 0xe5: return { "set 4,l" };
case 0xe6: return { "set 4,(hl)" };
case 0xe7: return { "set 4,a" };
case 0xe8: return { "set 5,b" };
case 0xe9: return { "set 5,c" };
case 0xea: return { "set 5,d" };
case 0xeb: return { "set 5,e" };
case 0xec: return { "set 5,h" };
case 0xed: return { "set 5,l" };
case 0xee: return { "set 5,(hl)" };
case 0xef: return { "set 5,a" };
case 0xf0: return { "set 6,b" };
case 0xf1: return { "set 6,c" };
case 0xf2: return { "set 6,d" };
case 0xf3: return { "set 6,e" };
case 0xf4: return { "set 6,h" };
case 0xf5: return { "set 6,l" };
case 0xf6: return { "set 6,(hl)" };
case 0xf7: return { "set 6,a" };
case 0xf8: return { "set 7,b" };
case 0xf9: return { "set 7,c" };
case 0xfa: return { "set 7,d" };
case 0xfb: return { "set 7,e" };
case 0xfc: return { "set 7,h" };
case 0xfd: return { "set 7,l" };
case 0xfe: return { "set 7,(hl)" };
case 0xff: return { "set 7,a" };
}
return "";
}
#endif

View File

@@ -0,0 +1,101 @@
enum {
A, F, AF,
B, C, BC,
D, E, DE,
H, L, HL,
SP, PC,
};
enum {
ZF, NF, HF, CF,
};
//register base class
//the idea here is to have all registers derive from a single base class.
//this allows construction of opcodes that can take any register as input or output,
//despite the fact that behind-the-scenes, special handling is done for eg: F, AF, HL, etc.
//registers can also be chained together: eg af = 0x0000 writes both a and f.
struct Register {
virtual operator unsigned() const = 0;
virtual unsigned operator=(unsigned x) = 0;
Register& operator=(const Register &x) { operator=((unsigned)x); return *this; }
unsigned operator++(int) { unsigned r = *this; operator=(*this + 1); return r; }
unsigned operator--(int) { unsigned r = *this; operator=(*this - 1); return r; }
unsigned operator++() { return operator=(*this + 1); }
unsigned operator--() { return operator=(*this - 1); }
unsigned operator |=(unsigned x) { return operator=(*this | x); }
unsigned operator ^=(unsigned x) { return operator=(*this ^ x); }
unsigned operator &=(unsigned x) { return operator=(*this & x); }
unsigned operator<<=(unsigned x) { return operator=(*this << x); }
unsigned operator>>=(unsigned x) { return operator=(*this >> x); }
unsigned operator +=(unsigned x) { return operator=(*this + x); }
unsigned operator -=(unsigned x) { return operator=(*this - x); }
unsigned operator *=(unsigned x) { return operator=(*this * x); }
unsigned operator /=(unsigned x) { return operator=(*this / x); }
unsigned operator %=(unsigned x) { return operator=(*this % x); }
};
struct Register8 : Register {
uint8 data;
operator unsigned() const { return data; }
unsigned operator=(unsigned x) { return data = x; }
};
struct RegisterF : Register {
bool z, n, h, c;
operator unsigned() const { return (z << 7) | (n << 6) | (h << 5) | (c << 4); }
unsigned operator=(unsigned x) { z = x & 0x80; n = x & 0x40; h = x & 0x20; c = x & 0x10; return *this; }
bool& operator[](unsigned r) {
static bool* table[] = { &z, &n, &h, &c };
return *table[r];
}
};
struct Register16 : Register {
uint16 data;
operator unsigned() const { return data; }
unsigned operator=(unsigned x) { return data = x; }
};
struct RegisterAF : Register {
Register8 &hi;
RegisterF &lo;
operator unsigned() const { return (hi << 8) | (lo << 0); }
unsigned operator=(unsigned x) { hi = x >> 8; lo = x >> 0; return *this; }
RegisterAF(Register8 &hi, RegisterF &lo) : hi(hi), lo(lo) {}
};
struct RegisterW : Register {
Register8 &hi, &lo;
operator unsigned() const { return (hi << 8) | (lo << 0); }
unsigned operator=(unsigned x) { hi = x >> 8; lo = x >> 0; return *this; }
RegisterW(Register8 &hi, Register8 &lo) : hi(hi), lo(lo) {}
};
struct Registers {
Register8 a;
RegisterF f;
RegisterAF af;
Register8 b;
Register8 c;
RegisterW bc;
Register8 d;
Register8 e;
RegisterW de;
Register8 h;
Register8 l;
RegisterW hl;
Register16 sp;
Register16 pc;
Register& operator[](unsigned r) {
static Register* table[] = { &a, &f, &af, &b, &c, &bc, &d, &e, &de, &h, &l, &hl, &sp, &pc };
return *table[r];
}
Registers() : af(a, f), bc(b, c), de(d, e), hl(h, l) {}
} r;

519
bsnes/gameboy/cpu/core/table.cpp Executable file
View File

@@ -0,0 +1,519 @@
#ifdef CPU_CPP
void CPU::initialize_opcode_table() {
opcode_table[0x00] = &CPU::op_nop;
opcode_table[0x01] = &CPU::op_ld_rr_nn<BC>;
opcode_table[0x02] = &CPU::op_ld_rr_a<BC>;
opcode_table[0x03] = &CPU::op_inc_rr<BC>;
opcode_table[0x04] = &CPU::op_inc_r<B>;
opcode_table[0x05] = &CPU::op_dec_r<B>;
opcode_table[0x06] = &CPU::op_ld_r_n<B>;
opcode_table[0x07] = &CPU::op_rlca;
opcode_table[0x08] = &CPU::op_ld_nn_sp;
opcode_table[0x09] = &CPU::op_add_hl_rr<BC>;
opcode_table[0x0a] = &CPU::op_ld_a_rr<BC>;
opcode_table[0x0b] = &CPU::op_dec_rr<BC>;
opcode_table[0x0c] = &CPU::op_inc_r<C>;
opcode_table[0x0d] = &CPU::op_dec_r<C>;
opcode_table[0x0e] = &CPU::op_ld_r_n<C>;
opcode_table[0x0f] = &CPU::op_rrca;
opcode_table[0x10] = &CPU::op_stop;
opcode_table[0x11] = &CPU::op_ld_rr_nn<DE>;
opcode_table[0x12] = &CPU::op_ld_rr_a<DE>;
opcode_table[0x13] = &CPU::op_inc_rr<DE>;
opcode_table[0x14] = &CPU::op_inc_r<D>;
opcode_table[0x15] = &CPU::op_dec_r<D>;
opcode_table[0x16] = &CPU::op_ld_r_n<D>;
opcode_table[0x17] = &CPU::op_rla;
opcode_table[0x18] = &CPU::op_jr_n;
opcode_table[0x19] = &CPU::op_add_hl_rr<DE>;
opcode_table[0x1a] = &CPU::op_ld_a_rr<DE>;
opcode_table[0x1b] = &CPU::op_dec_rr<DE>;
opcode_table[0x1c] = &CPU::op_inc_r<E>;
opcode_table[0x1d] = &CPU::op_dec_r<E>;
opcode_table[0x1e] = &CPU::op_ld_r_n<E>;
opcode_table[0x1f] = &CPU::op_rra;
opcode_table[0x20] = &CPU::op_jr_f_n<ZF, 0>;
opcode_table[0x21] = &CPU::op_ld_rr_nn<HL>;
opcode_table[0x22] = &CPU::op_ldi_hl_a;
opcode_table[0x23] = &CPU::op_inc_rr<HL>;
opcode_table[0x24] = &CPU::op_inc_r<H>;
opcode_table[0x25] = &CPU::op_dec_r<H>;
opcode_table[0x26] = &CPU::op_ld_r_n<H>;
opcode_table[0x27] = &CPU::op_daa;
opcode_table[0x28] = &CPU::op_jr_f_n<ZF, 1>;
opcode_table[0x29] = &CPU::op_add_hl_rr<HL>;
opcode_table[0x2a] = &CPU::op_ldi_a_hl;
opcode_table[0x2b] = &CPU::op_dec_rr<HL>;
opcode_table[0x2c] = &CPU::op_inc_r<L>;
opcode_table[0x2d] = &CPU::op_dec_r<L>;
opcode_table[0x2e] = &CPU::op_ld_r_n<L>;
opcode_table[0x2f] = &CPU::op_cpl;
opcode_table[0x30] = &CPU::op_jr_f_n<CF, 0>;
opcode_table[0x31] = &CPU::op_ld_rr_nn<SP>;
opcode_table[0x32] = &CPU::op_ldd_hl_a;
opcode_table[0x33] = &CPU::op_inc_rr<SP>;
opcode_table[0x34] = &CPU::op_inc_hl;
opcode_table[0x35] = &CPU::op_dec_hl;
opcode_table[0x36] = &CPU::op_ld_hl_n;
opcode_table[0x37] = &CPU::op_scf;
opcode_table[0x38] = &CPU::op_jr_f_n<CF, 1>;
opcode_table[0x39] = &CPU::op_add_hl_rr<SP>;
opcode_table[0x3a] = &CPU::op_ldd_a_hl;
opcode_table[0x3b] = &CPU::op_dec_rr<SP>;
opcode_table[0x3c] = &CPU::op_inc_r<A>;
opcode_table[0x3d] = &CPU::op_dec_r<A>;
opcode_table[0x3e] = &CPU::op_ld_r_n<A>;
opcode_table[0x3f] = &CPU::op_ccf;
opcode_table[0x40] = &CPU::op_ld_r_r<B, B>;
opcode_table[0x41] = &CPU::op_ld_r_r<B, C>;
opcode_table[0x42] = &CPU::op_ld_r_r<B, D>;
opcode_table[0x43] = &CPU::op_ld_r_r<B, E>;
opcode_table[0x44] = &CPU::op_ld_r_r<B, H>;
opcode_table[0x45] = &CPU::op_ld_r_r<B, L>;
opcode_table[0x46] = &CPU::op_ld_r_hl<B>;
opcode_table[0x47] = &CPU::op_ld_r_r<B, A>;
opcode_table[0x48] = &CPU::op_ld_r_r<C, B>;
opcode_table[0x49] = &CPU::op_ld_r_r<C, C>;
opcode_table[0x4a] = &CPU::op_ld_r_r<C, D>;
opcode_table[0x4b] = &CPU::op_ld_r_r<C, E>;
opcode_table[0x4c] = &CPU::op_ld_r_r<C, H>;
opcode_table[0x4d] = &CPU::op_ld_r_r<C, L>;
opcode_table[0x4e] = &CPU::op_ld_r_hl<C>;
opcode_table[0x4f] = &CPU::op_ld_r_r<C, A>;
opcode_table[0x50] = &CPU::op_ld_r_r<D, B>;
opcode_table[0x51] = &CPU::op_ld_r_r<D, C>;
opcode_table[0x52] = &CPU::op_ld_r_r<D, D>;
opcode_table[0x53] = &CPU::op_ld_r_r<D, E>;
opcode_table[0x54] = &CPU::op_ld_r_r<D, H>;
opcode_table[0x55] = &CPU::op_ld_r_r<D, L>;
opcode_table[0x56] = &CPU::op_ld_r_hl<D>;
opcode_table[0x57] = &CPU::op_ld_r_r<D, A>;
opcode_table[0x58] = &CPU::op_ld_r_r<E, B>;
opcode_table[0x59] = &CPU::op_ld_r_r<E, C>;
opcode_table[0x5a] = &CPU::op_ld_r_r<E, D>;
opcode_table[0x5b] = &CPU::op_ld_r_r<E, E>;
opcode_table[0x5c] = &CPU::op_ld_r_r<E, H>;
opcode_table[0x5d] = &CPU::op_ld_r_r<E, L>;
opcode_table[0x5e] = &CPU::op_ld_r_hl<E>;
opcode_table[0x5f] = &CPU::op_ld_r_r<E, A>;
opcode_table[0x60] = &CPU::op_ld_r_r<H, B>;
opcode_table[0x61] = &CPU::op_ld_r_r<H, C>;
opcode_table[0x62] = &CPU::op_ld_r_r<H, D>;
opcode_table[0x63] = &CPU::op_ld_r_r<H, E>;
opcode_table[0x64] = &CPU::op_ld_r_r<H, H>;
opcode_table[0x65] = &CPU::op_ld_r_r<H, L>;
opcode_table[0x66] = &CPU::op_ld_r_hl<H>;
opcode_table[0x67] = &CPU::op_ld_r_r<H, A>;
opcode_table[0x68] = &CPU::op_ld_r_r<L, B>;
opcode_table[0x69] = &CPU::op_ld_r_r<L, C>;
opcode_table[0x6a] = &CPU::op_ld_r_r<L, D>;
opcode_table[0x6b] = &CPU::op_ld_r_r<L, E>;
opcode_table[0x6c] = &CPU::op_ld_r_r<L, H>;
opcode_table[0x6d] = &CPU::op_ld_r_r<L, L>;
opcode_table[0x6e] = &CPU::op_ld_r_hl<L>;
opcode_table[0x6f] = &CPU::op_ld_r_r<L, A>;
opcode_table[0x70] = &CPU::op_ld_hl_r<B>;
opcode_table[0x71] = &CPU::op_ld_hl_r<C>;
opcode_table[0x72] = &CPU::op_ld_hl_r<D>;
opcode_table[0x73] = &CPU::op_ld_hl_r<E>;
opcode_table[0x74] = &CPU::op_ld_hl_r<H>;
opcode_table[0x75] = &CPU::op_ld_hl_r<L>;
opcode_table[0x76] = &CPU::op_halt;
opcode_table[0x77] = &CPU::op_ld_hl_r<A>;
opcode_table[0x78] = &CPU::op_ld_r_r<A, B>;
opcode_table[0x79] = &CPU::op_ld_r_r<A, C>;
opcode_table[0x7a] = &CPU::op_ld_r_r<A, D>;
opcode_table[0x7b] = &CPU::op_ld_r_r<A, E>;
opcode_table[0x7c] = &CPU::op_ld_r_r<A, H>;
opcode_table[0x7d] = &CPU::op_ld_r_r<A, L>;
opcode_table[0x7e] = &CPU::op_ld_r_hl<A>;
opcode_table[0x7f] = &CPU::op_ld_r_r<A, A>;
opcode_table[0x80] = &CPU::op_add_a_r<B>;
opcode_table[0x81] = &CPU::op_add_a_r<C>;
opcode_table[0x82] = &CPU::op_add_a_r<D>;
opcode_table[0x83] = &CPU::op_add_a_r<E>;
opcode_table[0x84] = &CPU::op_add_a_r<H>;
opcode_table[0x85] = &CPU::op_add_a_r<L>;
opcode_table[0x86] = &CPU::op_add_a_hl;
opcode_table[0x87] = &CPU::op_add_a_r<A>;
opcode_table[0x88] = &CPU::op_adc_a_r<B>;
opcode_table[0x89] = &CPU::op_adc_a_r<C>;
opcode_table[0x8a] = &CPU::op_adc_a_r<D>;
opcode_table[0x8b] = &CPU::op_adc_a_r<E>;
opcode_table[0x8c] = &CPU::op_adc_a_r<H>;
opcode_table[0x8d] = &CPU::op_adc_a_r<L>;
opcode_table[0x8e] = &CPU::op_adc_a_hl;
opcode_table[0x8f] = &CPU::op_adc_a_r<A>;
opcode_table[0x90] = &CPU::op_sub_a_r<B>;
opcode_table[0x91] = &CPU::op_sub_a_r<C>;
opcode_table[0x92] = &CPU::op_sub_a_r<D>;
opcode_table[0x93] = &CPU::op_sub_a_r<E>;
opcode_table[0x94] = &CPU::op_sub_a_r<H>;
opcode_table[0x95] = &CPU::op_sub_a_r<L>;
opcode_table[0x96] = &CPU::op_sub_a_hl;
opcode_table[0x97] = &CPU::op_sub_a_r<A>;
opcode_table[0x98] = &CPU::op_sbc_a_r<B>;
opcode_table[0x99] = &CPU::op_sbc_a_r<C>;
opcode_table[0x9a] = &CPU::op_sbc_a_r<D>;
opcode_table[0x9b] = &CPU::op_sbc_a_r<E>;
opcode_table[0x9c] = &CPU::op_sbc_a_r<H>;
opcode_table[0x9d] = &CPU::op_sbc_a_r<L>;
opcode_table[0x9e] = &CPU::op_sbc_a_hl;
opcode_table[0x9f] = &CPU::op_sbc_a_r<A>;
opcode_table[0xa0] = &CPU::op_and_a_r<B>;
opcode_table[0xa1] = &CPU::op_and_a_r<C>;
opcode_table[0xa2] = &CPU::op_and_a_r<D>;
opcode_table[0xa3] = &CPU::op_and_a_r<E>;
opcode_table[0xa4] = &CPU::op_and_a_r<H>;
opcode_table[0xa5] = &CPU::op_and_a_r<L>;
opcode_table[0xa6] = &CPU::op_and_a_hl;
opcode_table[0xa7] = &CPU::op_and_a_r<A>;
opcode_table[0xa8] = &CPU::op_xor_a_r<B>;
opcode_table[0xa9] = &CPU::op_xor_a_r<C>;
opcode_table[0xaa] = &CPU::op_xor_a_r<D>;
opcode_table[0xab] = &CPU::op_xor_a_r<E>;
opcode_table[0xac] = &CPU::op_xor_a_r<H>;
opcode_table[0xad] = &CPU::op_xor_a_r<L>;
opcode_table[0xae] = &CPU::op_xor_a_hl;
opcode_table[0xaf] = &CPU::op_xor_a_r<A>;
opcode_table[0xb0] = &CPU::op_or_a_r<B>;
opcode_table[0xb1] = &CPU::op_or_a_r<C>;
opcode_table[0xb2] = &CPU::op_or_a_r<D>;
opcode_table[0xb3] = &CPU::op_or_a_r<E>;
opcode_table[0xb4] = &CPU::op_or_a_r<H>;
opcode_table[0xb5] = &CPU::op_or_a_r<L>;
opcode_table[0xb6] = &CPU::op_or_a_hl;
opcode_table[0xb7] = &CPU::op_or_a_r<A>;
opcode_table[0xb8] = &CPU::op_cp_a_r<B>;
opcode_table[0xb9] = &CPU::op_cp_a_r<C>;
opcode_table[0xba] = &CPU::op_cp_a_r<D>;
opcode_table[0xbb] = &CPU::op_cp_a_r<E>;
opcode_table[0xbc] = &CPU::op_cp_a_r<H>;
opcode_table[0xbd] = &CPU::op_cp_a_r<L>;
opcode_table[0xbe] = &CPU::op_cp_a_hl;
opcode_table[0xbf] = &CPU::op_cp_a_r<A>;
opcode_table[0xc0] = &CPU::op_ret_f<ZF, 0>;
opcode_table[0xc1] = &CPU::op_pop_rr<BC>;
opcode_table[0xc2] = &CPU::op_jp_f_nn<ZF, 0>;
opcode_table[0xc3] = &CPU::op_jp_nn;
opcode_table[0xc4] = &CPU::op_call_f_nn<ZF, 0>;
opcode_table[0xc5] = &CPU::op_push_rr<BC>;
opcode_table[0xc6] = &CPU::op_add_a_n;
opcode_table[0xc7] = &CPU::op_rst_n<0x00>;
opcode_table[0xc8] = &CPU::op_ret_f<ZF, 1>;
opcode_table[0xc9] = &CPU::op_ret;
opcode_table[0xca] = &CPU::op_jp_f_nn<ZF, 1>;
opcode_table[0xcb] = &CPU::op_cb;
opcode_table[0xcc] = &CPU::op_call_f_nn<ZF, 1>;
opcode_table[0xcd] = &CPU::op_call_nn;
opcode_table[0xce] = &CPU::op_adc_a_n;
opcode_table[0xcf] = &CPU::op_rst_n<0x08>;
opcode_table[0xd0] = &CPU::op_ret_f<CF, 0>;
opcode_table[0xd1] = &CPU::op_pop_rr<DE>;
opcode_table[0xd2] = &CPU::op_jp_f_nn<CF, 0>;
opcode_table[0xd3] = &CPU::op_xx;
opcode_table[0xd4] = &CPU::op_call_f_nn<CF, 0>;
opcode_table[0xd5] = &CPU::op_push_rr<DE>;
opcode_table[0xd6] = &CPU::op_sub_a_n;
opcode_table[0xd7] = &CPU::op_rst_n<0x10>;
opcode_table[0xd8] = &CPU::op_ret_f<CF, 1>;
opcode_table[0xd9] = &CPU::op_reti;
opcode_table[0xda] = &CPU::op_jp_f_nn<CF, 1>;
opcode_table[0xdb] = &CPU::op_xx;
opcode_table[0xdc] = &CPU::op_call_f_nn<CF, 1>;
opcode_table[0xdd] = &CPU::op_xx;
opcode_table[0xde] = &CPU::op_sbc_a_n;
opcode_table[0xdf] = &CPU::op_rst_n<0x18>;
opcode_table[0xe0] = &CPU::op_ld_ffn_a;
opcode_table[0xe1] = &CPU::op_pop_rr<HL>;
opcode_table[0xe2] = &CPU::op_ld_ffc_a;
opcode_table[0xe3] = &CPU::op_xx;
opcode_table[0xe4] = &CPU::op_xx;
opcode_table[0xe5] = &CPU::op_push_rr<HL>;
opcode_table[0xe6] = &CPU::op_and_a_n;
opcode_table[0xe7] = &CPU::op_rst_n<0x20>;
opcode_table[0xe8] = &CPU::op_add_sp_n;
opcode_table[0xe9] = &CPU::op_jp_hl;
opcode_table[0xea] = &CPU::op_ld_nn_a;
opcode_table[0xeb] = &CPU::op_xx;
opcode_table[0xec] = &CPU::op_xx;
opcode_table[0xed] = &CPU::op_xx;
opcode_table[0xee] = &CPU::op_xor_a_n;
opcode_table[0xef] = &CPU::op_rst_n<0x28>;
opcode_table[0xf0] = &CPU::op_ld_a_ffn;
opcode_table[0xf1] = &CPU::op_pop_rr<AF>;
opcode_table[0xf2] = &CPU::op_ld_a_ffc;
opcode_table[0xf3] = &CPU::op_di;
opcode_table[0xf4] = &CPU::op_xx;
opcode_table[0xf5] = &CPU::op_push_rr<AF>;
opcode_table[0xf6] = &CPU::op_or_a_n;
opcode_table[0xf7] = &CPU::op_rst_n<0x30>;
opcode_table[0xf8] = &CPU::op_ld_hl_sp_n;
opcode_table[0xf9] = &CPU::op_ld_sp_hl;
opcode_table[0xfa] = &CPU::op_ld_a_nn;
opcode_table[0xfb] = &CPU::op_ei;
opcode_table[0xfc] = &CPU::op_xx;
opcode_table[0xfd] = &CPU::op_xx;
opcode_table[0xfe] = &CPU::op_cp_a_n;
opcode_table[0xff] = &CPU::op_rst_n<0x38>;
opcode_table_cb[0x00] = &CPU::op_rlc_r<B>;
opcode_table_cb[0x01] = &CPU::op_rlc_r<C>;
opcode_table_cb[0x02] = &CPU::op_rlc_r<D>;
opcode_table_cb[0x03] = &CPU::op_rlc_r<E>;
opcode_table_cb[0x04] = &CPU::op_rlc_r<H>;
opcode_table_cb[0x05] = &CPU::op_rlc_r<L>;
opcode_table_cb[0x06] = &CPU::op_rlc_hl;
opcode_table_cb[0x07] = &CPU::op_rlc_r<A>;
opcode_table_cb[0x08] = &CPU::op_rrc_r<B>;
opcode_table_cb[0x09] = &CPU::op_rrc_r<C>;
opcode_table_cb[0x0a] = &CPU::op_rrc_r<D>;
opcode_table_cb[0x0b] = &CPU::op_rrc_r<E>;
opcode_table_cb[0x0c] = &CPU::op_rrc_r<H>;
opcode_table_cb[0x0d] = &CPU::op_rrc_r<L>;
opcode_table_cb[0x0e] = &CPU::op_rrc_hl;
opcode_table_cb[0x0f] = &CPU::op_rrc_r<A>;
opcode_table_cb[0x10] = &CPU::op_rl_r<B>;
opcode_table_cb[0x11] = &CPU::op_rl_r<C>;
opcode_table_cb[0x12] = &CPU::op_rl_r<D>;
opcode_table_cb[0x13] = &CPU::op_rl_r<E>;
opcode_table_cb[0x14] = &CPU::op_rl_r<H>;
opcode_table_cb[0x15] = &CPU::op_rl_r<L>;
opcode_table_cb[0x16] = &CPU::op_rl_hl;
opcode_table_cb[0x17] = &CPU::op_rl_r<A>;
opcode_table_cb[0x18] = &CPU::op_rr_r<B>;
opcode_table_cb[0x19] = &CPU::op_rr_r<C>;
opcode_table_cb[0x1a] = &CPU::op_rr_r<D>;
opcode_table_cb[0x1b] = &CPU::op_rr_r<E>;
opcode_table_cb[0x1c] = &CPU::op_rr_r<H>;
opcode_table_cb[0x1d] = &CPU::op_rr_r<L>;
opcode_table_cb[0x1e] = &CPU::op_rr_hl;
opcode_table_cb[0x1f] = &CPU::op_rr_r<A>;
opcode_table_cb[0x20] = &CPU::op_sla_r<B>;
opcode_table_cb[0x21] = &CPU::op_sla_r<C>;
opcode_table_cb[0x22] = &CPU::op_sla_r<D>;
opcode_table_cb[0x23] = &CPU::op_sla_r<E>;
opcode_table_cb[0x24] = &CPU::op_sla_r<H>;
opcode_table_cb[0x25] = &CPU::op_sla_r<L>;
opcode_table_cb[0x26] = &CPU::op_sla_hl;
opcode_table_cb[0x27] = &CPU::op_sla_r<A>;
opcode_table_cb[0x28] = &CPU::op_sra_r<B>;
opcode_table_cb[0x29] = &CPU::op_sra_r<C>;
opcode_table_cb[0x2a] = &CPU::op_sra_r<D>;
opcode_table_cb[0x2b] = &CPU::op_sra_r<E>;
opcode_table_cb[0x2c] = &CPU::op_sra_r<H>;
opcode_table_cb[0x2d] = &CPU::op_sra_r<L>;
opcode_table_cb[0x2e] = &CPU::op_sra_hl;
opcode_table_cb[0x2f] = &CPU::op_sra_r<A>;
opcode_table_cb[0x30] = &CPU::op_swap_r<B>;
opcode_table_cb[0x31] = &CPU::op_swap_r<C>;
opcode_table_cb[0x32] = &CPU::op_swap_r<D>;
opcode_table_cb[0x33] = &CPU::op_swap_r<E>;
opcode_table_cb[0x34] = &CPU::op_swap_r<H>;
opcode_table_cb[0x35] = &CPU::op_swap_r<L>;
opcode_table_cb[0x36] = &CPU::op_swap_hl;
opcode_table_cb[0x37] = &CPU::op_swap_r<A>;
opcode_table_cb[0x38] = &CPU::op_srl_r<B>;
opcode_table_cb[0x39] = &CPU::op_srl_r<C>;
opcode_table_cb[0x3a] = &CPU::op_srl_r<D>;
opcode_table_cb[0x3b] = &CPU::op_srl_r<E>;
opcode_table_cb[0x3c] = &CPU::op_srl_r<H>;
opcode_table_cb[0x3d] = &CPU::op_srl_r<L>;
opcode_table_cb[0x3e] = &CPU::op_srl_hl;
opcode_table_cb[0x3f] = &CPU::op_srl_r<A>;
opcode_table_cb[0x40] = &CPU::op_bit_n_r<0, B>;
opcode_table_cb[0x41] = &CPU::op_bit_n_r<0, C>;
opcode_table_cb[0x42] = &CPU::op_bit_n_r<0, D>;
opcode_table_cb[0x43] = &CPU::op_bit_n_r<0, E>;
opcode_table_cb[0x44] = &CPU::op_bit_n_r<0, H>;
opcode_table_cb[0x45] = &CPU::op_bit_n_r<0, L>;
opcode_table_cb[0x46] = &CPU::op_bit_n_hl<0>;
opcode_table_cb[0x47] = &CPU::op_bit_n_r<0, A>;
opcode_table_cb[0x48] = &CPU::op_bit_n_r<1, B>;
opcode_table_cb[0x49] = &CPU::op_bit_n_r<1, C>;
opcode_table_cb[0x4a] = &CPU::op_bit_n_r<1, D>;
opcode_table_cb[0x4b] = &CPU::op_bit_n_r<1, E>;
opcode_table_cb[0x4c] = &CPU::op_bit_n_r<1, H>;
opcode_table_cb[0x4d] = &CPU::op_bit_n_r<1, L>;
opcode_table_cb[0x4e] = &CPU::op_bit_n_hl<1>;
opcode_table_cb[0x4f] = &CPU::op_bit_n_r<1, A>;
opcode_table_cb[0x50] = &CPU::op_bit_n_r<2, B>;
opcode_table_cb[0x51] = &CPU::op_bit_n_r<2, C>;
opcode_table_cb[0x52] = &CPU::op_bit_n_r<2, D>;
opcode_table_cb[0x53] = &CPU::op_bit_n_r<2, E>;
opcode_table_cb[0x54] = &CPU::op_bit_n_r<2, H>;
opcode_table_cb[0x55] = &CPU::op_bit_n_r<2, L>;
opcode_table_cb[0x56] = &CPU::op_bit_n_hl<2>;
opcode_table_cb[0x57] = &CPU::op_bit_n_r<2, A>;
opcode_table_cb[0x58] = &CPU::op_bit_n_r<3, B>;
opcode_table_cb[0x59] = &CPU::op_bit_n_r<3, C>;
opcode_table_cb[0x5a] = &CPU::op_bit_n_r<3, D>;
opcode_table_cb[0x5b] = &CPU::op_bit_n_r<3, E>;
opcode_table_cb[0x5c] = &CPU::op_bit_n_r<3, H>;
opcode_table_cb[0x5d] = &CPU::op_bit_n_r<3, L>;
opcode_table_cb[0x5e] = &CPU::op_bit_n_hl<3>;
opcode_table_cb[0x5f] = &CPU::op_bit_n_r<3, A>;
opcode_table_cb[0x60] = &CPU::op_bit_n_r<4, B>;
opcode_table_cb[0x61] = &CPU::op_bit_n_r<4, C>;
opcode_table_cb[0x62] = &CPU::op_bit_n_r<4, D>;
opcode_table_cb[0x63] = &CPU::op_bit_n_r<4, E>;
opcode_table_cb[0x64] = &CPU::op_bit_n_r<4, H>;
opcode_table_cb[0x65] = &CPU::op_bit_n_r<4, L>;
opcode_table_cb[0x66] = &CPU::op_bit_n_hl<4>;
opcode_table_cb[0x67] = &CPU::op_bit_n_r<4, A>;
opcode_table_cb[0x68] = &CPU::op_bit_n_r<5, B>;
opcode_table_cb[0x69] = &CPU::op_bit_n_r<5, C>;
opcode_table_cb[0x6a] = &CPU::op_bit_n_r<5, D>;
opcode_table_cb[0x6b] = &CPU::op_bit_n_r<5, E>;
opcode_table_cb[0x6c] = &CPU::op_bit_n_r<5, H>;
opcode_table_cb[0x6d] = &CPU::op_bit_n_r<5, L>;
opcode_table_cb[0x6e] = &CPU::op_bit_n_hl<5>;
opcode_table_cb[0x6f] = &CPU::op_bit_n_r<5, A>;
opcode_table_cb[0x70] = &CPU::op_bit_n_r<6, B>;
opcode_table_cb[0x71] = &CPU::op_bit_n_r<6, C>;
opcode_table_cb[0x72] = &CPU::op_bit_n_r<6, D>;
opcode_table_cb[0x73] = &CPU::op_bit_n_r<6, E>;
opcode_table_cb[0x74] = &CPU::op_bit_n_r<6, H>;
opcode_table_cb[0x75] = &CPU::op_bit_n_r<6, L>;
opcode_table_cb[0x76] = &CPU::op_bit_n_hl<6>;
opcode_table_cb[0x77] = &CPU::op_bit_n_r<6, A>;
opcode_table_cb[0x78] = &CPU::op_bit_n_r<7, B>;
opcode_table_cb[0x79] = &CPU::op_bit_n_r<7, C>;
opcode_table_cb[0x7a] = &CPU::op_bit_n_r<7, D>;
opcode_table_cb[0x7b] = &CPU::op_bit_n_r<7, E>;
opcode_table_cb[0x7c] = &CPU::op_bit_n_r<7, H>;
opcode_table_cb[0x7d] = &CPU::op_bit_n_r<7, L>;
opcode_table_cb[0x7e] = &CPU::op_bit_n_hl<7>;
opcode_table_cb[0x7f] = &CPU::op_bit_n_r<7, A>;
opcode_table_cb[0x80] = &CPU::op_res_n_r<0, B>;
opcode_table_cb[0x81] = &CPU::op_res_n_r<0, C>;
opcode_table_cb[0x82] = &CPU::op_res_n_r<0, D>;
opcode_table_cb[0x83] = &CPU::op_res_n_r<0, E>;
opcode_table_cb[0x84] = &CPU::op_res_n_r<0, H>;
opcode_table_cb[0x85] = &CPU::op_res_n_r<0, L>;
opcode_table_cb[0x86] = &CPU::op_res_n_hl<0>;
opcode_table_cb[0x87] = &CPU::op_res_n_r<0, A>;
opcode_table_cb[0x88] = &CPU::op_res_n_r<1, B>;
opcode_table_cb[0x89] = &CPU::op_res_n_r<1, C>;
opcode_table_cb[0x8a] = &CPU::op_res_n_r<1, D>;
opcode_table_cb[0x8b] = &CPU::op_res_n_r<1, E>;
opcode_table_cb[0x8c] = &CPU::op_res_n_r<1, H>;
opcode_table_cb[0x8d] = &CPU::op_res_n_r<1, L>;
opcode_table_cb[0x8e] = &CPU::op_res_n_hl<1>;
opcode_table_cb[0x8f] = &CPU::op_res_n_r<1, A>;
opcode_table_cb[0x90] = &CPU::op_res_n_r<2, B>;
opcode_table_cb[0x91] = &CPU::op_res_n_r<2, C>;
opcode_table_cb[0x92] = &CPU::op_res_n_r<2, D>;
opcode_table_cb[0x93] = &CPU::op_res_n_r<2, E>;
opcode_table_cb[0x94] = &CPU::op_res_n_r<2, H>;
opcode_table_cb[0x95] = &CPU::op_res_n_r<2, L>;
opcode_table_cb[0x96] = &CPU::op_res_n_hl<2>;
opcode_table_cb[0x97] = &CPU::op_res_n_r<2, A>;
opcode_table_cb[0x98] = &CPU::op_res_n_r<3, B>;
opcode_table_cb[0x99] = &CPU::op_res_n_r<3, C>;
opcode_table_cb[0x9a] = &CPU::op_res_n_r<3, D>;
opcode_table_cb[0x9b] = &CPU::op_res_n_r<3, E>;
opcode_table_cb[0x9c] = &CPU::op_res_n_r<3, H>;
opcode_table_cb[0x9d] = &CPU::op_res_n_r<3, L>;
opcode_table_cb[0x9e] = &CPU::op_res_n_hl<3>;
opcode_table_cb[0x9f] = &CPU::op_res_n_r<3, A>;
opcode_table_cb[0xa0] = &CPU::op_res_n_r<4, B>;
opcode_table_cb[0xa1] = &CPU::op_res_n_r<4, C>;
opcode_table_cb[0xa2] = &CPU::op_res_n_r<4, D>;
opcode_table_cb[0xa3] = &CPU::op_res_n_r<4, E>;
opcode_table_cb[0xa4] = &CPU::op_res_n_r<4, H>;
opcode_table_cb[0xa5] = &CPU::op_res_n_r<4, L>;
opcode_table_cb[0xa6] = &CPU::op_res_n_hl<4>;
opcode_table_cb[0xa7] = &CPU::op_res_n_r<4, A>;
opcode_table_cb[0xa8] = &CPU::op_res_n_r<5, B>;
opcode_table_cb[0xa9] = &CPU::op_res_n_r<5, C>;
opcode_table_cb[0xaa] = &CPU::op_res_n_r<5, D>;
opcode_table_cb[0xab] = &CPU::op_res_n_r<5, E>;
opcode_table_cb[0xac] = &CPU::op_res_n_r<5, H>;
opcode_table_cb[0xad] = &CPU::op_res_n_r<5, L>;
opcode_table_cb[0xae] = &CPU::op_res_n_hl<5>;
opcode_table_cb[0xaf] = &CPU::op_res_n_r<5, A>;
opcode_table_cb[0xb0] = &CPU::op_res_n_r<6, B>;
opcode_table_cb[0xb1] = &CPU::op_res_n_r<6, C>;
opcode_table_cb[0xb2] = &CPU::op_res_n_r<6, D>;
opcode_table_cb[0xb3] = &CPU::op_res_n_r<6, E>;
opcode_table_cb[0xb4] = &CPU::op_res_n_r<6, H>;
opcode_table_cb[0xb5] = &CPU::op_res_n_r<6, L>;
opcode_table_cb[0xb6] = &CPU::op_res_n_hl<6>;
opcode_table_cb[0xb7] = &CPU::op_res_n_r<6, A>;
opcode_table_cb[0xb8] = &CPU::op_res_n_r<7, B>;
opcode_table_cb[0xb9] = &CPU::op_res_n_r<7, C>;
opcode_table_cb[0xba] = &CPU::op_res_n_r<7, D>;
opcode_table_cb[0xbb] = &CPU::op_res_n_r<7, E>;
opcode_table_cb[0xbc] = &CPU::op_res_n_r<7, H>;
opcode_table_cb[0xbd] = &CPU::op_res_n_r<7, L>;
opcode_table_cb[0xbe] = &CPU::op_res_n_hl<7>;
opcode_table_cb[0xbf] = &CPU::op_res_n_r<7, A>;
opcode_table_cb[0xc0] = &CPU::op_set_n_r<0, B>;
opcode_table_cb[0xc1] = &CPU::op_set_n_r<0, C>;
opcode_table_cb[0xc2] = &CPU::op_set_n_r<0, D>;
opcode_table_cb[0xc3] = &CPU::op_set_n_r<0, E>;
opcode_table_cb[0xc4] = &CPU::op_set_n_r<0, H>;
opcode_table_cb[0xc5] = &CPU::op_set_n_r<0, L>;
opcode_table_cb[0xc6] = &CPU::op_set_n_hl<0>;
opcode_table_cb[0xc7] = &CPU::op_set_n_r<0, A>;
opcode_table_cb[0xc8] = &CPU::op_set_n_r<1, B>;
opcode_table_cb[0xc9] = &CPU::op_set_n_r<1, C>;
opcode_table_cb[0xca] = &CPU::op_set_n_r<1, D>;
opcode_table_cb[0xcb] = &CPU::op_set_n_r<1, E>;
opcode_table_cb[0xcc] = &CPU::op_set_n_r<1, H>;
opcode_table_cb[0xcd] = &CPU::op_set_n_r<1, L>;
opcode_table_cb[0xce] = &CPU::op_set_n_hl<1>;
opcode_table_cb[0xcf] = &CPU::op_set_n_r<1, A>;
opcode_table_cb[0xd0] = &CPU::op_set_n_r<2, B>;
opcode_table_cb[0xd1] = &CPU::op_set_n_r<2, C>;
opcode_table_cb[0xd2] = &CPU::op_set_n_r<2, D>;
opcode_table_cb[0xd3] = &CPU::op_set_n_r<2, E>;
opcode_table_cb[0xd4] = &CPU::op_set_n_r<2, H>;
opcode_table_cb[0xd5] = &CPU::op_set_n_r<2, L>;
opcode_table_cb[0xd6] = &CPU::op_set_n_hl<2>;
opcode_table_cb[0xd7] = &CPU::op_set_n_r<2, A>;
opcode_table_cb[0xd8] = &CPU::op_set_n_r<3, B>;
opcode_table_cb[0xd9] = &CPU::op_set_n_r<3, C>;
opcode_table_cb[0xda] = &CPU::op_set_n_r<3, D>;
opcode_table_cb[0xdb] = &CPU::op_set_n_r<3, E>;
opcode_table_cb[0xdc] = &CPU::op_set_n_r<3, H>;
opcode_table_cb[0xdd] = &CPU::op_set_n_r<3, L>;
opcode_table_cb[0xde] = &CPU::op_set_n_hl<3>;
opcode_table_cb[0xdf] = &CPU::op_set_n_r<3, A>;
opcode_table_cb[0xe0] = &CPU::op_set_n_r<4, B>;
opcode_table_cb[0xe1] = &CPU::op_set_n_r<4, C>;
opcode_table_cb[0xe2] = &CPU::op_set_n_r<4, D>;
opcode_table_cb[0xe3] = &CPU::op_set_n_r<4, E>;
opcode_table_cb[0xe4] = &CPU::op_set_n_r<4, H>;
opcode_table_cb[0xe5] = &CPU::op_set_n_r<4, L>;
opcode_table_cb[0xe6] = &CPU::op_set_n_hl<4>;
opcode_table_cb[0xe7] = &CPU::op_set_n_r<4, A>;
opcode_table_cb[0xe8] = &CPU::op_set_n_r<5, B>;
opcode_table_cb[0xe9] = &CPU::op_set_n_r<5, C>;
opcode_table_cb[0xea] = &CPU::op_set_n_r<5, D>;
opcode_table_cb[0xeb] = &CPU::op_set_n_r<5, E>;
opcode_table_cb[0xec] = &CPU::op_set_n_r<5, H>;
opcode_table_cb[0xed] = &CPU::op_set_n_r<5, L>;
opcode_table_cb[0xee] = &CPU::op_set_n_hl<5>;
opcode_table_cb[0xef] = &CPU::op_set_n_r<5, A>;
opcode_table_cb[0xf0] = &CPU::op_set_n_r<6, B>;
opcode_table_cb[0xf1] = &CPU::op_set_n_r<6, C>;
opcode_table_cb[0xf2] = &CPU::op_set_n_r<6, D>;
opcode_table_cb[0xf3] = &CPU::op_set_n_r<6, E>;
opcode_table_cb[0xf4] = &CPU::op_set_n_r<6, H>;
opcode_table_cb[0xf5] = &CPU::op_set_n_r<6, L>;
opcode_table_cb[0xf6] = &CPU::op_set_n_hl<6>;
opcode_table_cb[0xf7] = &CPU::op_set_n_r<6, A>;
opcode_table_cb[0xf8] = &CPU::op_set_n_r<7, B>;
opcode_table_cb[0xf9] = &CPU::op_set_n_r<7, C>;
opcode_table_cb[0xfa] = &CPU::op_set_n_r<7, D>;
opcode_table_cb[0xfb] = &CPU::op_set_n_r<7, E>;
opcode_table_cb[0xfc] = &CPU::op_set_n_r<7, H>;
opcode_table_cb[0xfd] = &CPU::op_set_n_r<7, L>;
opcode_table_cb[0xfe] = &CPU::op_set_n_hl<7>;
opcode_table_cb[0xff] = &CPU::op_set_n_r<7, A>;
}
#endif

156
bsnes/gameboy/cpu/cpu.cpp Executable file
View File

@@ -0,0 +1,156 @@
#include <gameboy/gameboy.hpp>
#define CPU_CPP
namespace GameBoy {
#include "core/core.cpp"
#include "mmio/mmio.cpp"
#include "timing/timing.cpp"
#include "serialization.cpp"
CPU cpu;
void CPU::Main() {
cpu.main();
}
void CPU::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(trace) print(disassemble(r[PC]), "\n");
interrupt_test();
uint8 opcode = op_read(r[PC]++);
(this->*opcode_table[opcode])();
}
}
void CPU::interrupt_raise(CPU::Interrupt id) {
if(id == Interrupt::Vblank) {
status.interrupt_request_vblank = 1;
if(status.interrupt_enable_vblank) status.halt = false;
}
if(id == Interrupt::Stat) {
status.interrupt_request_stat = 1;
if(status.interrupt_enable_stat) status.halt = false;
}
if(id == Interrupt::Timer) {
status.interrupt_request_timer = 1;
if(status.interrupt_enable_timer) status.halt = false;
}
if(id == Interrupt::Serial) {
status.interrupt_request_serial = 1;
if(status.interrupt_enable_serial) status.halt = false;
}
if(id == Interrupt::Joypad) {
status.interrupt_request_joypad = 1;
if(status.interrupt_enable_joypad) status.halt = status.stop = false;
}
}
void CPU::interrupt_test() {
if(status.ime) {
if(status.interrupt_request_vblank && status.interrupt_enable_vblank) {
status.interrupt_request_vblank = 0;
return interrupt_exec(0x0040);
}
if(status.interrupt_request_stat && status.interrupt_enable_stat) {
status.interrupt_request_stat = 0;
return interrupt_exec(0x0048);
}
if(status.interrupt_request_timer && status.interrupt_enable_timer) {
status.interrupt_request_timer = 0;
return interrupt_exec(0x0050);
}
if(status.interrupt_request_serial && status.interrupt_enable_serial) {
status.interrupt_request_serial = 0;
return interrupt_exec(0x0058);
}
if(status.interrupt_request_joypad && status.interrupt_enable_joypad) {
status.interrupt_request_joypad = 0;
return interrupt_exec(0x0060);
}
}
}
void CPU::interrupt_exec(uint16 pc) {
status.ime = 0;
op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0);
r[PC] = pc;
op_io();
op_io();
op_io();
}
void CPU::power() {
create(Main, 4194304);
for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
for(unsigned n = 0xff00; n <= 0xff0f; n++) bus.mmio[n] = this; //MMIO
for(unsigned n = 0xff80; n <= 0xffff; n++) bus.mmio[n] = this; //HRAM+IE
for(unsigned n = 0; n < 8192; n++) wram[n] = 0x00;
for(unsigned n = 0; n < 128; n++) hram[n] = 0x00;
r[PC] = 0x0000;
r[SP] = 0x0000;
r[AF] = 0x0000;
r[BC] = 0x0000;
r[DE] = 0x0000;
r[HL] = 0x0000;
status.clock = 0;
status.halt = false;
status.stop = false;
status.ime = 0;
status.p15 = 0;
status.p14 = 0;
status.joyp = 0;
status.mlt_req = 0;
status.serial_data = 0;
status.serial_bits = 0;
status.serial_transfer = 0;
status.serial_clock = 0;
status.div = 0;
status.tima = 0;
status.tma = 0;
status.timer_enable = 0;
status.timer_clock = 0;
status.interrupt_request_joypad = 0;
status.interrupt_request_serial = 0;
status.interrupt_request_timer = 0;
status.interrupt_request_stat = 0;
status.interrupt_request_vblank = 0;
status.interrupt_enable_joypad = 0;
status.interrupt_enable_serial = 0;
status.interrupt_enable_timer = 0;
status.interrupt_enable_stat = 0;
status.interrupt_enable_vblank = 0;
}
CPU::CPU() : trace(false) {
initialize_opcode_table();
}
}

78
bsnes/gameboy/cpu/cpu.hpp Executable file
View File

@@ -0,0 +1,78 @@
struct CPU : Processor, MMIO {
#include "core/core.hpp"
#include "mmio/mmio.hpp"
#include "timing/timing.hpp"
bool trace;
enum class Interrupt : unsigned {
Vblank,
Stat,
Timer,
Serial,
Joypad,
};
struct Status {
unsigned clock;
bool halt;
bool stop;
bool ime;
//$ff00 JOYP
bool p15;
bool p14;
uint8 joyp;
uint8 mlt_req;
//$ff01 SB
uint8 serial_data;
unsigned serial_bits;
//$ff02 SC
bool serial_transfer;
bool serial_clock;
//$ff04 DIV
uint8 div;
//$ff05 TIMA
uint8 tima;
//$ff06 TMA
uint8 tma;
//$ff07 TAC
bool timer_enable;
unsigned timer_clock;
//$ff0f IF
bool interrupt_request_joypad;
bool interrupt_request_serial;
bool interrupt_request_timer;
bool interrupt_request_stat;
bool interrupt_request_vblank;
//$ffff IE
bool interrupt_enable_joypad;
bool interrupt_enable_serial;
bool interrupt_enable_timer;
bool interrupt_enable_stat;
bool interrupt_enable_vblank;
} status;
uint8 wram[8192];
uint8 hram[128];
static void Main();
void main();
void interrupt_raise(Interrupt id);
void interrupt_test();
void interrupt_exec(uint16 pc);
void power();
void serialize(serializer&);
CPU();
};
extern CPU cpu;

144
bsnes/gameboy/cpu/mmio/mmio.cpp Executable file
View File

@@ -0,0 +1,144 @@
#ifdef CPU_CPP
void CPU::mmio_joyp_poll() {
unsigned button = 0, dpad = 0;
button |= system.interface->input_poll((unsigned)Input::Start) << 3;
button |= system.interface->input_poll((unsigned)Input::Select) << 2;
button |= system.interface->input_poll((unsigned)Input::B) << 1;
button |= system.interface->input_poll((unsigned)Input::A) << 0;
dpad |= system.interface->input_poll((unsigned)Input::Down) << 3;
dpad |= system.interface->input_poll((unsigned)Input::Up) << 2;
dpad |= system.interface->input_poll((unsigned)Input::Left) << 1;
dpad |= system.interface->input_poll((unsigned)Input::Right) << 0;
status.joyp = 0x0f;
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
if(status.p15 == 0) status.joyp &= button ^ 0x0f;
if(status.p14 == 0) status.joyp &= dpad ^ 0x0f;
if(status.joyp != 0x0f) interrupt_raise(Interrupt::Joypad);
}
uint8 CPU::mmio_read(uint16 addr) {
if(addr >= 0xc000 && addr <= 0xdfff) return wram[addr & 0x1fff];
if(addr >= 0xe000 && addr <= 0xfdff) return wram[addr & 0x1fff];
if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f];
if(addr == 0xff00) { //JOYP
return (status.p15 << 5)
| (status.p14 << 4)
| (status.joyp << 0);
}
if(addr == 0xff01) { //SB
return 0xff;
}
if(addr == 0xff02) { //SC
return (status.serial_transfer << 7)
| (status.serial_clock << 0);
}
if(addr == 0xff04) { //DIV
return status.div;
}
if(addr == 0xff05) { //TIMA
return status.tima;
}
if(addr == 0xff06) { //TMA
return status.tma;
}
if(addr == 0xff07) { //TAC
return (status.timer_enable << 2)
| (status.timer_clock << 0);
}
if(addr == 0xff0f) { //IF
return (status.interrupt_request_joypad << 4)
| (status.interrupt_request_serial << 3)
| (status.interrupt_request_timer << 2)
| (status.interrupt_request_stat << 1)
| (status.interrupt_request_vblank << 0);
}
if(addr == 0xffff) { //IE
return (status.interrupt_enable_joypad << 4)
| (status.interrupt_enable_serial << 3)
| (status.interrupt_enable_timer << 2)
| (status.interrupt_enable_stat << 1)
| (status.interrupt_enable_vblank << 0);
}
return 0x00;
}
void CPU::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0xc000 && addr <= 0xdfff) { wram[addr & 0x1fff] = data; return; }
if(addr >= 0xe000 && addr <= 0xfdff) { wram[addr & 0x1fff] = data; return; }
if(addr >= 0xff80 && addr <= 0xfffe) { hram[addr & 0x7f] = data; return; }
if(addr == 0xff00) { //JOYP
status.p15 = data & 0x20;
status.p14 = data & 0x10;
system.interface->joyp_write(status.p15, status.p14);
mmio_joyp_poll();
return;
}
if(addr == 0xff01) { //SB
status.serial_data = data;
return;
}
if(addr == 0xff02) { //SC
status.serial_transfer = data & 0x80;
status.serial_clock = data & 0x01;
if(status.serial_transfer) status.serial_bits = 8;
return;
}
if(addr == 0xff04) { //DIV
status.div = 0;
return;
}
if(addr == 0xff05) { //TIMA
status.tima = data;
return;
}
if(addr == 0xff06) { //TMA
status.tma = data;
return;
}
if(addr == 0xff07) { //TAC
status.timer_enable = data & 0x04;
status.timer_clock = data & 0x03;
return;
}
if(addr == 0xff0f) { //IF
status.interrupt_request_joypad = data & 0x10;
status.interrupt_request_serial = data & 0x08;
status.interrupt_request_timer = data & 0x04;
status.interrupt_request_stat = data & 0x02;
status.interrupt_request_vblank = data & 0x01;
return;
}
if(addr == 0xffff) { //IE
status.interrupt_enable_joypad = data & 0x10;
status.interrupt_enable_serial = data & 0x08;
status.interrupt_enable_timer = data & 0x04;
status.interrupt_enable_stat = data & 0x02;
status.interrupt_enable_vblank = data & 0x01;
return;
}
}
#endif

View File

@@ -0,0 +1,3 @@
void mmio_joyp_poll();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);

View File

@@ -0,0 +1,56 @@
#ifdef CPU_CPP
void CPU::serialize(serializer &s) {
s.array(wram);
s.array(hram);
s.integer(r.a.data);
s.integer(r.f.z);
s.integer(r.f.n);
s.integer(r.f.h);
s.integer(r.f.c);
s.integer(r.b.data);
s.integer(r.c.data);
s.integer(r.d.data);
s.integer(r.e.data);
s.integer(r.h.data);
s.integer(r.l.data);
s.integer(r.sp.data);
s.integer(r.pc.data);
s.integer(status.clock);
s.integer(status.halt);
s.integer(status.stop);
s.integer(status.ime);
s.integer(status.p15);
s.integer(status.p14);
s.integer(status.joyp);
s.integer(status.mlt_req);
s.integer(status.serial_data);
s.integer(status.serial_bits);
s.integer(status.serial_transfer);
s.integer(status.serial_clock);
s.integer(status.div);
s.integer(status.tima);
s.integer(status.tma);
s.integer(status.timer_enable);
s.integer(status.timer_clock);
s.integer(status.interrupt_request_joypad);
s.integer(status.interrupt_request_serial);
s.integer(status.interrupt_request_timer);
s.integer(status.interrupt_request_stat);
s.integer(status.interrupt_request_vblank);
s.integer(status.interrupt_enable_joypad);
s.integer(status.interrupt_enable_serial);
s.integer(status.interrupt_enable_timer);
s.integer(status.interrupt_enable_stat);
s.integer(status.interrupt_enable_vblank);
}
#endif

View File

@@ -0,0 +1,18 @@
#ifdef CPU_CPP
void CPU::op_io() {
add_clocks(4);
}
uint8 CPU::op_read(uint16 addr) {
uint8 r = bus.read(addr);
add_clocks(4);
return r;
}
void CPU::op_write(uint16 addr, uint8 data) {
bus.write(addr, data);
add_clocks(4);
}
#endif

View File

@@ -0,0 +1,82 @@
//4194304hz (4 * 1024 * 1024)
//70224 clocks/frame
// 456 clocks/scanline
// 154 scanlines/frame
#ifdef CPU_CPP
#include "opcode.cpp"
void CPU::add_clocks(unsigned clocks) {
system.clocks_executed += clocks;
scheduler.exit(Scheduler::ExitReason::StepEvent);
status.clock += clocks;
if(status.clock >= 4194304) {
status.clock -= 4194304;
cartridge.mbc3.second();
}
//4194304 / N(hz) - 1 = mask
if((status.clock & 15) == 0) timer_262144hz();
if((status.clock & 63) == 0) timer_65536hz();
if((status.clock & 255) == 0) timer_16384hz();
if((status.clock & 511) == 0) timer_8192hz();
if((status.clock & 1023) == 0) timer_4096hz();
lcd.clock -= clocks;
if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread);
apu.clock -= clocks;
if(apu.clock <= 0) co_switch(scheduler.active_thread = apu.thread);
}
void CPU::timer_262144hz() {
if(status.timer_enable && status.timer_clock == 1) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
}
void CPU::timer_65536hz() {
if(status.timer_enable && status.timer_clock == 2) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
}
void CPU::timer_16384hz() {
if(status.timer_enable && status.timer_clock == 3) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
status.div++;
}
void CPU::timer_8192hz() {
if(status.serial_transfer && status.serial_clock) {
if(--status.serial_bits == 0) {
status.serial_transfer = 0;
interrupt_raise(Interrupt::Serial);
}
}
}
void CPU::timer_4096hz() {
if(status.timer_enable && status.timer_clock == 0) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
void add_clocks(unsigned clocks);
void timer_262144hz();
void timer_65536hz();
void timer_16384hz();
void timer_8192hz();
void timer_4096hz();
//opcode.cpp
void op_io();
uint8 op_read(uint16 addr);
void op_write(uint16 addr, uint8 data);

96
bsnes/gameboy/gameboy.hpp Executable file
View File

@@ -0,0 +1,96 @@
//bgameboy
//author: byuu
//project started: 2010-12-27
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static const char Version[] = "000.19";
static unsigned SerializerVersion = 1;
}
}
#include <libco/libco.h>
#include <nall/foreach.hpp>
#include <nall/platform.hpp>
#include <nall/property.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/varint.hpp>
using namespace nall;
namespace GameBoy {
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef uint_t< 1> uint1;
typedef uint_t< 2> uint2;
typedef uint_t< 3> uint3;
typedef uint_t< 4> uint4;
typedef uint_t< 5> uint5;
typedef uint_t< 6> uint6;
typedef uint_t< 7> uint7;
typedef uint_t< 9> uint9;
typedef uint_t<10> uint10;
typedef uint_t<11> uint11;
typedef uint_t<12> uint12;
typedef uint_t<13> uint13;
typedef uint_t<14> uint14;
typedef uint_t<15> uint15;
typedef uint_t<17> uint17;
typedef uint_t<18> uint18;
typedef uint_t<19> uint19;
typedef uint_t<20> uint20;
typedef uint_t<21> uint21;
typedef uint_t<22> uint22;
typedef uint_t<23> uint23;
typedef uint_t<24> uint24;
typedef uint_t<25> uint25;
typedef uint_t<26> uint26;
typedef uint_t<27> uint27;
typedef uint_t<28> uint28;
typedef uint_t<29> uint29;
typedef uint_t<30> uint30;
typedef uint_t<31> uint31;
template<uint16 lo, uint16 hi>
alwaysinline bool within(uint16 addr) {
static const uint16 mask = ~(hi ^ lo);
return (addr & mask) == lo;
}
struct Processor {
cothread_t thread;
unsigned frequency;
int64 clock;
inline void create(void (*entrypoint_)(), unsigned frequency_) {
if(thread) co_delete(thread);
thread = co_create(65536 * sizeof(void*), entrypoint_);
frequency = frequency_;
clock = 0;
}
inline Processor() : thread(0) {}
};
#include <gameboy/memory/memory.hpp>
#include <gameboy/system/system.hpp>
#include <gameboy/scheduler/scheduler.hpp>
#include <gameboy/cartridge/cartridge.hpp>
#include <gameboy/cpu/cpu.hpp>
#include <gameboy/apu/apu.hpp>
#include <gameboy/lcd/lcd.hpp>
};

View File

@@ -0,0 +1,11 @@
class Interface {
public:
virtual void joyp_write(bool p15, bool p14) {}
virtual void video_refresh(const uint8_t *data) {}
virtual void audio_sample(int16_t center, int16_t left, int16_t right) {}
virtual void input_poll() {}
virtual bool input_poll(unsigned id) {}
virtual void message(const string &text) { print(text, "\n"); }
};

238
bsnes/gameboy/lcd/lcd.cpp Executable file
View File

@@ -0,0 +1,238 @@
#include <gameboy/gameboy.hpp>
#define LCD_CPP
namespace GameBoy {
#include "mmio/mmio.cpp"
#include "serialization.cpp"
LCD lcd;
void LCD::Main() {
lcd.main();
}
void LCD::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
add_clocks(4);
if(status.lx == 0) {
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.lx == 252) {
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
}
}
void LCD::add_clocks(unsigned clocks) {
status.lx += clocks;
if(status.lx >= 456) scanline();
clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread);
}
}
void LCD::scanline() {
status.lx -= 456;
if(++status.ly == 154) frame();
if(status.interrupt_lyc == true) {
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.ly < 144) render();
if(status.ly == 144) {
cpu.interrupt_raise(CPU::Interrupt::Vblank);
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
}
void LCD::frame() {
system.interface->video_refresh(screen);
system.interface->input_poll();
cpu.mmio_joyp_poll();
status.ly = 0;
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
void LCD::render() {
for(unsigned n = 0; n < 160; n++) line[n] = 0x00;
if(status.display_enable == true) {
if(status.bg_enable == true) render_bg();
if(status.window_display_enable == true) render_window();
if(status.obj_enable == true) render_obj();
}
uint8_t *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = (3 - line[n]) * 0x55;
}
uint16 LCD::read_tile(bool select, unsigned x, unsigned y) {
unsigned tmaddr = 0x1800 + (select << 10), tdaddr;
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
if(status.bg_tiledata_select == 0) {
tdaddr = 0x1000 + ((int8)vram[tmaddr] << 4);
} else {
tdaddr = 0x0000 + (vram[tmaddr] << 4);
}
tdaddr += (y & 7) << 1;
return (vram[tdaddr + 0] << 0) | (vram[tdaddr + 1] << 8);
}
void LCD::render_bg() {
unsigned iy = (status.ly + status.scy) & 255;
unsigned ix = status.scx, tx = ix & 7;
unsigned data = read_tile(status.bg_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
line[ox] = status.bgp[palette];
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = read_tile(status.bg_tilemap_select, ix, iy);
}
}
void LCD::render_window() {
if(status.ly - status.wy >= 144U) return;
unsigned iy = status.ly - status.wy;
unsigned ix = (status.wx - 7) & 255, tx = ix & 7;
unsigned data = read_tile(status.window_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(ox - (status.wx - 7) < 160U) line[ox] = status.bgp[palette];
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = read_tile(status.window_tilemap_select, ix, iy);
}
}
void LCD::render_obj() {
unsigned obj_size = (status.obj_size == 0 ? 8 : 16);
unsigned sprite[10], sprites = 0;
//find first ten sprites on this scanline
for(unsigned s = 0; s < 40; s++) {
unsigned sy = oam[(s << 2) + 0] - 16;
unsigned sx = oam[(s << 2) + 1] - 8;
sy = status.ly - sy;
if(sy >= obj_size) continue;
sprite[sprites++] = s;
if(sprites == 10) break;
}
//sort by X-coordinate, when equal, lower address comes first
for(unsigned x = 0; x < sprites; x++) {
for(unsigned y = x + 1; y < sprites; y++) {
signed sx = oam[(sprite[x] << 2) + 1] - 8;
signed sy = oam[(sprite[y] << 2) + 1] - 8;
if(sy < sx) {
sprite[x] ^= sprite[y];
sprite[y] ^= sprite[x];
sprite[x] ^= sprite[y];
}
}
}
//render backwards, so that first sprite has highest priority
for(signed s = sprites - 1; s >= 0; s--) {
unsigned n = sprite[s] << 2;
unsigned sy = oam[n + 0] - 16;
unsigned sx = oam[n + 1] - 8;
unsigned tile = oam[n + 2];
unsigned attribute = oam[n + 3];
sy = status.ly - sy;
if(sy >= obj_size) continue;
if(attribute & 0x40) sy ^= (obj_size - 1);
unsigned tdaddr = (tile << 4) + (sy << 1);
uint8 d0 = vram[tdaddr + 0];
uint8 d1 = vram[tdaddr + 1];
unsigned xflip = attribute & 0x20 ? 7 : 0;
for(unsigned tx = 0; tx < 8; tx++) {
uint8 palette = ((d0 & (0x80 >> tx)) ? 1 : 0)
| ((d1 & (0x80 >> tx)) ? 2 : 0);
if(palette == 0) continue;
palette = status.obp[(bool)(attribute & 0x10)][palette];
unsigned ox = sx + (tx ^ xflip);
if(ox <= 159) {
if(attribute & 0x80) {
if(line[ox] > 0) continue;
}
line[ox] = palette;
}
}
}
}
void LCD::power() {
create(Main, 4194304);
for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
for(unsigned n = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO
for(unsigned n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM
for(unsigned n = 0; n < 8192; n++) vram[n] = 0x00;
for(unsigned n = 0; n < 160; n++) oam [n] = 0x00;
for(unsigned n = 0; n < 160 * 144; n++) screen[n] = 0x00;
status.lx = 0;
status.display_enable = 0;
status.window_tilemap_select = 0;
status.window_display_enable = 0;
status.bg_tiledata_select = 0;
status.bg_tilemap_select = 0;
status.obj_size = 0;
status.obj_enable = 0;
status.bg_enable = 0;
status.interrupt_lyc = 0;
status.interrupt_oam = 0;
status.interrupt_vblank = 0;
status.interrupt_hblank = 0;
status.scy = 0;
status.scx = 0;
status.ly = 0;
status.lyc = 0;
for(unsigned n = 0; n < 4; n++) {
status.bgp[n] = n;
status.obp[0][n] = n;
status.obp[1][n] = n;
}
status.wy = 0;
status.wx = 0;
}
LCD::LCD() {
}
}

71
bsnes/gameboy/lcd/lcd.hpp Executable file
View File

@@ -0,0 +1,71 @@
struct LCD : Processor, MMIO {
#include "mmio/mmio.hpp"
struct Status {
unsigned lx;
//$ff40 LCDC
bool display_enable;
bool window_tilemap_select;
bool window_display_enable;
bool bg_tiledata_select;
bool bg_tilemap_select;
bool obj_size;
bool obj_enable;
bool bg_enable;
//$ff41 STAT
bool interrupt_lyc;
bool interrupt_oam;
bool interrupt_vblank;
bool interrupt_hblank;
//$ff42 SCY
uint8 scy;
//$ff43 SCX
uint8 scx;
//$ff44 LY
uint8 ly;
//$ff45 LYC
uint8 lyc;
//$ff47 BGP
uint8 bgp[4];
//$ff48 OBP0
//$ff49 OBP1
uint8 obp[2][4];
//$ff4a WY
uint8 wy;
//$ff4b WX
uint8 wx;
} status;
uint8 screen[160 * 144];
uint8 vram[8192];
uint8 oam[160];
uint8 line[160];
static void Main();
void main();
void add_clocks(unsigned clocks);
void scanline();
void frame();
void render();
uint16 read_tile(bool select, unsigned x, unsigned y);
void render_bg();
void render_window();
void render_obj();
void power();
void serialize(serializer&);
LCD();
};
extern LCD lcd;

165
bsnes/gameboy/lcd/mmio/mmio.cpp Executable file
View File

@@ -0,0 +1,165 @@
#ifdef LCD_CPP
uint8 LCD::mmio_read(uint16 addr) {
if(addr >= 0x8000 && addr <= 0x9fff) return vram[addr & 0x1fff];
if(addr >= 0xfe00 && addr <= 0xfe9f) return oam[addr & 0xff];
if(addr == 0xff40) { //LCDC
return (status.display_enable << 7)
| (status.window_tilemap_select << 6)
| (status.window_display_enable << 5)
| (status.bg_tiledata_select << 4)
| (status.bg_tilemap_select << 3)
| (status.obj_size << 2)
| (status.obj_enable << 1)
| (status.bg_enable << 0);
}
if(addr == 0xff41) { //STAT
unsigned mode;
if(status.ly >= 144) mode = 1; //Vblank
else if(status.lx < 80) mode = 2; //OAM
else if(status.lx < 252) mode = 3; //LCD
else mode = 0; //Hblank
return (status.interrupt_lyc << 6)
| (status.interrupt_oam << 5)
| (status.interrupt_vblank << 4)
| (status.interrupt_hblank << 3)
| ((status.ly == status.lyc) << 2)
| (mode << 0);
}
if(addr == 0xff42) { //SCY
return status.scy;
}
if(addr == 0xff43) { //SCX
return status.scx;
}
if(addr == 0xff44) { //LY
return status.ly;
}
if(addr == 0xff45) { //LYC
return status.lyc;
}
if(addr == 0xff47) { //BGP
return (status.bgp[3] << 6)
| (status.bgp[2] << 4)
| (status.bgp[1] << 2)
| (status.bgp[0] << 0);
}
if(addr == 0xff48) { //OBP0
return (status.obp[0][3] << 6)
| (status.obp[0][2] << 4)
| (status.obp[0][1] << 2)
| (status.obp[0][0] << 0);
}
if(addr == 0xff49) { //OBP1
return (status.obp[1][3] << 6)
| (status.obp[1][2] << 4)
| (status.obp[1][1] << 2)
| (status.obp[1][0] << 0);
}
if(addr == 0xff4a) { //WY
return status.wy;
}
if(addr == 0xff4b) { //WX
return status.wx;
}
return 0x00;
}
void LCD::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0x8000 && addr <= 0x9fff) { vram[addr & 0x1fff] = data; return; }
if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; }
if(addr == 0xff40) { //LCDC
status.display_enable = data & 0x80;
status.window_tilemap_select = data & 0x40;
status.window_display_enable = data & 0x20;
status.bg_tiledata_select = data & 0x10;
status.bg_tilemap_select = data & 0x08;
status.obj_size = data & 0x04;
status.obj_enable = data & 0x02;
status.bg_enable = data & 0x01;
return;
}
if(addr == 0xff41) { //STAT
status.interrupt_lyc = data & 0x40;
status.interrupt_oam = data & 0x20;
status.interrupt_vblank = data & 0x10;
status.interrupt_hblank = data & 0x08;
return;
}
if(addr == 0xff42) { //SCY
status.scy = data;
return;
}
if(addr == 0xff43) { //SCX
status.scx = data;
return;
}
if(addr == 0xff44) { //LY
status.ly = 0;
return;
}
if(addr == 0xff45) { //LYC
status.lyc = data;
return;
}
if(addr == 0xff46) { //DMA
for(unsigned n = 0x00; n <= 0x9f; n++) bus.write(0xfe00 + n, bus.read((data << 8) + n));
return;
}
if(addr == 0xff47) { //BGP
status.bgp[3] = (data >> 6) & 3;
status.bgp[2] = (data >> 4) & 3;
status.bgp[1] = (data >> 2) & 3;
status.bgp[0] = (data >> 0) & 3;
return;
}
if(addr == 0xff48) { //OBP0
status.obp[0][3] = (data >> 6) & 3;
status.obp[0][2] = (data >> 4) & 3;
status.obp[0][1] = (data >> 2) & 3;
status.obp[0][0] = (data >> 0) & 3;
return;
}
if(addr == 0xff49) { //OBP1
status.obp[1][3] = (data >> 6) & 3;
status.obp[1][2] = (data >> 4) & 3;
status.obp[1][1] = (data >> 2) & 3;
status.obp[1][0] = (data >> 0) & 3;
return;
}
if(addr == 0xff4a) { //WY
status.wy = data;
return;
}
if(addr == 0xff4b) { //WX
status.wx = data;
return;
}
}
#endif

View File

@@ -0,0 +1,2 @@
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);

View File

@@ -0,0 +1,38 @@
#ifdef LCD_CPP
void LCD::serialize(serializer &s) {
s.integer(status.lx);
s.integer(status.display_enable);
s.integer(status.window_tilemap_select);
s.integer(status.window_display_enable);
s.integer(status.bg_tiledata_select);
s.integer(status.bg_tilemap_select);
s.integer(status.obj_size);
s.integer(status.obj_enable);
s.integer(status.bg_enable);
s.integer(status.interrupt_lyc);
s.integer(status.interrupt_oam);
s.integer(status.interrupt_vblank);
s.integer(status.interrupt_hblank);
s.integer(status.scy);
s.integer(status.scx);
s.integer(status.ly);
s.integer(status.lyc);
s.array(status.bgp);
s.array(status.obp[0]);
s.array(status.obp[1]);
s.integer(status.wy);
s.integer(status.wx);
s.array(screen);
s.array(vram);
s.array(oam);
s.array(line);
}
#endif

56
bsnes/gameboy/memory/memory.cpp Executable file
View File

@@ -0,0 +1,56 @@
#include <gameboy/gameboy.hpp>
#define MEMORY_CPP
namespace GameBoy {
Unmapped unmapped;
Bus bus;
uint8_t& Memory::operator[](unsigned addr) {
return data[addr];
}
void Memory::allocate(unsigned size_) {
free();
size = size_;
data = new uint8_t[size]();
}
void Memory::copy(const uint8_t *data_, unsigned size_) {
free();
size = size_;
data = new uint8_t[size];
memcpy(data, data_, size);
}
void Memory::free() {
if(data) {
delete[] data;
data = 0;
}
}
Memory::Memory() {
data = 0;
size = 0;
}
Memory::~Memory() {
free();
}
//
uint8 Bus::read(uint16 addr) {
return mmio[addr]->mmio_read(addr);
}
void Bus::write(uint16 addr, uint8 data) {
mmio[addr]->mmio_write(addr, data);
}
void Bus::power() {
for(unsigned n = 0x0000; n <= 0xffff; n++) mmio[n] = &unmapped;
}
}

32
bsnes/gameboy/memory/memory.hpp Executable file
View File

@@ -0,0 +1,32 @@
struct Memory {
uint8_t *data;
unsigned size;
uint8_t& operator[](unsigned addr);
void allocate(unsigned size);
void copy(const uint8_t *data, unsigned size);
void free();
Memory();
~Memory();
};
struct MMIO {
virtual uint8 mmio_read(uint16 addr) = 0;
virtual void mmio_write(uint16 addr, uint8 data) = 0;
};
struct Unmapped : MMIO {
uint8 mmio_read(uint16) { return 0x00; }
void mmio_write(uint16, uint8) {}
};
struct Bus {
MMIO *mmio[65536];
uint8 read(uint16 addr);
void write(uint16 addr, uint8 data);
void power();
};
extern Unmapped unmapped;
extern Bus bus;

View File

@@ -0,0 +1,35 @@
#include <gameboy/gameboy.hpp>
#define SCHEDULER_CPP
namespace GameBoy {
Scheduler scheduler;
void Scheduler::enter() {
host_thread = co_active();
co_switch(active_thread);
}
void Scheduler::exit(ExitReason reason) {
exit_reason = reason;
active_thread = co_active();
co_switch(host_thread);
}
void Scheduler::swapto(Processor &p) {
active_thread = p.thread;
co_switch(active_thread);
}
void Scheduler::init() {
host_thread = co_active();
active_thread = cpu.thread;
}
Scheduler::Scheduler() {
exit_reason = ExitReason::UnknownEvent;
host_thread = 0;
active_thread = 0;
}
}

View File

@@ -0,0 +1,17 @@
struct Scheduler : property<Scheduler> {
enum class SynchronizeMode : unsigned { None, CPU, All } sync;
enum class ExitReason : unsigned { UnknownEvent, StepEvent, FrameEvent, SynchronizeEvent };
readonly<ExitReason> exit_reason;
cothread_t host_thread;
cothread_t active_thread;
void enter();
void exit(ExitReason);
void swapto(Processor&);
void init();
Scheduler();
};
extern Scheduler scheduler;

View File

@@ -0,0 +1,23 @@
#ifdef SYSTEM_CPP
//MD5SUM = 32fbbd84168d3482956eb3c5051637f5
const uint8_t System::BootROM::dmg[256] = {
0x31,0xfe,0xff,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,0x21,0x26,0xff,0x0e,
0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,0x77,0x3e,0xfc,0xe0,
0x47,0x11,0x04,0x01,0x21,0x10,0x80,0x1a,0xcd,0x95,0x00,0xcd,0x96,0x00,0x13,0x7b,
0xfe,0x34,0x20,0xf3,0x11,0xd8,0x00,0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,
0x3e,0x19,0xea,0x10,0x99,0x21,0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,
0xf9,0x2e,0x0f,0x18,0xf3,0x67,0x3e,0x64,0x57,0xe0,0x42,0x3e,0x91,0xe0,0x40,0x04,
0x1e,0x02,0x0e,0x0c,0xf0,0x44,0xfe,0x90,0x20,0xfa,0x0d,0x20,0xf7,0x1d,0x20,0xf2,
0x0e,0x13,0x24,0x7c,0x1e,0x83,0xfe,0x62,0x28,0x06,0x1e,0xc1,0xfe,0x64,0x20,0x06,
0x7b,0xe2,0x0c,0x3e,0x87,0xe2,0xf0,0x42,0x90,0xe0,0x42,0x15,0x20,0xd2,0x05,0x20,
0x4f,0x16,0x20,0x18,0xcb,0x4f,0x06,0x04,0xc5,0xcb,0x11,0x17,0xc1,0xcb,0x11,0x17,
0x05,0x20,0xf5,0x22,0x23,0x22,0x23,0xc9,0xce,0xed,0x66,0x66,0xcc,0x0d,0x00,0x0b,
0x03,0x73,0x00,0x83,0x00,0x0c,0x00,0x0d,0x00,0x08,0x11,0x1f,0x88,0x89,0x00,0x0e,
0xdc,0xcc,0x6e,0xe6,0xdd,0xdd,0xd9,0x99,0xbb,0xbb,0x67,0x63,0x6e,0x0e,0xec,0xcc,
0xdd,0xdc,0x99,0x9f,0xbb,0xb9,0x33,0x3e,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,
0x21,0x04,0x01,0x11,0xa8,0x00,0x1a,0x13,0xbe,0x20,0xfe,0x23,0x7d,0xfe,0x34,0x20,
0xf5,0x06,0x19,0x78,0x86,0x23,0x05,0x20,0xfb,0x86,0x20,0xfe,0x3e,0x01,0xe0,0x50,
};
#endif

View File

@@ -0,0 +1,23 @@
#ifdef SYSTEM_CPP
//MD5SUM = d574d4f9c12f305074798f54c091a8b4
const uint8_t System::BootROM::sgb[256] = {
0x31,0xfe,0xff,0x3e,0x30,0xe0,0x00,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,
0x21,0x26,0xff,0x0e,0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,
0x77,0x3e,0xfc,0xe0,0x47,0x21,0x5f,0xc0,0x0e,0x08,0xaf,0x32,0x0d,0x20,0xfc,0x11,
0x4f,0x01,0x3e,0xfb,0x0e,0x06,0xf5,0x06,0x00,0x1a,0x1b,0x32,0x80,0x47,0x0d,0x20,
0xf8,0x32,0xf1,0x32,0x0e,0x0e,0xd6,0x02,0xfe,0xef,0x20,0xea,0x11,0x04,0x01,0x21,
0x10,0x80,0x1a,0xcd,0xd3,0x00,0xcd,0xd4,0x00,0x13,0x7b,0xfe,0x34,0x20,0xf3,0x11,
0xe6,0x00,0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,0x3e,0x19,0xea,0x10,0x99,
0x21,0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,0xf9,0x2e,0x0f,0x18,0xf3,
0x3e,0x91,0xe0,0x40,0x21,0x00,0xc0,0x0e,0x00,0x3e,0x00,0xe2,0x3e,0x30,0xe2,0x06,
0x10,0x1e,0x08,0x2a,0x57,0xcb,0x42,0x3e,0x10,0x20,0x02,0x3e,0x20,0xe2,0x3e,0x30,
0xe2,0xcb,0x1a,0x1d,0x20,0xef,0x05,0x20,0xe8,0x3e,0x20,0xe2,0x3e,0x30,0xe2,0xcd,
0xc2,0x00,0x7d,0xfe,0x60,0x20,0xd2,0x0e,0x13,0x3e,0xc1,0xe2,0x0c,0x3e,0x07,0xe2,
0x18,0x3a,0x16,0x04,0xf0,0x44,0xfe,0x90,0x20,0xfa,0x1e,0x00,0x1d,0x20,0xfd,0x15,
0x20,0xf2,0xc9,0x4f,0x06,0x04,0xc5,0xcb,0x11,0x17,0xc1,0xcb,0x11,0x17,0x05,0x20,
0xf5,0x22,0x23,0x22,0x23,0xc9,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x01,0xe0,0x50,
};
#endif

View File

@@ -0,0 +1,63 @@
#ifdef SYSTEM_CPP
serializer System::serialize() {
serializer s(serialize_size);
unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = 0;
char description[512];
memset(&description, 0, sizeof description);
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
serialize_all(s);
return s;
}
bool System::unserialize(serializer &s) {
unsigned signature, version, crc32;
char description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
if(signature != 0x31545342) return false;
if(version != Info::SerializerVersion) return false;
//if(crc32 != 0) return false;
serialize_all(s);
return true;
}
void System::serialize(serializer &s) {
s.integer(clocks_executed);
}
void System::serialize_all(serializer &s) {
cartridge.serialize(s);
system.serialize(s);
cpu.serialize(s);
apu.serialize(s);
lcd.serialize(s);
}
void System::serialize_init() {
serializer s;
unsigned signature = 0, version = 0, crc32 = 0;
char description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
serialize_all(s);
serialize_size = s.size();
}
#endif

55
bsnes/gameboy/system/system.cpp Executable file
View File

@@ -0,0 +1,55 @@
#include <gameboy/gameboy.hpp>
#define SYSTEM_CPP
namespace GameBoy {
#include "bootrom-dmg.cpp"
#include "bootrom-sgb.cpp"
#include "serialization.cpp"
System system;
void System::run() {
scheduler.sync = Scheduler::SynchronizeMode::None;
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
}
}
void System::runtosave() {
scheduler.sync = Scheduler::SynchronizeMode::CPU;
runthreadtosave();
scheduler.active_thread = lcd.thread;
runthreadtosave();
}
void System::runthreadtosave() {
while(true) {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
}
}
}
void System::init(Interface *interface_) {
interface = interface_;
}
void System::load() {
serialize_init();
}
void System::power() {
bus.power();
cartridge.power();
cpu.power();
apu.power();
lcd.power();
scheduler.init();
clocks_executed = 0;
}
}

37
bsnes/gameboy/system/system.hpp Executable file
View File

@@ -0,0 +1,37 @@
class Interface;
enum class Input : unsigned {
Up, Down, Left, Right, B, A, Select, Start,
};
struct System {
struct BootROM {
static const uint8 dmg[256];
static const uint8 sgb[256];
} bootROM;
void run();
void runtosave();
void runthreadtosave();
void init(Interface*);
void load();
void power();
Interface *interface;
unsigned clocks_executed;
//serialization.cpp
unsigned serialize_size;
serializer serialize();
bool unserialize(serializer&);
void serialize(serializer&);
void serialize_all(serializer&);
void serialize_init();
};
#include <gameboy/interface/interface.hpp>
extern System system;

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="bsnes" version="1.0.0.0" processorArchitecture="x86"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
</assembly>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,4 +0,0 @@
@windres resource.rc resource.o
@mingw32-g++ -std=gnu++0x -mwindows -s -O3 -fomit-frame-pointer -I.. -o ../out/bsnes launcher.cpp resource.o
@del *.o
@pause

View File

@@ -1,2 +0,0 @@
clear
g++ -std=gnu++0x -s -O3 -fomit-frame-pointer -I.. -o ../out/bsnes launcher.cpp

View File

@@ -1,54 +0,0 @@
#include <nall/config.hpp>
#include <nall/detect.hpp>
#include <nall/file.hpp>
#include <nall/foreach.hpp>
#include <nall/platform.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
using namespace nall;
int main(int argc, char **argv) {
char path[PATH_MAX], *unused;
#if !defined(PLATFORM_WIN)
unused = realpath(argv[0], path);
#else
wchar_t **argw = CommandLineToArgvW(GetCommandLineW(), &argc);
unused = realpath(nall::utf8_t(argw[0]), path);
#endif
string realPath = dir(path);
string basePath = string(dir(path), "bsnes-qt.cfg");
unused = userpath(path);
if(!strend(path, "/") && !strend(path, "\\")) strcat(path, "/");
string userPath = string(path, ".bsnes/bsnes-qt.cfg");
configuration config;
string profile;
config.attach(profile = "", "system.profile");
if(config.load(userPath) == false) config.load(basePath);
if(profile == "") profile = "compatibility";
string binaryName = string("bsnes-", profile);
#if defined(PLATFORM_WIN)
binaryName << ".dll";
#endif
string fileName = string(realPath, binaryName);
#if !defined(PLATFORM_WIN)
char **args = new char*[argc + 1];
args[0] = strdup(binaryName);
for(unsigned i = 1; i < argc; i++) args[i] = strdup(argv[i]);
args[argc] = 0;
execvp(args[0], args);
execv(fileName, args);
print("[bsnes] Error: unable to locate binary file: ", binaryName, "\n");
#else
STARTUPINFOW si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(STARTUPINFOW));
if(!CreateProcessW(nall::utf16_t(fileName), GetCommandLineW(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
MessageBoxA(0, string("Error: unable to locate binary file: ", binaryName), "bsnes", MB_OK);
}
#endif
return 0;
}

View File

@@ -1,2 +0,0 @@
1 24 "bsnes.Manifest"
IDI_ICON1 ICON DISCARDABLE "bsnes.ico"

View File

@@ -3,12 +3,12 @@
namespace nall {
template<int bits> inline unsigned uclamp(const unsigned x) {
enum { y = (1U << bits) - 1 };
enum { y = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
return y + ((x - y) & -(x < y)); //min(x, y);
}
template<int bits> inline unsigned uclip(const unsigned x) {
enum { m = (1U << bits) - 1 };
enum { m = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
return (x & m);
}

View File

@@ -5,8 +5,14 @@
#include <nall/concept.hpp>
#undef foreach
#define foreach(iter, object) \
#define foreach2(iter, object) foreach3(iter, object, foreach_counter)
#define foreach3(iter, object, foreach_counter) \
for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \
for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0)
#define foreach_impl(...) foreach_decl(__VA_ARGS__, foreach3(__VA_ARGS__), foreach2(__VA_ARGS__), foreach_too_few_arguments)
#define foreach_decl(_1, _2, _3, N, ...) N
#define foreach(...) foreach_impl(__VA_ARGS__)
#endif

View File

@@ -27,7 +27,7 @@ namespace nall {
};
template<typename L> struct lambda : container {
L object;
mutable L object;
R operator()(P... p) const { return object(std::forward<P>(p)...); }
container* copy() const { return new lambda(object); }
lambda(const L& object) : object(object) {}
@@ -46,7 +46,7 @@ namespace nall {
return *this;
}
function(const function &source) { operator=(source); }
function(const function &source) : callback(0) { operator=(source); }
function() : callback(0) {}
function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); }
function(R (*function)(P...)) { callback = new global(function); }

105
bsnes/nall/gameboy/cartridge.hpp Executable file
View File

@@ -0,0 +1,105 @@
#ifndef NALL_GAMEBOY_CARTRIDGE_HPP
#define NALL_GAMEBOY_CARTRIDGE_HPP
namespace nall {
class GameBoyCartridge {
public:
string xml;
inline GameBoyCartridge(const uint8_t *data, unsigned size);
//private:
struct Information {
string mapper;
bool ram;
bool battery;
bool rtc;
bool rumble;
unsigned romsize;
unsigned ramsize;
} info;
};
GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
if(romsize < 0x4000) return;
info.mapper = "unknown";
info.ram = false;
info.battery = false;
info.rtc = false;
info.rumble = false;
info.romsize = 0;
info.ramsize = 0;
switch(romdata[0x0147]) {
case 0x00: info.mapper = "none"; break;
case 0x01: info.mapper = "MBC1"; break;
case 0x02: info.mapper = "MBC1"; info.ram = true; break;
case 0x03: info.mapper = "MBC1"; info.ram = true; info.battery = true; break;
case 0x05: info.mapper = "MBC2"; info.ram = true; break;
case 0x06: info.mapper = "MBC2"; info.ram = true; info.battery = true; break;
case 0x08: info.mapper = "none"; info.ram = true; break;
case 0x09: info.mapper = "MBC0"; info.ram = true; info.battery = true; break;
case 0x0b: info.mapper = "MMM01"; break;
case 0x0c: info.mapper = "MMM01"; info.ram = true; break;
case 0x0d: info.mapper = "MMM01"; info.ram = true; info.battery = true; break;
case 0x0f: info.mapper = "MBC3"; info.rtc = true; info.battery = true; break;
case 0x10: info.mapper = "MBC3"; info.rtc = true; info.ram = true; info.battery = true; break;
case 0x11: info.mapper = "MBC3"; break;
case 0x12: info.mapper = "MBC3"; info.ram = true; break;
case 0x13: info.mapper = "MBC3"; info.ram = true; info.battery = true; break;
case 0x19: info.mapper = "MBC5"; break;
case 0x1a: info.mapper = "MBC5"; info.ram = true; break;
case 0x1b: info.mapper = "MBC5"; info.ram = true; info.battery = true; break;
case 0x1c: info.mapper = "MBC5"; info.rumble = true; break;
case 0x1d: info.mapper = "MBC5"; info.rumble = true; info.ram = true; break;
case 0x1e: info.mapper = "MBC5"; info.rumble = true; info.ram = true; info.battery = true; break;
case 0xfc: break; //Pocket Camera
case 0xfd: break; //Bandai TAMA5
case 0xfe: info.mapper = "HuC3"; break;
case 0xff: info.mapper = "HuC1"; info.ram = true; info.battery = true; break;
}
switch(romdata[0x0148]) { default:
case 0x00: info.romsize = 2 * 16 * 1024; break;
case 0x01: info.romsize = 4 * 16 * 1024; break;
case 0x02: info.romsize = 8 * 16 * 1024; break;
case 0x03: info.romsize = 16 * 16 * 1024; break;
case 0x04: info.romsize = 32 * 16 * 1024; break;
case 0x05: info.romsize = 64 * 16 * 1024; break;
case 0x06: info.romsize = 128 * 16 * 1024; break;
case 0x07: info.romsize = 256 * 16 * 1024; break;
case 0x52: info.romsize = 72 * 16 * 1024; break;
case 0x53: info.romsize = 80 * 16 * 1024; break;
case 0x54: info.romsize = 96 * 16 * 1024; break;
}
switch(romdata[0x0149]) { default:
case 0x00: info.ramsize = 0 * 1024; break;
case 0x01: info.ramsize = 2 * 1024; break;
case 0x02: info.ramsize = 8 * 1024; break;
case 0x03: info.ramsize = 32 * 1024; break;
}
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
xml << "<cartridge mapper='" << info.mapper << "'";
if(info.rtc) xml << " rtc='true'";
if(info.rumble) xml << " rumble='true'";
xml << ">\n";
xml << " <rom size='" << hex(romsize) << "'/>\n"; //TODO: trust/check info.romsize?
if(info.ramsize > 0)
xml << " <ram size='" << hex(info.ramsize) << "' battery='" << info.battery << "'/>\n";
xml << "</cartridge>\n";
xml.transform("'", "\"");
}
}
#endif

32
bsnes/nall/public_cast.hpp Executable file
View File

@@ -0,0 +1,32 @@
#ifndef NALL_PUBLIC_CAST_HPP
#define NALL_PUBLIC_CAST_HPP
//this is a proof-of-concept-*only* C++ access-privilege elevation exploit.
//this code is 100% legal C++, per C++98 section 14.7.2 paragraph 8:
//"access checking rules do not apply to names in explicit instantiations."
//usage example:
//struct N { typedef void (Class::*)(); };
//template class public_cast<N, &Class::Reference>;
//(class.*public_cast<N>::value);
//Class::Reference may be public, protected or private
//Class::Reference may be a function, object or variable
namespace nall {
template<typename T, typename T::type... P> struct public_cast;
template<typename T> struct public_cast<T> {
static typename T::type value;
};
template<typename T> typename T::type public_cast<T>::value;
template<typename T, typename T::type P> struct public_cast<T, P> {
static typename T::type value;
};
template<typename T, typename T::type P> typename T::type public_cast<T, P>::value = public_cast<T>::value = P;
}
#endif

103
bsnes/nall/reference_array.hpp Executable file
View File

@@ -0,0 +1,103 @@
#ifndef NALL_REFERENCE_ARRAY_HPP
#define NALL_REFERENCE_ARRAY_HPP
#include <type_traits>
#include <nall/bit.hpp>
#include <nall/concept.hpp>
namespace nall {
template<typename T> struct reference_array {
protected:
typedef typename std::remove_reference<T>::type *Tptr;
Tptr *pool;
unsigned poolsize, buffersize;
public:
unsigned size() const { return buffersize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) free(pool);
pool = 0;
poolsize = 0;
buffersize = 0;
}
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
pool = (Tptr*)realloc(pool, newsize * sizeof(T));
poolsize = newsize;
buffersize = min(buffersize, newsize);
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(bit::round(newsize));
buffersize = newsize;
}
void append(const T data) {
unsigned index = buffersize++;
if(index >= poolsize) resize(index + 1);
pool[index] = &data;
}
template<typename... Args> reference_array(Args&... args) : pool(0), poolsize(0), buffersize(0) {
construct(args...);
}
~reference_array() {
reset();
}
reference_array& operator=(const reference_array &source) {
if(pool) free(pool);
buffersize = source.buffersize;
poolsize = source.poolsize;
pool = (Tptr*)malloc(sizeof(T) * poolsize);
memcpy(pool, source.pool, sizeof(T) * buffersize);
return *this;
}
reference_array& operator=(const reference_array &&source) {
if(pool) free(pool);
pool = source.pool;
poolsize = source.poolsize;
buffersize = source.buffersize;
source.pool = 0;
source.reset();
return *this;
}
inline T operator[](unsigned index) {
if(index >= buffersize) throw "reference_array[] out of bounds";
return *pool[index];
}
inline const T operator[](unsigned index) const {
if(index >= buffersize) throw "reference_array[] out of bounds";
return *pool[index];
}
private:
void construct() {
}
void construct(const reference_array &source) {
operator=(source);
}
void construct(const reference_array &&source) {
operator=(std::move(source));
}
template<typename... Args> void construct(T data, Args&... args) {
append(data);
construct(args...);
}
};
template<typename T> struct has_size<reference_array<T>> { enum { value = true }; };
}
#endif

View File

@@ -112,13 +112,13 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
if(type == TypeBsx) {
xml << "<cartridge/>";
xmlMemoryMap = xml;
xmlMemoryMap = xml.transform("'", "\"");
return;
}
if(type == TypeSufamiTurbo) {
xml << "<cartridge/>";
xmlMemoryMap = xml;
xmlMemoryMap = xml.transform("'", "\"");
return;
}
@@ -128,7 +128,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " <ram size='" << hex(gameboy_ram_size(data, size)) << "'/>\n";
}
xml << "</cartridge>\n";
xmlMemoryMap = xml;
xmlMemoryMap = xml.transform("'", "\"");
return;
}
@@ -145,23 +145,19 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <supergameboy revision='1'>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </mmio>\n";
xml << " </supergameboy>\n";
xml << " <icd2 revision='1'>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </icd2>\n";
} else if(type == TypeSuperGameBoy2Bios) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <supergameboy revision='2'>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </mmio>\n";
xml << " </supergameboy>\n";
xml << " <icd2 revision='2'>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </icd2>\n";
} else if(has_spc7110) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-0f:8000-ffff'/>\n";
@@ -283,19 +279,23 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " </superfx>\n";
} else if(mapper == SA1ROM) {
xml << " <sa1>\n";
xml << " <rom>\n";
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-ff:0000-ffff'/>\n";
xml << " </rom>\n";
xml << " <mcu>\n";
xml << " <rom>\n";
xml << " <map mode='direct' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='direct' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='direct' address='c0-ff:0000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram>\n";
xml << " <map mode='direct' address='00-3f:6000-7fff'/>\n";
xml << " <map mode='direct' address='80-bf:6000-7fff'/>\n";
xml << " </ram>\n";
xml << " </mcu>\n";
xml << " <iram size='800'>\n";
xml << " <map mode='linear' address='00-3f:3000-37ff'/>\n";
xml << " <map mode='linear' address='80-bf:3000-37ff'/>\n";
xml << " </iram>\n";
xml << " <bwram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='00-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='40-4f:0000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:6000-7fff'/>\n";
xml << " </bwram>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:2200-23ff'/>\n";
@@ -338,11 +338,14 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " </slot>\n";
xml << " </bsx>\n";
} else if(mapper == BSXROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <bsx>\n";
xml << " <mcu>\n";
xml << " <map address='00-3f:8000-ffff'/>\n";
xml << " <map address='80-bf:8000-ffff'/>\n";
xml << " <map address='40-7f:0000-ffff'/>\n";
xml << " <map address='c0-ff:0000-ffff'/>\n";
xml << " <map address='20-3f:6000-7fff'/>\n";
xml << " </mcu>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:5000-5fff'/>\n";
xml << " <map address='80-bf:5000-5fff'/>\n";
@@ -379,10 +382,8 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
if(has_srtc) {
xml << " <srtc>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:2800-2801'/>\n";
xml << " <map address='80-bf:2800-2801'/>\n";
xml << " </mmio>\n";
xml << " <map address='00-3f:2800-2801'/>\n";
xml << " <map address='80-bf:2800-2801'/>\n";
xml << " </srtc>\n";
}
@@ -400,15 +401,13 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
if(has_cx4) {
xml << " <cx4>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </mmio>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </cx4>\n";
}
if(has_dsp1) {
xml << " <upd77c25 program='dsp1b.bin' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n";
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp1b.bin' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n";
if(dsp1_mapper == DSP1LoROM1MB) {
xml << " <dr>\n";
xml << " <map address='20-3f:8000-bfff'/>\n";
@@ -437,11 +436,11 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " <map address='80-9f:7000-7fff'/>\n";
xml << " </sr>\n";
}
xml << " </upd77c25>\n";
xml << " </necdsp>\n";
}
if(has_dsp2) {
xml << " <upd77c25 program='dsp2.bin' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n";
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp2.bin' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n";
xml << " <dr>\n";
xml << " <map address='20-3f:8000-bfff'/>\n";
xml << " <map address='a0-bf:8000-bfff'/>\n";
@@ -450,11 +449,11 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " <map address='20-3f:c000-ffff'/>\n";
xml << " <map address='a0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
xml << " </upd77c25>\n";
xml << " </necdsp>\n";
}
if(has_dsp3) {
xml << " <upd77c25 program='dsp3.bin' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n";
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp3.bin' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n";
xml << " <dr>\n";
xml << " <map address='20-3f:8000-bfff'/>\n";
xml << " <map address='a0-bf:8000-bfff'/>\n";
@@ -463,11 +462,11 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " <map address='20-3f:c000-ffff'/>\n";
xml << " <map address='a0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
xml << " </upd77c25>\n";
xml << " </necdsp>\n";
}
if(has_dsp4) {
xml << " <upd77c25 program='dsp4.bin' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n";
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp4.bin' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n";
xml << " <dr>\n";
xml << " <map address='30-3f:8000-bfff'/>\n";
xml << " <map address='b0-bf:8000-bfff'/>\n";
@@ -476,48 +475,59 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " <map address='30-3f:c000-ffff'/>\n";
xml << " <map address='b0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
xml << " </upd77c25>\n";
xml << " </necdsp>\n";
}
if(has_obc1) {
xml << " <obc1>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </mmio>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </obc1>\n";
}
if(has_st010) {
xml << " <setadsp program='ST-0010'>\n";
xml << " <mmio>\n";
xml << " <necdsp revision='upd96050' frequency='10000000' program='st0010.bin' sha256='55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e'>\n";
xml << " <dr>\n";
xml << " <map address='60:0000'/>\n";
xml << " <map address='e0:0000'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='60:0001'/>\n";
xml << " <map address='e0:0001'/>\n";
xml << " </sr>\n";
xml << " <dp>\n";
xml << " <map address='68-6f:0000-0fff'/>\n";
xml << " <map address='e8-ef:0000-0fff'/>\n";
xml << " </mmio>\n";
xml << " </setadsp>\n";
xml << " </dp>\n";
xml << " </necdsp>\n";
}
if(has_st011) {
//ST-0011 addresses not verified; chip is unsupported
xml << " <setadsp program='ST-0011'>\n";
xml << " <mmio>\n";
xml << " <necdsp revision='upd96050' frequency='15000000' program='st0011.bin' sha256='651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e'>\n";
xml << " <dr>\n";
xml << " <map address='60:0000'/>\n";
xml << " <map address='e0:0000'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='60:0001'/>\n";
xml << " <map address='e0:0001'/>\n";
xml << " </sr>\n";
xml << " <dp>\n";
xml << " <map address='68-6f:0000-0fff'/>\n";
xml << " <map address='e8-ef:0000-0fff'/>\n";
xml << " </mmio>\n";
xml << " </setadsp>\n";
xml << " </dp>\n";
xml << " </necdsp>\n";
}
if(has_st018) {
xml << " <setarisc program='ST-0018'>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:3800-38ff'/>\n";
xml << " <map address='80-bf:3800-38ff'/>\n";
xml << " </mmio>\n";
xml << " <map address='00-3f:3800-38ff'/>\n";
xml << " <map address='80-bf:3800-38ff'/>\n";
xml << " </setarisc>\n";
}
xml << "</cartridge>\n";
xmlMemoryMap = xml;
xmlMemoryMap = xml.transform("'", "\"");
}
void SNESCartridge::read_header(const uint8_t *data, unsigned size) {

View File

@@ -25,7 +25,7 @@ namespace nall {
inline string& append(unsigned int value);
inline string& append(double value);
inline bool readfile(const char*);
inline bool readfile(const string&);
inline string& replace (const char*, const char*);
inline string& qreplace(const char*, const char*);
@@ -145,10 +145,15 @@ namespace nall {
inline unsigned strlcpy(string &dest, const char *src, unsigned length);
inline unsigned strlcat(string &dest, const char *src, unsigned length);
inline string substr(const char *src, unsigned start = 0, unsigned length = 0);
template<unsigned length = 0, char padding = '0'> inline string hex(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string integer(intmax_t value);
template<unsigned length = 0, char padding = '0'> inline string decimal(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string binary(uintmax_t value);
inline string integer(intmax_t value);
template<unsigned length = 0> inline string linteger(intmax_t value);
template<unsigned length = 0> inline string rinteger(intmax_t value);
inline string decimal(uintmax_t value);
template<unsigned length = 0> inline string ldecimal(uintmax_t value);
template<unsigned length = 0> inline string rdecimal(uintmax_t value);
template<unsigned length = 0> inline string hex(uintmax_t value);
template<unsigned length = 0> inline string binary(uintmax_t value);
inline unsigned fp(char *str, double value);
inline string fp(double value);

View File

@@ -65,8 +65,11 @@ intmax_t integer(const char *str) {
intmax_t result = 0;
bool negate = false;
//check for negation
if(*str == '-') {
//check for sign
if(*str == '+') {
negate = false;
str++;
} else if(*str == '-') {
negate = true;
str++;
}

View File

@@ -95,7 +95,7 @@ string::~string() {
if(data) free(data);
}
bool string::readfile(const char *filename) {
bool string::readfile(const string &filename) {
assign("");
#if !defined(_WIN32)

View File

@@ -27,98 +27,196 @@ string substr(const char *src, unsigned start, unsigned length) {
/* arithmetic <> string */
template<unsigned length, char padding> string hex(uintmax_t value) {
string output;
unsigned offset = 0;
//render string backwards, as we do not know its length yet
do {
unsigned n = value & 15;
output[offset++] = n < 10 ? '0' + n : 'a' + n - 10;
value >>= 4;
} while(value);
while(offset < length) output[offset++] = padding;
output[offset--] = 0;
//reverse the string in-place
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
}
return output;
}
template<unsigned length, char padding> string integer(intmax_t value) {
string output;
unsigned offset = 0;
string integer(intmax_t value) {
bool negative = value < 0;
if(negative) value = abs(value);
do {
unsigned n = value % 10;
output[offset++] = '0' + n;
value /= 10;
} while(value);
while(offset < length) output[offset++] = padding;
if(negative) output[offset++] = '-';
output[offset--] = 0;
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
}
return output;
}
template<unsigned length, char padding> string decimal(uintmax_t value) {
string output;
unsigned offset = 0;
char buffer[64];
unsigned size = 0;
do {
unsigned n = value % 10;
output[offset++] = '0' + n;
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
while(offset < length) output[offset++] = padding;
output[offset--] = 0;
char result[size + 1];
memset(result, '0', size);
result[size] = 0;
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return output;
return (const char*)result;
}
template<unsigned length, char padding> string binary(uintmax_t value) {
string output;
unsigned offset = 0;
template<unsigned length_> string linteger(intmax_t value) {
bool negative = value < 0;
if(negative) value = abs(value);
char buffer[64];
unsigned size = 0;
do {
unsigned n = value % 10;
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
result[length] = 0;
for(signed x = 0, y = size - 1; x < length && y >= 0; x++, y--) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_> string rinteger(intmax_t value) {
bool negative = value < 0;
if(negative) value = abs(value);
char buffer[64];
unsigned size = 0;
do {
unsigned n = value % 10;
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return (const char*)result;
}
string decimal(uintmax_t value) {
char buffer[64];
unsigned size = 0;
do {
unsigned n = value % 10;
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size] = 0;
char result[size + 1];
memset(result, '0', size);
result[size] = 0;
for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_> string ldecimal(uintmax_t value) {
char buffer[64];
unsigned size = 0;
do {
unsigned n = value % 10;
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
result[length] = 0;
for(signed x = 0, y = size - 1; x < length && y >= 0; x++, y--) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_> string rdecimal(uintmax_t value) {
char buffer[64];
unsigned size = 0;
do {
unsigned n = value % 10;
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_> string hex(uintmax_t value) {
char buffer[64];
unsigned size = 0;
do {
unsigned n = value & 15;
buffer[size++] = n < 10 ? '0' + n : 'a' + n - 10;
value >>= 4;
} while(value);
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, '0', length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_> string binary(uintmax_t value) {
char buffer[256];
unsigned size = 0;
do {
unsigned n = value & 1;
output[offset++] = '0' + n;
buffer[size++] = '0' + n;
value >>= 1;
} while(value);
while(offset < length) output[offset++] = padding;
output[offset--] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, '0', length);
result[length] = 0;
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return output;
return (const char*)result;
}
//using sprintf is certainly not the most ideal method to convert

View File

@@ -8,84 +8,110 @@
namespace nall {
template<unsigned bits> class uint_t {
private:
enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value
typedef typename static_if<
sizeof(int) >= bytes,
unsigned int,
typename static_if<
sizeof(long) >= bytes,
unsigned long,
typename static_if<
sizeof(long long) >= bytes,
unsigned long long,
void
>::type
>::type
>::type T;
static_assert(!std::is_same<T, void>::value, "");
T data;
unsigned data;
public:
inline operator T() const { return data; }
inline T operator ++(int) { T r = data; data = uclip<bits>(data + 1); return r; }
inline T operator --(int) { T r = data; data = uclip<bits>(data - 1); return r; }
inline T operator ++() { return data = uclip<bits>(data + 1); }
inline T operator --() { return data = uclip<bits>(data - 1); }
inline T operator =(const T i) { return data = uclip<bits>(i); }
inline T operator |=(const T i) { return data = uclip<bits>(data | i); }
inline T operator ^=(const T i) { return data = uclip<bits>(data ^ i); }
inline T operator &=(const T i) { return data = uclip<bits>(data & i); }
inline T operator<<=(const T i) { return data = uclip<bits>(data << i); }
inline T operator>>=(const T i) { return data = uclip<bits>(data >> i); }
inline T operator +=(const T i) { return data = uclip<bits>(data + i); }
inline T operator -=(const T i) { return data = uclip<bits>(data - i); }
inline T operator *=(const T i) { return data = uclip<bits>(data * i); }
inline T operator /=(const T i) { return data = uclip<bits>(data / i); }
inline T operator %=(const T i) { return data = uclip<bits>(data % i); }
inline operator unsigned() const { return data; }
inline unsigned operator ++(int) { unsigned r = data; data = uclip<bits>(data + 1); return r; }
inline unsigned operator --(int) { unsigned r = data; data = uclip<bits>(data - 1); return r; }
inline unsigned operator ++() { return data = uclip<bits>(data + 1); }
inline unsigned operator --() { return data = uclip<bits>(data - 1); }
inline unsigned operator =(const unsigned i) { return data = uclip<bits>(i); }
inline unsigned operator |=(const unsigned i) { return data = uclip<bits>(data | i); }
inline unsigned operator ^=(const unsigned i) { return data = uclip<bits>(data ^ i); }
inline unsigned operator &=(const unsigned i) { return data = uclip<bits>(data & i); }
inline unsigned operator<<=(const unsigned i) { return data = uclip<bits>(data << i); }
inline unsigned operator>>=(const unsigned i) { return data = uclip<bits>(data >> i); }
inline unsigned operator +=(const unsigned i) { return data = uclip<bits>(data + i); }
inline unsigned operator -=(const unsigned i) { return data = uclip<bits>(data - i); }
inline unsigned operator *=(const unsigned i) { return data = uclip<bits>(data * i); }
inline unsigned operator /=(const unsigned i) { return data = uclip<bits>(data / i); }
inline unsigned operator %=(const unsigned i) { return data = uclip<bits>(data % i); }
inline uint_t() : data(0) {}
inline uint_t(const T i) : data(uclip<bits>(i)) {}
inline uint_t(const unsigned i) : data(uclip<bits>(i)) {}
};
template<unsigned bits> class int_t {
private:
enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value
typedef typename static_if<
sizeof(int) >= bytes,
signed int,
typename static_if<
sizeof(long) >= bytes,
signed long,
typename static_if<
sizeof(long long) >= bytes,
signed long long,
void
>::type
>::type
>::type T;
static_assert(!std::is_same<T, void>::value, "");
T data;
signed data;
public:
inline operator T() const { return data; }
inline T operator ++(int) { T r = data; data = sclip<bits>(data + 1); return r; }
inline T operator --(int) { T r = data; data = sclip<bits>(data - 1); return r; }
inline T operator ++() { return data = sclip<bits>(data + 1); }
inline T operator --() { return data = sclip<bits>(data - 1); }
inline T operator =(const T i) { return data = sclip<bits>(i); }
inline T operator |=(const T i) { return data = sclip<bits>(data | i); }
inline T operator ^=(const T i) { return data = sclip<bits>(data ^ i); }
inline T operator &=(const T i) { return data = sclip<bits>(data & i); }
inline T operator<<=(const T i) { return data = sclip<bits>(data << i); }
inline T operator>>=(const T i) { return data = sclip<bits>(data >> i); }
inline T operator +=(const T i) { return data = sclip<bits>(data + i); }
inline T operator -=(const T i) { return data = sclip<bits>(data - i); }
inline T operator *=(const T i) { return data = sclip<bits>(data * i); }
inline T operator /=(const T i) { return data = sclip<bits>(data / i); }
inline T operator %=(const T i) { return data = sclip<bits>(data % i); }
inline operator signed() const { return data; }
inline signed operator ++(int) { signed r = data; data = sclip<bits>(data + 1); return r; }
inline signed operator --(int) { signed r = data; data = sclip<bits>(data - 1); return r; }
inline signed operator ++() { return data = sclip<bits>(data + 1); }
inline signed operator --() { return data = sclip<bits>(data - 1); }
inline signed operator =(const signed i) { return data = sclip<bits>(i); }
inline signed operator |=(const signed i) { return data = sclip<bits>(data | i); }
inline signed operator ^=(const signed i) { return data = sclip<bits>(data ^ i); }
inline signed operator &=(const signed i) { return data = sclip<bits>(data & i); }
inline signed operator<<=(const signed i) { return data = sclip<bits>(data << i); }
inline signed operator>>=(const signed i) { return data = sclip<bits>(data >> i); }
inline signed operator +=(const signed i) { return data = sclip<bits>(data + i); }
inline signed operator -=(const signed i) { return data = sclip<bits>(data - i); }
inline signed operator *=(const signed i) { return data = sclip<bits>(data * i); }
inline signed operator /=(const signed i) { return data = sclip<bits>(data / i); }
inline signed operator %=(const signed i) { return data = sclip<bits>(data % i); }
inline int_t() : data(0) {}
inline int_t(const T i) : data(sclip<bits>(i)) {}
inline int_t(const signed i) : data(sclip<bits>(i)) {}
};
class varuint_t {
private:
unsigned data;
unsigned mask;
public:
inline operator unsigned() const { return data; }
inline unsigned operator ++(int) { unsigned r = data; data = (data + 1) & mask; return r; }
inline unsigned operator --(int) { unsigned r = data; data = (data - 1) & mask; return r; }
inline unsigned operator ++() { return data = (data + 1) & mask; }
inline unsigned operator --() { return data = (data - 1) & mask; }
inline unsigned operator =(const unsigned i) { return data = (i) & mask; }
inline unsigned operator |=(const unsigned i) { return data = (data | i) & mask; }
inline unsigned operator ^=(const unsigned i) { return data = (data ^ i) & mask; }
inline unsigned operator &=(const unsigned i) { return data = (data & i) & mask; }
inline unsigned operator<<=(const unsigned i) { return data = (data << i) & mask; }
inline unsigned operator>>=(const unsigned i) { return data = (data >> i) & mask; }
inline unsigned operator +=(const unsigned i) { return data = (data + i) & mask; }
inline unsigned operator -=(const unsigned i) { return data = (data - i) & mask; }
inline unsigned operator *=(const unsigned i) { return data = (data * i) & mask; }
inline unsigned operator /=(const unsigned i) { return data = (data / i) & mask; }
inline unsigned operator %=(const unsigned i) { return data = (data % i) & mask; }
inline void bits(unsigned bits) { mask = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1); data &= mask; }
inline varuint_t() : data(0), mask(~0U) {}
inline varuint_t(const unsigned i) : data(i), mask(~0U) {}
};
class varuintmax_t {
private:
uintmax_t data;
uintmax_t mask;
public:
inline operator uintmax_t() const { return data; }
inline uintmax_t operator ++(int) { uintmax_t r = data; data = (data + 1) & mask; return r; }
inline uintmax_t operator --(int) { uintmax_t r = data; data = (data - 1) & mask; return r; }
inline uintmax_t operator ++() { return data = (data + 1) & mask; }
inline uintmax_t operator --() { return data = (data - 1) & mask; }
inline uintmax_t operator =(const uintmax_t i) { return data = (i) & mask; }
inline uintmax_t operator |=(const uintmax_t i) { return data = (data | i) & mask; }
inline uintmax_t operator ^=(const uintmax_t i) { return data = (data ^ i) & mask; }
inline uintmax_t operator &=(const uintmax_t i) { return data = (data & i) & mask; }
inline uintmax_t operator<<=(const uintmax_t i) { return data = (data << i) & mask; }
inline uintmax_t operator>>=(const uintmax_t i) { return data = (data >> i) & mask; }
inline uintmax_t operator +=(const uintmax_t i) { return data = (data + i) & mask; }
inline uintmax_t operator -=(const uintmax_t i) { return data = (data - i) & mask; }
inline uintmax_t operator *=(const uintmax_t i) { return data = (data * i) & mask; }
inline uintmax_t operator /=(const uintmax_t i) { return data = (data / i) & mask; }
inline uintmax_t operator %=(const uintmax_t i) { return data = (data % i) & mask; }
inline void bits(unsigned bits) { mask = (1ULL << (bits - 1)) + ((1ULL << (bits - 1)) - 1); data &= mask; }
inline varuintmax_t() : data(0), mask(~0ULL) {}
inline varuintmax_t(const uintmax_t i) : data(i), mask(~0ULL) {}
};
}

View File

@@ -46,7 +46,7 @@ namespace nall {
void reserve(unsigned newsize) {
newsize = bit::round(newsize); //round to nearest power of two (for amortized growth)
T *poolcopy = (T*)malloc(newsize * sizeof(T));
T *poolcopy = (T*)calloc(newsize, sizeof(T));
for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]);
for(unsigned i = 0; i < objectsize; i++) pool[i].~T();
free(pool);

170
bsnes/phoenix/core/core.cpp Executable file
View File

@@ -0,0 +1,170 @@
#include "state.hpp"
#include "layout/fixed-layout.cpp"
#include "layout/horizontal-layout.cpp"
#include "layout/vertical-layout.cpp"
#if defined(PHOENIX_WINDOWS)
#include "../windows/windows.cpp"
#elif defined(PHOENIX_QT)
#include "../qt/qt.cpp"
#elif defined(PHOENIX_GTK)
#include "../gtk/gtk.cpp"
#elif defined(PHOENIX_REFERENCE)
#include "../reference/reference.cpp"
#endif
Object::Object() { OS::initialize(); }
Geometry OS::availableGeometry() { return pOS::availableGeometry(); }
Geometry OS::desktopGeometry() { return pOS::desktopGeometry(); }
string OS::fileLoad_(Window &parent, const string &path, const lstring &filter_) { auto filter = filter_; if(filter.size() == 0) filter.append("All files (*)"); return pOS::fileLoad(parent, path, filter); }
string OS::fileSave_(Window &parent, const string &path, const lstring &filter_) { auto filter = filter_; if(filter.size() == 0) filter.append("All files (*)"); return pOS::fileSave(parent, path, filter); }
string OS::folderSelect(Window &parent, const string &path) { return pOS::folderSelect(parent, path); }
void OS::main() { return pOS::main(); }
bool OS::pendingEvents() { return pOS::pendingEvents(); }
void OS::processEvents() { return pOS::processEvents(); }
void OS::quit() { return pOS::quit(); }
void OS::initialize() { static bool initialized = false; if(initialized == false) { initialized = true; return pOS::initialize(); } }
void Font::setBold(bool bold) { state.bold = bold; return p.setBold(bold); }
void Font::setFamily(const string &family) { state.family = family; return p.setFamily(family); }
void Font::setItalic(bool italic) { state.italic = italic; return p.setItalic(italic); }
void Font::setSize(unsigned size) { state.size = size; return p.setSize(size); }
void Font::setUnderline(bool underline) { state.underline = underline; return p.setUnderline(underline); }
Font::Font() : state(*new State), p(*new pFont(*this)) { p.constructor(); }
MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::information(parent, text, buttons); }
MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::question(parent, text, buttons); }
MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::warning(parent, text, buttons); }
MessageWindow::Response MessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::critical(parent, text, buttons); }
Window Window::None;
void Window::append(Layout &layout) { state.layout.append(layout); return p.append(layout); }
void Window::append(Menu &menu) { state.menu.append(menu); ((Action&)menu).state.parent = this; return p.append(menu); }
void Window::append(Widget &widget) { state.widget.append(widget); return p.append(widget); }
Geometry Window::frameGeometry() { Geometry geometry = p.geometry(), margin = p.frameMargin(); return { geometry.x - margin.x, geometry.y - margin.y, geometry.width + margin.width, geometry.height + margin.height }; }
Geometry Window::frameMargin() { return p.frameMargin(); }
bool Window::focused() { return p.focused(); }
Geometry Window::geometry() { return p.geometry(); }
void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { state.backgroundColor = true; state.backgroundColorRed = red; state.backgroundColorGreen = green; state.backgroundColorBlue = blue; return p.setBackgroundColor(red, green, blue); }
void Window::setFrameGeometry(const Geometry &geometry) { Geometry margin = p.frameMargin(); return setGeometry({ geometry.x + margin.x, geometry.y + margin.y, geometry.width - margin.width, geometry.height - margin.height }); }
void Window::setFocused() { return p.setFocused(); }
void Window::setFullScreen(bool fullScreen) { state.fullScreen = fullScreen; return p.setFullScreen(fullScreen); }
void Window::setGeometry(const Geometry &geometry) { state.geometry = geometry; return p.setGeometry(geometry); }
void Window::setMenuFont(Font &font) { state.menuFont = &font; return p.setMenuFont(font); }
void Window::setMenuVisible(bool visible) { state.menuVisible = visible; return p.setMenuVisible(visible); }
void Window::setResizable(bool resizable) { state.resizable = resizable; return p.setResizable(resizable); }
void Window::setStatusFont(Font &font) { state.statusFont = &font; return p.setStatusFont(font); }
void Window::setStatusText(const string &text) { state.statusText = text; return p.setStatusText(text); }
void Window::setStatusVisible(bool visible) { state.statusVisible = visible; return p.setStatusVisible(visible); }
void Window::setTitle(const string &text) { state.title = text; return p.setTitle(text); }
void Window::setVisible(bool visible) { state.visible = visible; return p.setVisible(visible); }
void Window::setWidgetFont(Font &font) { state.widgetFont = &font; return p.setWidgetFont(font); }
Window::Window() : state(*new State), p(*new pWindow(*this)) { p.constructor(); }
void Action::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); }
void Action::setVisible(bool visible) { state.visible = visible; return p.setVisible(visible); }
Action::Action(pAction &p) : state(*new State), p(p) { p.constructor(); }
void Menu::append(Action &action) { state.action.append(action); return p.append(action); }
void Menu::setText(const string &text) { state.text = text; return p.setText(text); }
Menu::Menu() : state(*new State), base_from_member<pMenu&>(*new pMenu(*this)), Action(base_from_member<pMenu&>::value), p(base_from_member<pMenu&>::value) { p.constructor(); }
Separator::Separator() : base_from_member<pSeparator&>(*new pSeparator(*this)), Action(base_from_member<pSeparator&>::value), p(base_from_member<pSeparator&>::value) { p.constructor(); }
void Item::setText(const string &text) { state.text = text; return p.setText(text); }
Item::Item() : state(*new State), base_from_member<pItem&>(*new pItem(*this)), Action(base_from_member<pItem&>::value), p(base_from_member<pItem&>::value) { p.constructor(); }
bool CheckItem::checked() { return p.checked(); }
void CheckItem::setChecked(bool checked) { state.checked = checked; return p.setChecked(checked); }
void CheckItem::setText(const string &text) { state.text = text; return p.setText(text); }
CheckItem::CheckItem() : state(*new State), base_from_member<pCheckItem&>(*new pCheckItem(*this)), Action(base_from_member<pCheckItem&>::value), p(base_from_member<pCheckItem&>::value) { p.constructor(); }
void RadioItem::group_(const reference_array<RadioItem&> &list) { foreach(item, list) item.p.setGroup(item.state.group = list); if(list.size()) list[0].setChecked(); }
bool RadioItem::checked() { return p.checked(); }
void RadioItem::setChecked() { foreach(item, state.group) item.state.checked = false; state.checked = true; return p.setChecked(); }
void RadioItem::setText(const string &text) { state.text = text; return p.setText(text); }
RadioItem::RadioItem() : state(*new State), base_from_member<pRadioItem&>(*new pRadioItem(*this)), Action(base_from_member<pRadioItem&>::value), p(base_from_member<pRadioItem&>::value) { p.constructor(); }
bool Widget::enabled() { return state.enabled; }
void Widget::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); }
void Widget::setFocused() { return p.setFocused(); }
void Widget::setFont(Font &font) { state.font = &font; return p.setFont(font); }
void Widget::setGeometry(const Geometry &geometry) { state.geometry = geometry; return p.setGeometry(geometry); }
void Widget::setVisible(bool visible) { state.visible = visible; return p.setVisible(visible); }
bool Widget::visible() { return state.visible; }
Widget::Widget() : state(*new State), p(*new pWidget(*this)) { state.abstract = true; p.constructor(); }
Widget::Widget(pWidget &p) : state(*new State), p(p) { p.constructor(); }
void Button::setText(const string &text) { state.text = text; return p.setText(text); }
Button::Button() : state(*new State), base_from_member<pButton&>(*new pButton(*this)), Widget(base_from_member<pButton&>::value), p(base_from_member<pButton&>::value) { p.constructor(); }
bool CheckBox::checked() { return p.checked(); }
void CheckBox::setChecked(bool checked) { state.checked = checked; return p.setChecked(checked); }
void CheckBox::setText(const string &text) { state.text = text; return p.setText(text); }
CheckBox::CheckBox() : state(*new State), base_from_member<pCheckBox&>(*new pCheckBox(*this)), Widget(base_from_member<pCheckBox&>::value), p(base_from_member<pCheckBox&>::value) { p.constructor(); }
void ComboBox::append(const string &text) { state.text.append(text); return p.append(text); }
void ComboBox::reset() { state.selection = 0; state.text.reset(); return p.reset(); }
unsigned ComboBox::selection() { return p.selection(); }
void ComboBox::setSelection(unsigned row) { state.selection = row; return p.setSelection(row); }
ComboBox::ComboBox() : state(*new State), base_from_member<pComboBox&>(*new pComboBox(*this)), Widget(base_from_member<pComboBox&>::value), p(base_from_member<pComboBox&>::value) { p.constructor(); }
void HexEdit::setColumns(unsigned columns) { state.columns = columns; return p.setColumns(columns); }
void HexEdit::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void HexEdit::setOffset(unsigned offset) { state.offset = offset; return p.setOffset(offset); }
void HexEdit::setRows(unsigned rows) { state.rows = rows; return p.setRows(rows); }
void HexEdit::update() { return p.update(); }
HexEdit::HexEdit() : state(*new State), base_from_member<pHexEdit&>(*new pHexEdit(*this)), Widget(base_from_member<pHexEdit&>::value), p(base_from_member<pHexEdit&>::value) { p.constructor(); }
unsigned HorizontalSlider::position() { return p.position(); }
void HorizontalSlider::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void HorizontalSlider::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
HorizontalSlider::HorizontalSlider() : state(*new State), base_from_member<pHorizontalSlider&>(*new pHorizontalSlider(*this)), Widget(base_from_member<pHorizontalSlider&>::value), p(base_from_member<pHorizontalSlider&>::value) { p.constructor(); }
void Label::setText(const string &text) { state.text = text; return p.setText(text); }
Label::Label() : state(*new State), base_from_member<pLabel&>(*new pLabel(*this)), Widget(base_from_member<pLabel&>::value), p(base_from_member<pLabel&>::value) { p.constructor(); }
void LineEdit::setEditable(bool editable) { state.editable = editable; return p.setEditable(editable); }
void LineEdit::setText(const string &text) { state.text = text; return p.setText(text); }
string LineEdit::text() { return p.text(); }
LineEdit::LineEdit() : state(*new State), base_from_member<pLineEdit&>(*new pLineEdit(*this)), Widget(base_from_member<pLineEdit&>::value), p(base_from_member<pLineEdit&>::value) { p.constructor(); }
void ListView::append_(const lstring &text) { state.checked.append(false); state.text.append(text); return p.append(text); }
void ListView::autoSizeColumns() { return p.autoSizeColumns(); }
bool ListView::checked(unsigned row) { return p.checked(row); }
void ListView::modify_(unsigned row, const lstring &text) { state.text[row] = text; return p.modify(row, text); }
void ListView::reset() { state.checked.reset(); state.text.reset(); return p.reset(); }
bool ListView::selected() { return p.selected(); }
unsigned ListView::selection() { return p.selection(); }
void ListView::setCheckable(bool checkable) { state.checkable = checkable; return p.setCheckable(checkable); }
void ListView::setChecked(unsigned row, bool checked) { state.checked[row] = checked; return p.setChecked(row, checked); }
void ListView::setHeaderText_(const lstring &text) { state.headerText = text; return p.setHeaderText(text); }
void ListView::setHeaderVisible(bool visible) { state.headerVisible = visible; return p.setHeaderVisible(visible); }
void ListView::setSelected(bool selected) { state.selected = selected; return p.setSelected(selected); }
void ListView::setSelection(unsigned row) { state.selected = true; state.selection = row; return p.setSelection(row); }
ListView::ListView() : state(*new State), base_from_member<pListView&>(*new pListView(*this)), Widget(base_from_member<pListView&>::value), p(base_from_member<pListView&>::value) { p.constructor(); }
void ProgressBar::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
ProgressBar::ProgressBar() : state(*new State), base_from_member<pProgressBar&>(*new pProgressBar(*this)), Widget(base_from_member<pProgressBar&>::value), p(base_from_member<pProgressBar&>::value) { p.constructor(); }
void RadioBox::group_(const reference_array<RadioBox&> &list) { foreach(item, list) item.p.setGroup(item.state.group = list); if(list.size()) list[0].setChecked(); }
bool RadioBox::checked() { return p.checked(); }
void RadioBox::setChecked() { foreach(item, state.group) item.state.checked = false; state.checked = true; return p.setChecked(); }
void RadioBox::setText(const string &text) { state.text = text; return p.setText(text); }
RadioBox::RadioBox() : state(*new State), base_from_member<pRadioBox&>(*new pRadioBox(*this)), Widget(base_from_member<pRadioBox&>::value), p(base_from_member<pRadioBox&>::value) { p.constructor(); }
void TextEdit::setCursorPosition(unsigned position) { state.cursorPosition = position; return p.setCursorPosition(position); }
void TextEdit::setEditable(bool editable) { state.editable = editable; return p.setEditable(editable); }
void TextEdit::setText(const string &text) { state.text = text; return p.setText(text); }
void TextEdit::setWordWrap(bool wordWrap) { state.wordWrap = wordWrap; return p.setWordWrap(wordWrap); }
string TextEdit::text() { return p.text(); }
TextEdit::TextEdit() : state(*new State), base_from_member<pTextEdit&>(*new pTextEdit(*this)), Widget(base_from_member<pTextEdit&>::value), p(base_from_member<pTextEdit&>::value) { p.constructor(); }
unsigned VerticalSlider::position() { return p.position(); }
void VerticalSlider::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void VerticalSlider::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
VerticalSlider::VerticalSlider() : state(*new State), base_from_member<pVerticalSlider&>(*new pVerticalSlider(*this)), Widget(base_from_member<pVerticalSlider&>::value), p(base_from_member<pVerticalSlider&>::value) { p.constructor(); }
uintptr_t Viewport::handle() { return p.handle(); }
Viewport::Viewport() : base_from_member<pViewport&>(*new pViewport(*this)), Widget(base_from_member<pViewport&>::value), p(base_from_member<pViewport&>::value) { p.constructor(); }

405
bsnes/phoenix/core/core.hpp Executable file
View File

@@ -0,0 +1,405 @@
struct Font;
struct Window;
struct Menu;
struct Layout;
struct Widget;
struct pOS;
struct pFont;
struct pWindow;
struct pAction;
struct pMenu;
struct pSeparator;
struct pItem;
struct pCheckItem;
struct pRadioItem;
struct pLayout;
struct pWidget;
struct pButton;
struct pCheckBox;
struct pComboBox;
struct pHexEdit;
struct pHorizontalSlider;
struct pLabel;
struct pLineEdit;
struct pListView;
struct pProgressBar;
struct pRadioBox;
struct pTextEdit;
struct pVerticalSlider;
struct pViewport;
struct Geometry {
signed x, y;
unsigned width, height;
inline Geometry() : x(0), y(0), width(0), height(0) {}
inline Geometry(signed x, signed y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {}
};
struct Object {
Object();
Object& operator=(const Object&) = delete;
Object(const Object&) = delete;
virtual void unused() {} //allows dynamic_cast<> on Object
};
struct OS : Object {
static Geometry availableGeometry();
static Geometry desktopGeometry();
template<typename... Args> static nall::string fileLoad(Window &parent, const nall::string &path, const Args&... args) { return fileLoad_(parent, path, { args... }); }
template<typename... Args> static nall::string fileSave(Window &parent, const nall::string &path, const Args&... args) { return fileSave_(parent, path, { args... }); }
static nall::string folderSelect(Window &parent, const nall::string &path);
static void main();
static bool pendingEvents();
static void processEvents();
static void quit();
OS();
static void initialize();
private:
static nall::string fileLoad_(Window &parent, const nall::string &path, const nall::lstring& filter);
static nall::string fileSave_(Window &parent, const nall::string &path, const nall::lstring& filter);
};
struct Font : Object {
void setBold(bool bold = true);
void setFamily(const nall::string &family);
void setItalic(bool italic = true);
void setSize(unsigned size);
void setUnderline(bool underline = true);
Font();
struct State;
State &state;
pFont &p;
};
struct MessageWindow : Object {
enum class Buttons : unsigned {
Ok,
OkCancel,
YesNo,
};
enum class Response : unsigned {
Ok,
Cancel,
Yes,
No,
};
static Response information(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
static Response question(Window &parent, const nall::string &text, Buttons = Buttons::YesNo);
static Response warning(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
static Response critical(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
};
struct Window : Object {
static Window None;
nall::function<void ()> onClose;
nall::function<void ()> onMove;
nall::function<void ()> onSize;
void append(Layout &layout);
void append(Menu &menu);
void append(Widget &widget);
Geometry frameGeometry();
Geometry frameMargin();
bool focused();
Geometry geometry();
void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue);
void setFrameGeometry(const Geometry &geometry);
void setFocused();
void setFullScreen(bool fullScreen = true);
void setGeometry(const Geometry &geometry);
void setMenuFont(Font &font);
void setMenuVisible(bool visible = true);
void setResizable(bool resizable = true);
void setStatusFont(Font &font);
void setStatusText(const nall::string &text);
void setStatusVisible(bool visible = true);
void setTitle(const nall::string &text);
void setVisible(bool visible = true);
void setWidgetFont(Font &font);
Window();
struct State;
State &state;
pWindow &p;
};
struct Action : Object {
void setEnabled(bool enabled = true);
void setVisible(bool visible = true);
Action(pAction &p);
struct State;
State &state;
pAction &p;
};
struct Menu : private nall::base_from_member<pMenu&>, Action {
void append(Action &action);
void setText(const nall::string &text);
Menu();
struct State;
State &state;
pMenu &p;
};
struct Separator : private nall::base_from_member<pSeparator&>, Action {
Separator();
pSeparator &p;
};
struct Item : private nall::base_from_member<pItem&>, Action {
nall::function<void ()> onTick;
void setText(const nall::string &text);
Item();
struct State;
State &state;
pItem &p;
};
struct CheckItem : private nall::base_from_member<pCheckItem&>, Action {
nall::function<void ()> onTick;
bool checked();
void setChecked(bool checked = true);
void setText(const nall::string &text);
CheckItem();
struct State;
State &state;
pCheckItem &p;
};
struct RadioItem : private nall::base_from_member<pRadioItem&>, Action {
template<typename... Args> static void group(Args&... args) { group_({ args... }); }
nall::function<void ()> onTick;
bool checked();
void setChecked();
void setText(const nall::string &text);
RadioItem();
struct State;
State &state;
pRadioItem &p;
private:
static void group_(const nall::reference_array<RadioItem&> &list);
};
struct Layout : Object {
virtual void setGeometry(Geometry &geometry) = 0;
virtual void setParent(Window &parent) = 0;
virtual void setVisible(bool visible = true) = 0;
};
struct Widget : Object {
bool enabled();
void setEnabled(bool enabled = true);
void setFocused();
void setFont(Font &font);
void setGeometry(const Geometry &geometry);
void setVisible(bool visible = true);
bool visible();
Widget();
Widget(pWidget &p);
struct State;
State &state;
pWidget &p;
};
struct Button : private nall::base_from_member<pButton&>, Widget {
nall::function<void ()> onTick;
void setText(const nall::string &text);
Button();
struct State;
State &state;
pButton &p;
};
struct CheckBox : private nall::base_from_member<pCheckBox&>, Widget {
nall::function<void ()> onTick;
bool checked();
void setChecked(bool checked = true);
void setText(const nall::string &text);
CheckBox();
struct State;
State &state;
pCheckBox &p;
};
struct ComboBox : private nall::base_from_member<pComboBox&>, Widget {
nall::function<void ()> onChange;
void append(const nall::string &text);
void reset();
unsigned selection();
void setSelection(unsigned row);
ComboBox();
struct State;
State &state;
pComboBox &p;
};
struct HexEdit : private nall::base_from_member<pHexEdit&>, Widget {
nall::function<uint8_t (unsigned)> onRead;
nall::function<void (unsigned, uint8_t)> onWrite;
void setColumns(unsigned columns);
void setLength(unsigned length);
void setOffset(unsigned offset);
void setRows(unsigned rows);
void update();
HexEdit();
struct State;
State &state;
pHexEdit &p;
};
struct HorizontalSlider : private nall::base_from_member<pHorizontalSlider&>, Widget {
nall::function<void ()> onChange;
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
HorizontalSlider();
struct State;
State &state;
pHorizontalSlider &p;
};
struct Label : private nall::base_from_member<pLabel&>, Widget {
void setText(const nall::string &text);
Label();
struct State;
State &state;
pLabel &p;
};
struct LineEdit : private nall::base_from_member<pLineEdit&>, Widget {
nall::function<void ()> onActivate;
nall::function<void ()> onChange;
void setEditable(bool editable = true);
void setText(const nall::string &text);
nall::string text();
LineEdit();
struct State;
State &state;
pLineEdit &p;
};
struct ListView : private nall::base_from_member<pListView&>, Widget {
nall::function<void ()> onActivate;
nall::function<void ()> onChange;
nall::function<void (unsigned)> onTick;
template<typename... Args> void append(const Args&... args) { append_({ args... }); }
void autoSizeColumns();
bool checked(unsigned row);
template<typename... Args> void modify(unsigned row, const Args&... args) { modify_(row, { args... }); }
void reset();
bool selected();
unsigned selection();
void setCheckable(bool checkable = true);
void setChecked(unsigned row, bool checked = true);
template<typename... Args> void setHeaderText(const Args&... args) { setHeaderText_({ args... }); }
void setHeaderVisible(bool visible = true);
void setSelected(bool selected = true);
void setSelection(unsigned row);
ListView();
struct State;
State &state;
pListView &p;
private:
void append_(const nall::lstring &list);
void modify_(unsigned row, const nall::lstring &list);
void setHeaderText_(const nall::lstring &list);
};
struct ProgressBar : private nall::base_from_member<pProgressBar&>, Widget {
void setPosition(unsigned position);
ProgressBar();
struct State;
State &state;
pProgressBar &p;
};
struct RadioBox : private nall::base_from_member<pRadioBox&>, Widget {
template<typename... Args> static void group(Args&... args) { group_({ args... }); }
nall::function<void ()> onTick;
bool checked();
void setChecked();
void setText(const nall::string &text);
RadioBox();
struct State;
State &state;
pRadioBox &p;
private:
static void group_(const nall::reference_array<RadioBox&> &list);
};
struct TextEdit : private nall::base_from_member<pTextEdit&>, Widget {
nall::function<void ()> onChange;
void setCursorPosition(unsigned position);
void setEditable(bool editable = true);
void setText(const nall::string &text);
void setWordWrap(bool wordWrap = true);
nall::string text();
TextEdit();
struct State;
State &state;
pTextEdit &p;
};
struct VerticalSlider : private nall::base_from_member<pVerticalSlider&>, Widget {
nall::function<void ()> onChange;
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
VerticalSlider();
struct State;
State &state;
pVerticalSlider &p;
};
struct Viewport : private nall::base_from_member<pViewport&>, Widget {
uintptr_t handle();
Viewport();
pViewport &p;
};
#include "layout/fixed-layout.hpp"
#include "layout/horizontal-layout.hpp"
#include "layout/vertical-layout.hpp"

View File

@@ -0,0 +1,20 @@
void FixedLayout::setParent(Window &parent) {
foreach(child, children) {
parent.append(*child.widget);
child.widget->setGeometry(child.geometry);
}
}
void FixedLayout::append(Widget &widget, const Geometry &geometry) {
children.append({ &widget, geometry });
}
void FixedLayout::setGeometry(Geometry &geometry) {
}
void FixedLayout::setVisible(bool visible) {
foreach(child, children) child.widget->setVisible(visible);
}
FixedLayout::FixedLayout() {
}

View File

@@ -0,0 +1,15 @@
struct FixedLayout : Layout {
void append(Widget &widget, const Geometry &geometry);
void setGeometry(Geometry &geometry);
void setParent(Window &parent);
void setVisible(bool visible);
FixedLayout();
//private:
Window *parent;
struct Children {
Widget *widget;
Geometry geometry;
};
nall::linear_vector<Children> children;
};

View File

@@ -0,0 +1,88 @@
void HorizontalLayout::setParent(Window &parent) {
foreach(child, children) {
if(child.layout) child.layout->setParent(parent);
if(child.widget) parent.append(*child.widget);
}
}
void HorizontalLayout::append(VerticalLayout &layout, unsigned width, unsigned height, unsigned spacing) {
layout.width = width;
layout.height = height;
children.append({ &layout, 0, width, height, spacing });
}
void HorizontalLayout::append(Widget &widget, unsigned width, unsigned height, unsigned spacing) {
children.append({ 0, &widget, width, height, spacing });
}
void HorizontalLayout::setGeometry(Geometry &geometry) {
geometry.x += margin;
geometry.y += margin;
geometry.width -= margin * 2;
geometry.height -= margin * 2;
unsigned geometryWidth = width ? width : geometry.width;
unsigned geometryHeight = height ? height : geometry.height;
Geometry baseGeometry = geometry;
linear_vector<HorizontalLayout::Children> children = this->children;
unsigned minimumWidth = 0;
foreach(child, children) minimumWidth += child.width + child.spacing;
unsigned autosizeWidgets = 0;
foreach(child, children) {
if(child.width == 0) autosizeWidgets++;
}
foreach(child, children) {
if(child.width == 0) child.width = (geometryWidth - minimumWidth) / autosizeWidgets;
if(child.height == 0) child.height = geometryHeight;
}
unsigned maxHeight = 0;
foreach(child, children) {
maxHeight = max(maxHeight, child.height);
}
foreach(child, children) {
if(child.layout) {
child.layout->setGeometry(geometry);
geometry.x += child.spacing;
geometry.width -= child.spacing;
geometry.y = baseGeometry.y;
geometry.height = baseGeometry.height;
}
if(child.widget) {
child.widget->setGeometry({ geometry.x, geometry.y, child.width, child.height });
geometry.x += child.width + child.spacing;
geometry.width -= child.width + child.spacing;
}
}
geometry.y += maxHeight;
geometry.height -= maxHeight;
}
void HorizontalLayout::setMargin(unsigned margin_) {
margin = margin_;
}
unsigned HorizontalLayout::minimumWidth() {
unsigned width = margin * 2;
foreach(child, children) width += child.width + child.spacing;
return width;
}
void HorizontalLayout::setVisible(bool visible) {
foreach(child, children) {
if(child.layout) child.layout->setVisible(visible);
if(child.widget) child.widget->setVisible(visible);
}
}
HorizontalLayout::HorizontalLayout() {
margin = 0;
width = 0;
height = 0;
}

View File

@@ -0,0 +1,23 @@
struct VerticalLayout;
struct HorizontalLayout : public Layout {
void append(VerticalLayout &layout, unsigned width, unsigned height, unsigned spacing = 0);
void append(Widget &widget, unsigned width, unsigned height, unsigned spacing = 0);
unsigned minimumWidth();
void setGeometry(Geometry &geometry);
void setMargin(unsigned margin);
void setParent(Window &parent);
void setVisible(bool visible);
HorizontalLayout();
//private:
unsigned margin;
unsigned width;
unsigned height;
struct Children {
VerticalLayout *layout;
Widget *widget;
unsigned width, height, spacing;
};
nall::linear_vector<Children> children;
};

View File

@@ -0,0 +1,88 @@
void VerticalLayout::setParent(Window &parent) {
foreach(child, children) {
if(child.layout) child.layout->setParent(parent);
if(child.widget) parent.append(*child.widget);
}
}
void VerticalLayout::append(HorizontalLayout &layout, unsigned width, unsigned height, unsigned spacing) {
layout.width = width;
layout.height = height;
children.append({ &layout, 0, width, height, spacing });
}
void VerticalLayout::append(Widget &widget, unsigned width, unsigned height, unsigned spacing) {
children.append({ 0, &widget, width, height, spacing });
}
void VerticalLayout::setGeometry(Geometry &geometry) {
geometry.x += margin;
geometry.y += margin;
geometry.width -= margin * 2;
geometry.height -= margin * 2;
unsigned geometryWidth = width ? width : geometry.width;
unsigned geometryHeight = height ? height : geometry.height;
Geometry baseGeometry = geometry;
linear_vector<VerticalLayout::Children> children = this->children;
unsigned minimumHeight = 0;
foreach(child, children) minimumHeight += child.height + child.spacing;
unsigned autosizeWidgets = 0;
foreach(child, children) {
if(child.height == 0) autosizeWidgets++;
}
foreach(child, children) {
if(child.width == 0) child.width = geometryWidth;
if(child.height == 0) child.height = (geometryHeight - minimumHeight) / autosizeWidgets;
}
unsigned maxWidth = 0;
foreach(child, children) {
maxWidth = max(maxWidth, child.width);
}
foreach(child, children) {
if(child.layout) {
child.layout->setGeometry(geometry);
geometry.x = baseGeometry.x;
geometry.width = baseGeometry.width;
geometry.y += child.spacing;
geometry.height -= child.spacing;
}
if(child.widget) {
child.widget->setGeometry({ geometry.x, geometry.y, child.width, child.height });
geometry.y += child.height + child.spacing;
geometry.height -= child.height + child.spacing;
}
}
geometry.x += maxWidth;
geometry.width -= maxWidth;
}
void VerticalLayout::setMargin(unsigned margin_) {
margin = margin_;
}
unsigned VerticalLayout::minimumHeight() {
unsigned height = margin * 2;
foreach(child, children) height += child.height + child.spacing;
return height;
}
void VerticalLayout::setVisible(bool visible) {
foreach(child, children) {
if(child.layout) child.layout->setVisible(visible);
if(child.widget) child.widget->setVisible(visible);
}
}
VerticalLayout::VerticalLayout() {
margin = 0;
width = 0;
height = 0;
}

View File

@@ -0,0 +1,23 @@
struct HorizontalLayout;
struct VerticalLayout : public Layout {
void append(HorizontalLayout &layout, unsigned width, unsigned height, unsigned spacing = 0);
void append(Widget &widget, unsigned width, unsigned height, unsigned spacing = 0);
unsigned minimumHeight();
void setGeometry(Geometry &geometry);
void setMargin(unsigned margin);
void setParent(Window &parent);
void setVisible(bool visible);
VerticalLayout();
//private:
unsigned margin;
unsigned width;
unsigned height;
struct Children {
HorizontalLayout *layout;
Widget *widget;
unsigned width, height, spacing;
};
nall::linear_vector<Children> children;
};

224
bsnes/phoenix/core/state.hpp Executable file
View File

@@ -0,0 +1,224 @@
struct Font::State {
bool bold;
string family;
bool italic;
unsigned size;
bool underline;
State() {
bold = false;
italic = false;
size = 8;
underline = false;
}
};
struct Window::State {
bool backgroundColor;
unsigned backgroundColorRed, backgroundColorGreen, backgroundColorBlue;
bool fullScreen;
Geometry geometry;
reference_array<Layout&> layout;
reference_array<Menu&> menu;
Font *menuFont;
bool menuVisible;
bool resizable;
Font *statusFont;
string statusText;
bool statusVisible;
string title;
bool visible;
reference_array<Widget&> widget;
Font *widgetFont;
State() {
backgroundColor = false;
backgroundColorRed = 0;
backgroundColorGreen = 0;
backgroundColorBlue = 0;
fullScreen = false;
geometry = { 128, 128, 256, 256 };
menuFont = 0;
menuVisible = false;
resizable = true;
statusVisible = false;
visible = false;
widgetFont = 0;
}
};
struct Action::State {
bool enabled;
Window *parent;
bool visible;
State() {
enabled = true;
parent = 0;
visible = true;
}
};
struct Menu::State {
reference_array<Action&> action;
string text;
};
struct Item::State {
string text;
};
struct CheckItem::State {
bool checked;
string text;
State() {
checked = false;
}
};
struct RadioItem::State {
bool checked;
reference_array<RadioItem&> group;
string text;
State() {
checked = true;
}
};
struct Widget::State {
bool abstract;
bool enabled;
Font *font;
Geometry geometry;
bool visible;
State() {
abstract = false;
enabled = true;
font = 0;
geometry = { 0, 0, 0, 0 };
visible = true;
}
};
struct Button::State {
string text;
State() {
}
};
struct CheckBox::State {
bool checked;
string text;
State() {
checked = false;
}
};
struct ComboBox::State {
unsigned selection;
linear_vector<string> text;
State() {
selection = 0;
}
};
struct HexEdit::State {
unsigned columns;
unsigned length;
unsigned offset;
unsigned rows;
State() {
columns = 16;
length = 0;
offset = 0;
rows = 16;
}
};
struct HorizontalSlider::State {
unsigned length;
unsigned position;
State() {
length = 101;
position = 0;
}
};
struct Label::State {
string text;
};
struct LineEdit::State {
bool editable;
string text;
State() {
editable = true;
}
};
struct ListView::State {
bool checkable;
array<bool> checked;
lstring headerText;
bool headerVisible;
bool selected;
unsigned selection;
linear_vector<lstring> text;
State() {
checkable = false;
headerVisible = false;
selected = false;
selection = 0;
}
};
struct ProgressBar::State {
unsigned position;
State() {
position = 0;
}
};
struct RadioBox::State {
bool checked;
reference_array<RadioBox&> group;
string text;
State() {
checked = true;
}
};
struct TextEdit::State {
unsigned cursorPosition;
bool editable;
string text;
bool wordWrap;
State() {
cursorPosition = 0;
editable = true;
wordWrap = false;
}
};
struct VerticalSlider::State {
unsigned length;
unsigned position;
State() {
length = 101;
position = 0;
}
};

View File

@@ -0,0 +1,22 @@
static void Action_setFont(GtkWidget *widget, gpointer font) {
if(font == 0) return;
gtk_widget_modify_font(widget, (PangoFontDescription*)font);
if(GTK_IS_CONTAINER(widget)) {
gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Action_setFont, (PangoFontDescription*)font);
}
}
void pAction::setEnabled(bool enabled) {
gtk_widget_set_sensitive(widget, enabled);
}
void pAction::setFont(Font &font) {
Action_setFont(widget, font.p.gtkFont);
}
void pAction::setVisible(bool visible) {
gtk_widget_set_visible(widget, visible);
}
void pAction::constructor() {
}

View File

@@ -0,0 +1,22 @@
static void CheckItem_tick(CheckItem *self) {
if(self->p.locked == false && self->onTick) self->onTick();
}
bool pCheckItem::checked() {
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
}
void pCheckItem::setChecked(bool checked) {
locked = true;
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), checked);
locked = false;
}
void pCheckItem::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pCheckItem::constructor() {
widget = gtk_check_menu_item_new_with_label("");
g_signal_connect_swapped(G_OBJECT(widget), "toggled", G_CALLBACK(CheckItem_tick), (gpointer)&checkItem);
}

View File

@@ -0,0 +1,12 @@
static void Item_tick(Item *self) {
if(self->onTick) self->onTick();
}
void pItem::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pItem::constructor() {
widget = gtk_menu_item_new_with_label("");
g_signal_connect_swapped(G_OBJECT(widget), "activate", G_CALLBACK(Item_tick), (gpointer)&item);
}

View File

@@ -0,0 +1,19 @@
void pMenu::append(Action &action) {
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), action.p.widget);
gtk_widget_show(action.p.widget);
}
void pMenu::setFont(Font &font) {
pAction::setFont(font);
foreach(item, menu.state.action) item.p.setFont(font);
}
void pMenu::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pMenu::constructor() {
submenu = gtk_menu_new();
widget = gtk_menu_item_new_with_label("");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), submenu);
}

View File

@@ -0,0 +1,33 @@
static void RadioItem_tick(RadioItem *self) {
if(self->p.locked == false && self->checked() && self->onTick) self->onTick();
}
bool pRadioItem::checked() {
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
}
void pRadioItem::setChecked() {
locked = true;
foreach(item, radioItem.state.group) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item.p.widget), false);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), true);
locked = false;
}
void pRadioItem::setGroup(const reference_array<RadioItem&> &group) {
foreach(item, group, n) {
if(n == 0) continue;
GSList *currentGroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(group[0].p.widget));
if(currentGroup != gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item.p.widget))) {
gtk_radio_menu_item_set_group(GTK_RADIO_MENU_ITEM(item.p.widget), currentGroup);
}
}
}
void pRadioItem::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pRadioItem::constructor() {
widget = gtk_radio_menu_item_new_with_label(0, "");
g_signal_connect_swapped(G_OBJECT(widget), "toggled", G_CALLBACK(RadioItem_tick), (gpointer)&radioItem);
}

View File

@@ -0,0 +1,3 @@
void pSeparator::constructor() {
widget = gtk_separator_menu_item_new();
}

View File

@@ -1,13 +0,0 @@
static void Button_tick(Button *self) {
if(self->onTick) self->onTick();
}
void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_button_new_with_label(text);
widget->parent = &parent;
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "clicked", G_CALLBACK(Button_tick), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}

View File

@@ -1,59 +0,0 @@
static void Canvas_expose(Canvas *self) {
uint32_t *rgb = self->canvas->bufferRGB;
uint32_t *bgr = self->canvas->bufferBGR;
for(unsigned y = self->object->widget->allocation.height; y; y--) {
for(unsigned x = self->object->widget->allocation.width; x; x--) {
uint32_t pixel = *rgb++;
*bgr++ = ((pixel << 16) & 0xff0000) | (pixel & 0x00ff00) | ((pixel >> 16) & 0x0000ff);
}
}
gdk_draw_rgb_32_image(
self->object->widget->window,
self->object->widget->style->fg_gc[GTK_WIDGET_STATE(self->object->widget)],
0, 0, self->object->widget->allocation.width, self->object->widget->allocation.height,
GDK_RGB_DITHER_NONE, (guchar*)self->canvas->bufferBGR, self->canvas->pitch
);
}
void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
canvas->bufferRGB = new uint32_t[width * height]();
canvas->bufferBGR = new uint32_t[width * height]();
canvas->pitch = width * sizeof(uint32_t);
object->widget = gtk_drawing_area_new();
widget->parent = &parent;
GdkColor color;
color.pixel = color.red = color.green = color.blue = 0;
gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color);
gtk_widget_set_double_buffered(object->widget, false);
gtk_widget_add_events(object->widget, GDK_EXPOSURE_MASK);
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
uint32_t* Canvas::buffer() {
return canvas->bufferRGB;
}
void Canvas::redraw() {
GdkRectangle rect;
rect.x = 0;
rect.y = 0;
rect.width = object->widget->allocation.width;
rect.height = object->widget->allocation.height;
gdk_window_invalidate_rect(object->widget->window, &rect, true);
}
Canvas::Canvas() {
canvas = new Canvas::Data;
canvas->bufferRGB = 0;
canvas->bufferBGR = 0;
}
Canvas::~Canvas() {
if(canvas->bufferRGB) delete[] canvas->bufferRGB;
if(canvas->bufferBGR) delete[] canvas->bufferBGR;
}

Some files were not shown because too many files have changed in this diff Show More