Compare commits

..

21 Commits
v098 ... v099

Author SHA1 Message Date
Tim Allen
c074c6e064 Update to v099 release.
byuu says:

Time for a new release. There are a few important emulation improvements
and a few new features; but for the most part, this release focuses on
major code refactoring, the details of which I will mostly spare you.

The major change is that, as of v099, the SNES balanced and performance
cores have been removed from higan. Basically, in addition to my five
other emulation cores, these were too much of a burden to maintain. And
they've come along as far as I was able to develop them. If you need to
use these cores, please use these two from the v098 release.

I'm very well aware that ~80% of the people using higan for SNES
emulation were using the two removed profiles. But they simply had
to go. Hopefully in the future, we can compensate for their loss by
increasing the performance of the accuracy core.

Changelog (since v098):

SFC: balanced profile removed
SFC: performance profile removed
SFC: expansion port devices can now be changed during gameplay (atlhough
    you shouldn't)
SFC: fixed bug in SharpRTC leap year calculations
SFC: emulated new research findings for the S-DD1 coprocessor
SFC: fixed CPU emulation-mode wrapping bug with pei, [dp], [dp]+y
    instructions [AWJ]
SFC: fixed Super Game Boy bug that caused the bottom tile-row to flicker
    in games
GB: added MBC1M (multi-cart) mapper; icarus can't detect these so manual
    manifests are needed for now
GB: corrected return value when HuC3 unmapped RAM is read; fixes Robopon
    [endrift]
GB: improved STAT IRQ emulation; fixes Altered Space, etc [endrift,
    gekkio]
GB: partial emulation of DMG STAT write IRQ bug; fixes Legend of Zerd,
    Road Rash, etc
nall: execute() fix, for some Linux platforms that had trouble detecting
    icarus
nall: new BitField class; which allows for simplifying flag/register
    emulation in various cores
ruby: added Windows WASAPI audio driver (experimental)
ruby: remove attempts to call glSwapIntervalEXT (fixes crashing on some
    Linux systems)
ui: timing settings panel removed
video: restored saturation, gamma, luminance settings
video: added new post-emulation sprite system; light gun cursors are
    now higher-resolution
audio: new resampler (6th-order Butterworth biquad IIR); quite a bit
    faster than the old one
audio: added optional basic reverb filter (for fun)
higan: refresh video outside cooperative threads (workaround for shoddy
    code in AMD graphics drivers)
higan: individual emulation cores no longer have unique names
higan: really substantial code refactoring; 43% reduction in binary size

Off the bat, here are the known bugs:

hiro/Windows: focus stealing bug on startup. Needs to be fixed in hiro,
    not with a cheap hack to tomoko.

higan/SFC: some of the coprocessors are saving some volatile memory to
    disk. Completely harmless, but still needs to be fixed.

ruby/WASAPI: some sound cards have a lot of issues with the current driver
    (eg FitzRoy's). We need to find a clean way to fix this before it
    can be made the default driver. Which would be a huge win because
    the latency improvements are substantial, and in exclusive mode,
    WASAPI allows G-sync to work very well.

[From the v099 WIP thread, here's the changelog since v098r19:

- GB: don't force mode 1 during force-blank; fixes v098r16 regression
  with many Game Boy games
- GB: only perform the STAT write IRQ bug during vblank, not hblank
  (still not hardware accurate, though)

-Ed.]
2016-06-11 11:13:18 +10:00
Tim Allen
50420e3dd2 Update to v098r19 release.
byuu says:

Changelog:
- added nall/bit-field.hpp
- updated all CPU cores (sans LR35902 due to some complexities) to use
  BitFields instead of bools
- updated as many CPU cores as I could to use BitFields instead of union {
  struct { uint8_t ... }; }; pairs

The speed changes are mostly a wash for this. In some instances,
I noticed a ~2-3% speedup (eg SNES emulation), and in others a 2-3%
slowdown (eg Famicom emulation.) It's within the margin of error, so
it's safe to say it has no impact.

This does give us a lot of new useful things, however:

- no more manual reconstruction of flag values from lots of left shifts
  and ORs
- no more manual deconstruction of flag values from lots of ANDs
- ability to get completely free aliases to flag groups (eg GSU can
  provide alt2, alt1 and also alt (which is alt2,alt1 combined)
- removes the need for the nasty order_lsbN macro hack (eventually will
  make higan 100% endian independent)
- saves us from insane compilers that try and do nasty things with
  alignment on union-structs
- saves us from insane compilers that try to store bit-field bits in
  reverse order
- will allow some really novel new use cases (I'm planning an
  instant-decode ARM opcode function, for instance.)
- reduces code size (we can serialize flag registers in one line instead
  of one for each flag)

However, I probably won't use it for super critical code that's constantly
reading out register values (eg PPU MMIO registers.) I think there we
would end up with a performance penalty.
2016-06-09 08:26:35 +10:00
Tim Allen
b08449215a Update to v098r18 release.
byuu says:

Changelog:
- hiro: fixed the BrowserDialog column resizing when navigating to new
  folders (prevents clipping of filenames)
  - note: this is kind of a quick-fix; but I have a good idea how to do
    the proper fix now
- nall: added BitField<T, Lo, Hi> class
  - note: not yet working on the SFC CPU class; need to go at it with
    a debugger to find out what's happening
- GB: emulated DMG/SGB STAT IRQ bug; fixes Zerd no Densetsu and Road Rash
  (won't fix anything else; don't get hopes up)
2016-06-07 21:55:03 +10:00
Tim Allen
9b452c9f5f Update to v098r17 release.
byuu says:

Changelog:
- fixed Super Game Boy regression from v096r04 with bottom tile row
  flickering
- fixed GB STAT IRQ regression from previous WIP
  - Altered Space is now playable
  - GBVideoPlayer isn't; but nobody seems to know exactly what weird
    hardware quirk that one relies on to work
- ~3-4% speed improvement in SuperFX games by eliminating function<>
  callback on register assignments
  - most noticeable in Doom in-game; least noticeable on Yoshi's Island
    title screen (darn)
- finished GSU core and SuperFX coprocessor code cleanups
- did some more work cleaning up the LR35902 core and GB CPU code

Just a fair warning: don't get your hopes up on these GB
fixes. Cliffhanger now hangs completely (har har), and none of the
other bugs are fixed. We pretty much did all this work just for Altered
Space. So, I hope you like playing Altered Space.
2016-06-06 08:10:01 +10:00
Tim Allen
3681961ca5 Update to v098r16 release.
byuu says:

Changelog:
- GNUmakefile: reverted $(call unique,) to $(strip)
- processor/r6502: removed templates; reduces object size from 146.5kb
  to 107.6kb
- processor/lr35902: removed templates; reduces object size from 386.2kb
  to 197.4kb
- processor/spc700: merged op macros for switch table declarations
- sfc/coprocessor/sa1: partial cleanups; flattened directory structure
- sfc/coprocessor/superfx: partial cleanups; flattened directory structure
- sfc/coprocessor/icd2: flattened directory structure
- gb/ppu: changed behavior of STAT IRQs

Major caveat! The GB/GBC STAT IRQ changes has a major bug in it somewhere
that's seriously breaking most games. I'm pushing the WIP anyway, because
I believe the changes to be mostly correct. I'd like to get more people
looking at these changes, and also try more heavy-handed hacking and
diff comparison logging between the previous WIP and this one.
2016-06-05 15:03:21 +10:00
Tim Allen
20ac95ee49 Update to v098r15 release.
byuu says:

Changelog:
- removed template usage from processor/spc700; cleaned up many function
  names and the switch table
  - object size: 176.8kb => 127.3kb
  - source code size: 43.5kb => 37.0kb
- fixed processor/r65816 BRK/COP vector regression [hex_usr]
- corrected HuC3 unmapped RAM read value; fixes Robopon [endrift]
- cosmetic: simplified the butterworth constant calculation
  [Wolfram|Alpha]

The SPC700 core changes took forever, about three hours of work.

Only the LR35902 and R6502 still need their template functions
removed. The point of this is that it doesn't cause any speed penalty
to do so, and it results in smaller binary sizes and faster compilation
times.
2016-06-05 14:52:43 +10:00
Tim Allen
fdc41611cf Update to v098r14 release.
byuu says:

Changelog:
- improved attenuation of biquad filter by computing butterworth Q
  coefficients correctly (instead of using the same constant)
- adding 1e-25 to each input sample into the biquad filters to try and
  prevent denormalization
- updated normalization from [0.0 to 1.0] to [-1.0 to +1.0]; volume/reverb
  happen in floating-point mode now
- good amount of work to make the base Emulator::Audio support any number
  of output channels
  - so that we don't have to do separate work on left/right channels;
    and can instead share the code for each channel
- Emulator::Interface::audioSample(int16 left, int16 right); changed to:
  - Emulator::Interface::audioSample(double* samples, uint channels);
  - samples are normalized [-1.0 to +1.0]
  - for now at least, channels will be the value given to
    Emulator::Audio::reset()
- fixed GUI crash on startup when audio driver is set to None

I'm probably going to be updating ruby to accept normalized doubles as
well; but I'm not sure if I will try and support anything other 2-channel
audio output. It'll depend on how easy it is to do so; perhaps it'll be
a per-driver setting.

The denormalization thing is fierce. If that happens, it drops the
emulator framerate from 220fps to about 20fps for Game Boy emulation. And
that happens basically whenever audio output is silent. I'm probably
also going to make a nall/denormal.hpp file at some point with
platform-specific functionality to set the CPU state to "denormals as
zero" where applicable. I'll still add the 1e-25 offset (inaudible)
as another fallback.
2016-06-01 21:23:22 +10:00
Tim Allen
839813d0f1 Update to v098r13 release.
byuu says:

Changelog:
- nall/dsp returns with new iir/biquad.hpp and resampler/cubic.hpp files
- nall/queue.hpp added (simple ring buffer ... nall/vector wouldn't
  cause too many moves with FIFO)
- audio streams now only buffer 20ms; so even if multiple audio streams
  desync, latency can never exceed 20ms
- replaced blackman windwed sinc FIR hermite audio filter with transposed
  direct form II biquadratic sixth-order IIR butterworth filter (better
  attenuation of frequencies above 20KHz, faster, no need for decimation,
  less code)
- put in experimental eight-tap echo filter (a lot better than what I
  had before, but still rather weak)
- substantial cleanups to the SuperFX GSU processor core (slightly
  faster, 479KB->100KB object file, 42.7KB->33.4KB source code size,
  way less code duplication)

We'll definitely want to test the whole SuperFX library (not many games)
just to make sure there's no regressions caused by this one.

Not sure what I want to do with audio processing effects yet. I've always
really wanted lots of fun controls to customize audio, and now finally
with this new biquad filter, I can finally start implementing real
effects. For instance, an equalizer wouldn't be too complicated anymore.

The new reverb effect is still a poor man's version. I need to find human
readable source for implementing a comb-filter properly. I'm pretty sure
I can already treat nall::queue as an all-pass filter since all that
does is phase shift (fancy audio term for "delay audio"). What's really
going to be hard is figuring out how to expose user-friendly settings for
controlling it. It looks like you need a bunch of coprime coefficients,
and I don't think casual users are going to be able to hand-enter coprime
values to get the echo effect they want. I uh ... don't even know how
to calculate coprime values dynamically right now >_> But we're going
to have to, as they are correlated to the output sampling rate.

We'll definitely want to make some audio profiles so that users can
quickly select pre-configured themes that sound nice, but expose the
underlying coefficients so that they can tweak stuff to their liking. This
isn't just about higan, this is about me trying to learn digital signal
processing, so please don't be too upset about feature creep or anything
on this.

Anyway ... I'm having some difficulties with my audio right now. When
the reverb effect is enabled, there's a bunch of static on system
reset for just a moment. But this should not be possible. nall::queue
is initializing all previous reverb sample elements to 0.0. I don't
understand where static is coming in from. Further, we have the same
issue with both the windowed sinc and the biquad filters ... a bit of
a popping sound when starting a game. Any help tracking this down would
be appreciated.

There's also one really annoying issue ... I can't seem to do reverb
or volume adjustments with normalized samples. If I say "volume *= 0.5"
in higan/audio/audio.cpp line 68, it doesn't just halve the volume, it
adds a whole bunch of distortion. This makes absolutely zero sense to
me. The sample values are between 0.0 (mute) and 1.0 (full volume) here,
so multiplying a double by 0.5 shouldn't cause distortion. So right now,
I'm doing these adjustments with less precision after denormalizing back
to int16. Anyone ever see something like that? :/
2016-06-01 08:29:36 +10:00
Tim Allen
7f3cfa17b9 Update to v098r12 release.
byuu says:

Changelog:
- higan/video: added support for Emulator::Sprite
- higan/resource: a new system for accessing embedded binary files
  inside the emulation cores; holds the sprites
- higan/sfc/superscope,justifier: re-enabled display of crosshairs
- higan/sfc/superscope: fixed turbo toggle (also shows different
  crosshair color when in turbo mode)
- higan/sfc/ppu: always outputs at 512x480 resolution now
  - causes a slight speed-hit from ~127fps to ~125fps;
  - but allows high-resolution 32x32 cursors that look way better;
  - also avoids the need to implement sprite scaling logic

Right now, the PPU code to always output at 480-height is a really gross
hack. Don't worry, I'll make that nicer before release.

Also, superscope.cpp and justifier.cpp are built around a 256x240
screen. But since we now have 512x480, we can make the cursor's movement
much smoother by doubling the resolution on both axes. The actual games
won't see any accuracy improvements when firing the light guns, but the
cursors will animate nicer so I think it's still worth it. I'll work on
that before the next release as well.

The current 32x32 cursors are nicer, but we can do better now with full
24-bit color. So feel free to submit alternatives. I'll probably reject
them, but you can always try :D

The sprites don't support alpha blending, just color keying (0x00000000
= transparent; anything else is 0xff......). We can revisit that later
if necessary.

The way I have it designed, the only files that do anything with
Emulator::Sprite at all are the superscope and justifier folders.
I didn't have to add any hooks anywhere else. Rendering the sprite is
a lot cleaner than the old code, too.
2016-05-26 21:20:15 +10:00
Tim Allen
ae5d380d06 Update to v098r11 release.
byuu says:

Changelog:
- fixed nall/path.hpp compilation issue
- fixed ruby/audio/xaudio header declaration compilation issue (again)
- cleaned up xaudio2.hpp file to match my coding syntax (12.5% of the
  file was whitespace overkill)
- added null terminator entry to nall/windows/utf8.hpp argc[] array
- nall/windows/guid.hpp uses the Windows API for generating the GUID
  - this should stop all the bug reports where two nall users were
    generating GUIDs at the exact same second
- fixed hiro/cocoa compilation issue with uint# types
- fixed major higan/sfc Super Game Boy audio latency issue
- fixed higan/sfc CPU core bug with pei, [dp], [dp]+y instructions
- major cleanups to higan/processor/r65816 core
  - merged emulation/native-mode opcodes
  - use camel-case naming on memory.hpp functions
  - simplify address masking code for memory.hpp functions
  - simplify a few opcodes themselves (avoid redundant copies, etc)
  - rename regs.* to r.* to match modern convention of other CPU cores
- removed device.order<> concept from Emulator::Interface
  - cores will now do the translation to make the job of the UI easier
- fixed plurality naming of arrays in Emulator::Interface
  - example: emulator.ports[p].devices[d].inputs[i]
  - example: vector<Medium> media
- probably more surprises

Major show-stoppers to the next official release:
- we need to work on GB core improvements: LY=153/0 case, multiple STAT
  IRQs case, GBC audio output regs, etc.
- we need to re-add software cursors for light guns (Super Scope,
  Justifier)
- after the above, we need to fix the turbo button for the Super Scope

I really have no idea how I want to implement the light guns. Ideally,
we'd want it in higan/video, so we can support the NES Zapper with the
same code. But this isn't going to be easy, because only the SNES knows
when its output is interlaced, and its resolutions can vary as
{256,512}x{224,240,448,480} which requires pixel doubling that was
hard-coded to the SNES-specific behavior, but isn't appropriate to be
exposed in higan/video.
2016-05-25 21:13:02 +10:00
Tim Allen
3ebc77c148 Update to v098r10 release.
byuu says:

Changelog:
- synchronized tomoko, loki, icarus with extensive changes to nall
  (118KiB diff)
2016-05-16 19:51:12 +10:00
Tim Allen
6ae0abe3d3 Update to v098r09 release.
byuu says:

Changelog:
- fixed major nall/vector/prepend bug
- renamed hiro/ListView to hiro/TableView
- added new hiro/ListView control which is a simplified abstraction of
  hiro/TableView
- updated higan's cheat database window and icarus' scan dialog to use
  the new ListView control
- compilation works once again on all platforms (Windows, Cocoa, GTK,
  Qt)
- the loki skeleton compiles once again (removed nall/DSP references;
  updated port/device ID names)

Small catch: need to capture layout resize events internally in Windows
to call resizeColumns. For now, just resize the icarus window to get it
to use the full window width for list view items.
2016-05-04 20:07:13 +10:00
Tim Allen
0955295475 Update to v098r08 release.
byuu says:

Changelog:
- nall/vector rewritten from scratch
- higan/audio uses nall/vector instead of raw pointers
- higan/sfc/coprocessor/sdd1 updated with new research information
- ruby/video/glx and ruby/video/glx2: fuck salt glXSwapIntervalEXT!

The big change here is definitely nall/vector. The Windows, OS X and Qt
ports won't compile until you change some first/last strings to
left/right, but GTK will compile.

I'd be really grateful if anyone could stress-test nall/vector. Pretty
much everything I do relies on this class. If we introduce a bug, the
worst case scenario is my entire SFC game dump database gets corrupted,
or the byuu.org server gets compromised. So it's really critical that we
test the hell out of this right now.

The S-DD1 changes mean you need to update your installation of icarus
again. Also, even though the Lunar FMV never really worked on the
accuracy core anyway (it didn't initialize the PPU properly), it really
won't work now that we emulate the hard-limit of 16MiB for S-DD1 games.
2016-05-02 19:57:04 +10:00
Tim Allen
7cdae5195a Update to v098r07 release.
byuu says:

Changelog:
- GB: support modeSelect and RAM for MBC1M (Momotarou Collection)
- audio: implemented native resampling support into Emulator::Stream
- audio: removed nall::DSP completely

Unfortunately, the new resampler didn't turn out quite as fast as I had
hoped. The final hermite resampling added some overhead; and I had to
bump up the kernel count to 500 from 400 to get the buzzing to go away
on my main PC. I think that's due to it running at 48000hz output
instead of 44100hz output, maybe?

Compared to Ryphecha's:
(NES) Mega Man 2: 167fps -> 166fps
(GB) Mega Man II: 224fps -> 200fps
(WSC) Riviera: 143fps -> 151fps

Odd that the WS/WSC ends up faster while the DMG/CGB ends up slower.

But this knocks 922 lines down to 146 lines. The only files left in all
of higan not written (or rewritten) by me are ruby/xaudio2.h and
libco/ppc.c
2016-04-23 17:55:59 +10:00
Tim Allen
e2ee6689a0 Update to v098r06 release.
byuu says:

Changelog:
- emulation cores now refresh video from host thread instead of
  cothreads (fix AMD crash)
- SFC: fixed another bug with leap year months in SharpRTC emulation
- SFC: cleaned up camelCase on function names for
  armdsp,epsonrtc,hitachidsp,mcc,nss,sharprtc classes
- GB: added MBC1M emulation (requires manually setting mapper=MBC1M in
  manifest.bml for now, sorry)
- audio: implemented Emulator::Audio mixer and effects processor
- audio: implemented Emulator::Stream interface
  - it is now possible to have more than two audio streams: eg SNES
    + SGB + MSU1 + Voicer-Kun (eventually)
- audio: added reverb delay + reverb level settings; exposed balance
  configuration in UI
- video: reworked palette generation to re-enable saturation, gamma,
  luminance adjustments
- higan/emulator.cpp is gone since there was nothing left in it

I know you guys are going to say the color adjust/balance/reverb stuff
is pointless. And indeed it mostly is. But I like the idea of allowing
some fun special effects and configurability that isn't system-wide.

Note: there seems to be some kind of added audio lag in the SGB
emulation now, and I don't really understand why. The code should be
effectively identical to what I had before. The only main thing is that
I'm sampling things to 48000hz instead of 32040hz before mixing. There's
no point where I'm intentionally introducing added latency though. I'm
kind of stumped, so if anyone wouldn't mind taking a look at it, it'd be
much appreciated :/

I don't have an MSU1 test ROM, but the latency issue may affect MSU1 as
well, and that would be very bad.
2016-04-22 23:35:51 +10:00
Tim Allen
55e507d5df Update to v098r05 release.
byuu says:

Changelog:
- WS/WSC: re-added support for screen rotation (code is inside WS core)
- ruby: changed sample(uint16_t left, uint16_t right) to sample(int16_t
  left, int16_t right);
  - requires casting to uint prior to shifting in each driver, but
    I felt it was misleading to use uint16_t just to avoid that
- ruby: WASAPI is now built in by default; has wareya's improvements,
  and now supports latency adjust
- tomoko: audio settings panel has new "Exclusive Mode" checkbox for
  WASAPI driver only
  - note: although the setting *does* take effect in real-time, I'd
    suggest restarting the emulator after changing it
- tomoko: audio latency can now be set to 0ms (which in reality means
  "the minimum supported by the driver")
- all: increased cothread size from 512KiB to 2MiB to see if it fixes
  bullshit AMD driver crashes
  - this appears to cause a slight speed penalty due to cache locality
    going down between threads, though
2016-04-18 20:49:45 +10:00
Tim Allen
a2d3b8ba15 Update to v098r04 release.
byuu says:

Changelog:
- SFC: fixed behavior of 21fx $21fe register when no device is connected
  (must return zero)
- SFC: reduced 21fx buffer size to 1024 bytes in both directions to
  mirror the FT232H we are using
- SFC: eliminated dsp/modulo-array.hpp [1]
- higan: implemented higan/video interface and migrated all cores to it
  [2]

[1] the echo history buffer was 8-bytes, so there was no need for it at
all here. Not sure what I was thinking. The BRR buffer was 12-bytes, and
has very weird behavior ... but there's only a single location in the
code where it actually writes to this buffer. It's much easier to just
write to the buffer three times there instead of implementing an entire
class just to abstract away two lines of code. This change actually
boosted the speed from ~124.5fps to around ~127.5fps, but that's within
the margin of error for GCC. I doubt it's actually faster this way.

The DSP core could really use a ton of work. It comes from a port of
blargg's spc_dsp to my coding style, but he was extremely fond of using
32-bit signed integers everywhere. There's a lot of opportunity to
remove red tape masking by resizing the variables to their actual state
sizes.

I really need to find where I put spc_dsp6.sfc from blargg. It's a great
test to verify if I've made any mistakes in my implementation that would
cause regressions. Don't suppose anyone has it?

[2] so again, the idea is that higan/audio and higan/video are going to
sit between the emulation cores and the user interfaces. The hope is to
output raw encoding data from the emulation cores without having to
worry about the video display format (generally 24-bit RGB) of the host
display. And also to avoid having to repeat myself with eg three
separate implementations of interframe blending, and so on.

Furthermore, the idea is that the user interface can configure its side
of the settings, and the emulation cores can configure their sides.
Thus, neither has to worry about the other end. And now we can spin off
new user interfaces much easier without having to mess with all of these
things.

Right now, I've implemented color emulation, interframe blending and
SNES horizontal color bleed. I did not implement scanlines (and
interlace effects for them) yet, but I probably will at some point.

Further, for right now, the WonderSwan/Color screen rotation is busted
and will only show games in the horizontal orientation. Obviously this
must be fixed before the next official release, but I'll want to think
about how to implement it.

Also, the SNES light gun pointers are missing for now.

Things are a bit messy right now as I've gone through several revisions
of how to handle these things, so a good house cleaning is in order once
everything is feature-complete again. I need to sit down and think
through how and where I want to handle things like light gun cursors,
LCD icons, and maybe even rasterized text messages.

And obviously ... higan/audio is still just nall::DSP's headers. I need
to revamp that whole interface. I want to make it quite powerful with
a true audio mixer so I can handle things like
SNES+SGB+MSU1+Voicer-Kun+SNES-CD (five separate audio streams at once.)

The video system has the concept of "effects" for things like color
bleed and interframe blending. I want to extend on this with useful
other effects, such as NTSC simulation, maybe bringing back my mini-HQ2x
filter, etc. I'd also like to restore the saturation/gamma/luma
adjustment sliders ... I always liked allowing people to compensate for
their displays without having to change settings system-wide. Lastly,
I've always wanted to see some audio effects. Although I doubt we'll
ever get my dream of CoreAudio-style profiles, I'd like to get some
basic equalizer settings and echo/reverb effects in there.
2016-04-12 07:29:56 +10:00
Tim Allen
1929ad47d2 Update to v098r03 release.
byuu says:

It took several hours, but I've rebuilt much of the SNES' bus memory
mapping architecture.

The new design unifies the cartridge string-based mapping
("00-3f,80-bf:8000-ffff") and internal bus.map calls. The map() function
now has an accompanying unmap() function, and instead of a fixed 256
callbacks, it'll scan to find the first available slot. unmap() will
free slots up when zero addresses reference a given slot.

The controllers and expansion port are now both entirely dynamic.
Instead of load/unload/power/reset, they only have the constructor
(power/reset/load) and destructor (unload). What this means is you can
now dynamically change even expansion port devices after the system is
loaded.

Note that this is incredibly dangerous and stupid, but ... oh well. The
whole point of this was for 21fx. There's no way to change the expansion
port device prior to loading a game, but if the 21fx isn't active, then
the reset vector hijack won't work. Now you can load a 21fx game, change
the expansion port device, and simply reset the system to active the
device.

The unification of design between controller port devices and expansion
port devices is nice, and overall this results in a reduction of code
(all of the Mapping stuff in Cartridge is gone, replaced with direct bus
mapping.) And there's always the potential to expand this system more in
the future now.

The big missing feature right now is the ability to push/pop mappings.
So if you look at how the 21fx does the reset vector, you might vomit
a little bit. But ... it works.

Also changed exit(0) to _exit(0) in the POSIX version of nall::execute.

[The _exit(0) thing is an attempt to make higan not crash when it tries
to launch icarus and it's not on $PATH. The theory is that higan forks,
then the child tries to exec icarus and fails, so it exits, all the
unique_ptrs clean up their resources and tell the X server to free
things the parent process is still using. Calling _exit() prevents
destructors from running, and seems to prevent the problem. -Ed.]
2016-04-09 20:21:18 +10:00
Tim Allen
7403e69307 Update to v098r02 release.
byuu says:

Changelog:
- SFC: fixed a regression on auto joypad polling due to missing
  parentheses
- SFC: exported new PPU::vdisp() const -> uint; function [1]
- SFC: merged PPU MMIO functions into the read/write handles (as
  I previously did for the CPU)
- higan: removed individual emulator core names (bnes, bsnes, bgb, bgba,
  bws) [2] Forgot:
- to remove /tomoko from the about dialog

[1] note that technically I was relying on the cached, per-frame
overscan setting when the CPU and light guns were polling the number of
active display scanlines per frame. This was technically incorrect as
you can change this value mid-frame and it'll kick in. I've never seen
any game toggle overscan every frame, we only know about this because
anomie tested this a long time ago. So, nothing should break, but ...
you know how the SNES is. You can't even look at the code without
something breaking, so I figured I'd mention it >_>

[2] I'll probably keep referring to the SNES core as bsnes anyway.
I don't mind if you guys use the b<system> names as shorthand. The
simplification is mostly to make the branding easier.
2016-04-09 15:20:41 +10:00
Tim Allen
19e1d89f00 Update to v098r01 release.
byuu says:

Changelog:
- SFC: balanced profile removed
- SFC: performance profile removed
- SFC: code for handling non-threaded CPU, SMP, DSP, PPU removed
- SFC: Coprocessor, Controller (and expansion port) shared Thread code
  merged to SFC::Cothread
  - Cothread here just means "Thread with CPU affinity" (couldn't think
    of a better name, sorry)
- SFC: CPU now has vector<Thread*> coprocessors, peripherals;
  - this is the beginning of work to allow expansion port devices to be
    dynamically changed at run-time
- ruby: all audio drivers default to 48000hz instead of 22050hz now if
  no frequency is assigned
  - note: the WASAPI driver can default to whatever the native frequency
    is; doesn't have to be 48000hz
- tomoko: removed the ability to change the frequency from the UI (but
  it will display the frequency used)
- tomoko: removed the timing settings panel
  - the goal is to work toward smooth video via adaptive sync
  - the model is broken by not being in control of the audio frequency
    anyway
  - it's further broken by PAL running at 50hz and WSC running at 75hz
  - it was always broken anyway by SNES interlace timing varying from
    progressive timing
- higan: audio/ stub created (for now, it's just nall/dsp/ moved here
  and included as a header)
- higan: video/ stub created
- higan/GNUmakefile: now includes build rules for essential components
  (libco, emulator, audio, video)

The audio changes are in preparation to merge wareya's awesome WASAPI
work without the need for the nall/dsp resampler.
2016-04-09 13:40:12 +10:00
Tim Allen
aff00506c5 Update to v098 hotfix release.
byuu says:

There was a minor SNES input regression spotted very shortly after
release.
2016-04-09 12:43:12 +10:00
628 changed files with 12730 additions and 30899 deletions

View File

@@ -2,11 +2,10 @@ include ../nall/GNUmakefile
target := tomoko
# target := loki
profile := accuracy
# console := true
flags += -I. -I.. -O3
objects := libco
objects := libco audio video resource
# profile-guided optimization mode
# pgo := instrument
@@ -54,6 +53,11 @@ compile = \
all: build;
obj/libco.o: ../libco/libco.c $(call rwildcard,../libco/)
obj/audio.o: audio/audio.cpp $(call rwildcard,audio/)
obj/video.o: video/video.cpp $(call rwildcard,video/)
obj/resource.o: resource/resource.cpp $(call rwildcard,resource/)
ui := target-$(target)
include $(ui)/GNUmakefile

90
higan/audio/audio.cpp Normal file
View File

@@ -0,0 +1,90 @@
#include <emulator/emulator.hpp>
namespace Emulator {
#include "stream.cpp"
Audio audio;
auto Audio::reset(maybe<uint> channels_, maybe<double> frequency_) -> void {
if(channels_) channels = channels_();
if(frequency_) frequency = frequency_();
streams.reset();
reverb.reset();
reverb.resize(channels);
for(auto c : range(channels)) {
reverb[c].resize(7);
reverb[c][0].resize(1229);
reverb[c][1].resize(1559);
reverb[c][2].resize(1907);
reverb[c][3].resize(4057);
reverb[c][4].resize(8117);
reverb[c][5].resize(8311);
reverb[c][6].resize(9931);
}
}
auto Audio::setInterface(Interface* interface) -> void {
this->interface = interface;
}
auto Audio::setVolume(double volume) -> void {
this->volume = volume;
}
auto Audio::setBalance(double balance) -> void {
this->balance = balance;
}
auto Audio::setReverb(bool enabled) -> void {
this->reverbEnable = enabled;
}
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
shared_pointer<Stream> stream = new Stream;
stream->reset(channels, frequency, this->frequency);
streams.append(stream);
return stream;
}
auto Audio::process() -> void {
while(true) {
for(auto& stream : streams) {
if(!stream->pending()) return;
}
double samples[channels] = {0};
for(auto& stream : streams) {
double buffer[16];
uint length = stream->read(buffer), offset = 0;
for(auto c : range(channels)) {
samples[c] += buffer[offset];
if(++offset >= length) offset = 0;
}
}
for(auto c : range(channels)) {
samples[c] /= streams.size();
if(reverbEnable) {
samples[c] *= 0.125;
for(auto n : range(7)) samples[c] += 0.125 * reverb[c][n].last();
for(auto n : range(7)) reverb[c][n].write(samples[c]);
samples[c] *= 8.000;
}
samples[c] *= volume;
}
if(channels == 2) {
if(balance < 0.0) samples[1] *= 1.0 + balance;
if(balance > 0.0) samples[0] *= 1.0 - balance;
}
interface->audioSample(samples, channels);
}
}
}

65
higan/audio/audio.hpp Normal file
View File

@@ -0,0 +1,65 @@
#pragma once
#include <nall/dsp/iir/biquad.hpp>
#include <nall/dsp/resampler/cubic.hpp>
namespace Emulator {
struct Interface;
struct Audio;
struct Stream;
struct Audio {
auto reset(maybe<uint> channels = nothing, maybe<double> frequency = nothing) -> void;
auto setInterface(Interface*) -> void;
auto setVolume(double volume) -> void;
auto setBalance(double balance) -> void;
auto setReverb(bool enabled) -> void;
auto createStream(uint channels, double frequency) -> shared_pointer<Stream>;
private:
auto process() -> void;
Interface* interface = nullptr;
vector<shared_pointer<Stream>> streams;
uint channels = 0;
double frequency = 0.0;
double volume = 1.0;
double balance = 0.0;
bool reverbEnable = false;
vector<vector<queue<double>>> reverb;
friend class Stream;
};
struct Stream {
auto reset(uint channels, double inputFrequency, double outputFrequency) -> void;
auto pending() const -> bool;
auto read(double* samples) -> uint;
auto write(const double* samples) -> void;
template<typename... P> auto sample(P&&... p) -> void {
double samples[sizeof...(P)] = {forward<P>(p)...};
write(samples);
}
private:
const uint order = 6; //Nth-order filter (must be an even number)
struct Channel {
vector<DSP::IIR::Biquad> iir;
DSP::Resampler::Cubic resampler;
};
vector<Channel> channels;
friend class Audio;
};
extern Audio audio;
}

35
higan/audio/stream.cpp Normal file
View File

@@ -0,0 +1,35 @@
auto Stream::reset(uint channels_, double inputFrequency, double outputFrequency) -> void {
channels.reset();
channels.resize(channels_);
for(auto& channel : channels) {
if(outputFrequency / inputFrequency <= 0.5) {
channel.iir.resize(order / 2);
for(auto phase : range(order / 2)) {
double q = DSP::IIR::Biquad::butterworth(order, phase);
channel.iir[phase].reset(DSP::IIR::Biquad::Type::LowPass, 20000.0 / inputFrequency, q);
}
}
channel.resampler.reset(inputFrequency, outputFrequency);
}
}
auto Stream::pending() const -> bool {
return channels && channels[0].resampler.pending();
}
auto Stream::read(double* samples) -> uint {
for(auto c : range(channels)) samples[c] = channels[c].resampler.read();
return channels.size();
}
auto Stream::write(const double* samples) -> void {
for(auto c : range(channels)) {
double sample = samples[c] + 1e-25; //constant offset used to suppress denormals
for(auto& iir : channels[c].iir) sample = iir.process(sample);
channels[c].resampler.write(sample);
}
audio.process();
}

View File

@@ -1,3 +0,0 @@
objects += emulator
obj/emulator.o: emulator/emulator.cpp $(call rwildcard,emulator/)

View File

@@ -1,34 +0,0 @@
#include <emulator/emulator.hpp>
namespace Emulator {
auto Interface::videoColor(uint16 r, uint16 g, uint16 b) -> uint32 {
double saturation = 1.0;
double gamma = 1.0;
double luminance = 1.0;
if(saturation != 1.0) {
uint16 grayscale = uclamp<16>((r + g + b) / 3);
double inverse = max(0.0, 1.0 - saturation);
r = uclamp<16>(r * saturation + grayscale * inverse);
g = uclamp<16>(g * saturation + grayscale * inverse);
b = uclamp<16>(b * saturation + grayscale * inverse);
}
if(gamma != 1.0) {
double reciprocal = 1.0 / 32767.0;
r = r > 32767 ? r : uint16(32767 * pow(r * reciprocal, gamma));
g = g > 32767 ? g : uint16(32767 * pow(g * reciprocal, gamma));
b = b > 32767 ? b : uint16(32767 * pow(b * reciprocal, gamma));
}
if(luminance != 1.0) {
r = uclamp<16>(r * luminance);
g = uclamp<16>(g * luminance);
b = uclamp<16>(b * luminance);
}
return 255 << 24 | (r >> 8) << 16 | (g >> 8) << 8 | (b >> 8) << 0;
}
}

View File

@@ -1,23 +1,18 @@
#pragma once
#include <nall/nall.hpp>
#include <nall/dsp.hpp>
using namespace nall;
#include <audio/audio.hpp>
#include <video/video.hpp>
#include <resource/resource.hpp>
namespace Emulator {
static const string Name = "higan";
static const string Version = "098";
static const string Version = "099";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";
#if defined(PROFILE_ACCURACY)
static const string Profile = "Accuracy";
#elif defined(PROFILE_BALANCED)
static const string Profile = "Balanced";
#elif defined(PROFILE_PERFORMANCE)
static const string Profile = "Performance";
#endif
}
#include "interface.hpp"

View File

@@ -17,13 +17,13 @@ struct Interface {
} capability;
} information;
struct Media {
struct Medium {
uint id;
string name;
string type;
bool bootable; //false for cartridge slots (eg Sufami Turbo cartridges)
};
vector<Media> media;
vector<Medium> media;
struct Device {
uint id;
@@ -35,23 +35,22 @@ struct Interface {
string name;
uintptr guid; //user data field
};
vector<Input> input;
vector<uint> order;
vector<Input> inputs;
};
struct Port {
uint id;
string name;
vector<Device> device;
vector<Device> devices;
};
vector<Port> port;
vector<Port> ports;
struct Bind {
virtual auto loadRequest(uint, string, string, bool) -> void {}
virtual auto loadRequest(uint, string, bool) -> void {}
virtual auto saveRequest(uint, string) -> void {}
virtual auto videoRefresh(const uint32*, uint, uint, uint) -> void {}
virtual auto audioSample(int16, int16) -> void {}
virtual auto audioSample(const double*, uint) -> void {}
virtual auto inputPoll(uint, uint, uint) -> int16 { return 0; }
virtual auto inputRumble(uint, uint, uint, bool) -> void {}
virtual auto dipSettings(const Markup::Node&) -> uint { return 0; }
@@ -65,7 +64,7 @@ struct Interface {
auto loadRequest(uint id, string path, bool required) -> void { return bind->loadRequest(id, path, required); }
auto saveRequest(uint id, string path) -> void { return bind->saveRequest(id, path); }
auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void { return bind->videoRefresh(data, pitch, width, height); }
auto audioSample(int16 lsample, int16 rsample) -> void { return bind->audioSample(lsample, rsample); }
auto audioSample(const double* samples, uint channels) -> void { return bind->audioSample(samples, channels); }
auto inputPoll(uint port, uint device, uint input) -> int16 { return bind->inputPoll(port, device, input); }
auto inputRumble(uint port, uint device, uint input, bool enable) -> void { return bind->inputRumble(port, device, input, enable); }
auto dipSettings(const Markup::Node& node) -> uint { return bind->dipSettings(node); }
@@ -75,7 +74,13 @@ struct Interface {
//information
virtual auto manifest() -> string = 0;
virtual auto title() -> string = 0;
//video information
virtual auto videoFrequency() -> double = 0;
virtual auto videoColors() -> uint32 { return 1 << 19; }
virtual auto videoColor(uint32 color) -> uint64 { return 0; }
//audio information
virtual auto audioFrequency() -> double = 0;
//media interface

View File

@@ -57,7 +57,7 @@ auto APU::main() -> void {
//output = filter.run_lopass(output);
output = sclamp<16>(output);
interface->audioSample(output, output);
stream->sample(output / 32768.0);
tick();
}
@@ -89,6 +89,7 @@ auto APU::power() -> void {
auto APU::reset() -> void {
create(APU::Enter, 21'477'272);
stream = Emulator::audio.createStream(1, 21'477'272.0 / 12.0);
pulse[0].reset();
pulse[1].reset();

View File

@@ -1,4 +1,6 @@
struct APU : Thread {
shared_pointer<Emulator::Stream> stream;
APU();
static auto Enter() -> void;

View File

@@ -1,22 +1,17 @@
#pragma once
//license: GPLv3
//started: 2011-09-05
#include <emulator/emulator.hpp>
#include <processor/r6502/r6502.hpp>
namespace Famicom {
namespace Info {
static const string Name = "bnes";
static const uint SerializerVersion = 2;
static const uint SerializerVersion = 3;
}
}
/*
bnes - Famicom emulator
authors: byuu, Ryphecha
license: GPLv3
project started: 2011-09-05
*/
#include <libco/libco.h>
namespace Famicom {
@@ -27,7 +22,7 @@ namespace Famicom {
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
if(thread) co_delete(thread);
thread = co_create(65536 * sizeof(void*), entrypoint);
thread = co_create(65'536 * sizeof(void*), entrypoint);
this->frequency = frequency;
clock = 0;
}

View File

@@ -15,12 +15,15 @@ auto Input::latch(bool data) -> void {
}
auto Input::data(bool port) -> bool {
//table to convert native button ordering to Emulator::Interface ordering
static const uint lookup[] = {5, 4, 6, 7, 0, 1, 2, 3};
bool result = 0;
if(port == 0) {
if(port1 == Device::Joypad) {
if(counter1 >= 8) return 1;
result = interface->inputPoll(0, 0u, counter1);
result = interface->inputPoll(0, 0u, lookup[counter1]);
if(latchdata == 0) counter1++;
}
}
@@ -28,7 +31,7 @@ auto Input::data(bool port) -> bool {
if(port == 1) {
if(port2 == Device::Joypad) {
if(counter2 >= 8) return 1;
result = interface->inputPoll(1, 0u, counter2);
result = interface->inputPoll(1, 0u, lookup[counter2]);
if(latchdata == 0) counter2++;
}
}

View File

@@ -22,25 +22,24 @@ Interface::Interface() {
media.append({ID::Famicom, "Famicom", "fc", true});
{ Device device{0, ID::Port1 | ID::Port2, "Controller"};
device.input.append({0, 0, "A" });
device.input.append({1, 0, "B" });
device.input.append({2, 0, "Select"});
device.input.append({3, 0, "Start" });
device.input.append({4, 0, "Up" });
device.input.append({5, 0, "Down" });
device.input.append({6, 0, "Left" });
device.input.append({7, 0, "Right" });
device.order = {4, 5, 6, 7, 1, 0, 2, 3};
this->device.append(device);
device.inputs.append({0, 0, "Up" });
device.inputs.append({1, 0, "Down" });
device.inputs.append({2, 0, "Left" });
device.inputs.append({3, 0, "Right" });
device.inputs.append({4, 0, "B" });
device.inputs.append({5, 0, "A" });
device.inputs.append({6, 0, "Select"});
device.inputs.append({7, 0, "Start" });
devices.append(device);
}
port.append({0, "Port 1"});
port.append({1, "Port 2"});
ports.append({0, "Port 1"});
ports.append({1, "Port 2"});
for(auto& device : this->device) {
for(auto& port : this->port) {
for(auto& device : devices) {
for(auto& port : ports) {
if(device.portmask & (1 << port.id)) {
port.device.append(device);
port.devices.append(device);
}
}
}
@@ -58,6 +57,61 @@ auto Interface::videoFrequency() -> double {
return 21477272.0 / (262.0 * 1364.0 - 4.0);
}
auto Interface::videoColors() -> uint32 {
return 1 << 9;
}
auto Interface::videoColor(uint32 n) -> uint64 {
double saturation = 2.0;
double hue = 0.0;
double contrast = 1.0;
double brightness = 1.0;
double gamma = settings.colorEmulation ? 1.8 : 2.2;
int color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
static const double black = 0.518, white = 1.962, attenuation = 0.746;
static const double levels[8] = {
0.350, 0.518, 0.962, 1.550,
1.094, 1.506, 1.962, 1.962,
};
double lo_and_hi[2] = {
levels[level + 4 * (color == 0x0)],
levels[level + 4 * (color < 0xd)],
};
double y = 0.0, i = 0.0, q = 0.0;
auto wave = [](int p, int color) { return (color + p + 8) % 12 < 6; };
for(int p : range(12)) {
double spot = lo_and_hi[wave(p, color)];
if(((n & 0x040) && wave(p, 12))
|| ((n & 0x080) && wave(p, 4))
|| ((n & 0x100) && wave(p, 8))
) spot *= attenuation;
double v = (spot - black) / (white - black);
v = (v - 0.5) * contrast + 0.5;
v *= brightness / 12.0;
y += v;
i += v * cos((Math::Pi / 6.0) * (p + hue));
q += v * sin((Math::Pi / 6.0) * (p + hue));
}
i *= saturation;
q *= saturation;
auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : pow(f, 2.2 / gamma); };
uint64 r = uclamp<16>(65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q));
uint64 g = uclamp<16>(65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q));
uint64 b = uclamp<16>(65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q));
return r << 32 | g << 16 | b << 0;
}
auto Interface::audioFrequency() -> double {
return 21477272.0 / 12.0;
}
@@ -182,7 +236,11 @@ auto Interface::get(const string& name) -> any {
}
auto Interface::set(const string& name, const any& value) -> bool {
if(name == "Color Emulation" && value.is<bool>()) return settings.colorEmulation = value.get<bool>(), true;
if(name == "Color Emulation" && value.is<bool>()) {
settings.colorEmulation = value.get<bool>();
system.configureVideoPalette();
return true;
}
if(name == "Scanline Emulation" && value.is<bool>()) return settings.scanlineEmulation = value.get<bool>(), true;
return false;
}

View File

@@ -28,6 +28,8 @@ struct Interface : Emulator::Interface {
auto manifest() -> string;
auto title() -> string;
auto videoFrequency() -> double;
auto videoColors() -> uint32;
auto videoColor(uint32 color) -> uint64;
auto audioFrequency() -> double;
auto loaded() -> bool;
@@ -53,7 +55,7 @@ struct Interface : Emulator::Interface {
auto set(const string& name, const any& value) -> bool override;
private:
vector<Device> device;
vector<Device> devices;
};
struct Settings {

View File

@@ -3,7 +3,6 @@
namespace Famicom {
PPU ppu;
#include "video.cpp"
#include "serialization.cpp"
@@ -43,10 +42,13 @@ auto PPU::scanline() -> void {
auto PPU::frame() -> void {
status.field ^= 1;
video.refresh();
scheduler.exit(Scheduler::Event::Frame);
}
auto PPU::refresh() -> void {
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
}
auto PPU::power() -> void {
}

View File

@@ -1,5 +1,3 @@
#include "video.hpp"
struct PPU : Thread {
static auto Enter() -> void;
auto main() -> void;
@@ -7,6 +5,7 @@ struct PPU : Thread {
auto scanline() -> void;
auto frame() -> void;
auto refresh() -> void;
auto power() -> void;
auto reset() -> void;

View File

@@ -1,94 +0,0 @@
Video video;
Video::Video() {
output = new uint32[256 * 480];
paletteLiteral = new uint32[1 << 9];
paletteStandard = new uint32[1 << 9];
paletteEmulation = new uint32[1 << 9];
}
auto Video::reset() -> void {
memory::fill(output(), 256 * 480);
for(auto color : range(1 << 9)) {
paletteLiteral[color] = color;
paletteStandard[color] = generateColor(color, 2.0, 0.0, 1.0, 1.0, 2.2);
paletteEmulation[color] = generateColor(color, 2.0, 0.0, 1.0, 1.0, 1.8);
}
}
auto Video::refresh() -> void {
auto output = this->output();
auto& palette = settings.colorEmulation ? paletteEmulation : paletteStandard;
if(settings.scanlineEmulation) {
for(uint y = 0; y < 240; y++) {
auto source = ppu.buffer + y * 256;
auto targetLo = output + y * 512;
auto targetHi = output + y * 512 + 256;
for(uint x = 0; x < 256; x++) {
auto color = palette[*source++];
*targetLo++ = color;
*targetHi++ = (255 << 24) | ((color & 0xfefefe) >> 1);
}
}
interface->videoRefresh(output, 256 * sizeof(uint32), 256, 480);
} else {
for(uint y = 0; y < 240; y++) {
auto source = ppu.buffer + y * 256;
auto target = output + y * 256;
for(uint x = 0; x < 256; x++) {
*target++ = palette[*source++];
}
}
interface->videoRefresh(output, 256 * sizeof(uint32), 256, 240);
}
}
auto Video::generateColor(
uint n, double saturation, double hue,
double contrast, double brightness, double gamma
) -> uint32 {
int color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
static const double black = 0.518, white = 1.962, attenuation = 0.746;
static const double levels[8] = {
0.350, 0.518, 0.962, 1.550,
1.094, 1.506, 1.962, 1.962,
};
double lo_and_hi[2] = {
levels[level + 4 * (color == 0x0)],
levels[level + 4 * (color < 0xd)],
};
double y = 0.0, i = 0.0, q = 0.0;
auto wave = [](int p, int color) { return (color + p + 8) % 12 < 6; };
for(int p : range(12)) {
double spot = lo_and_hi[wave(p, color)];
if(((n & 0x040) && wave(p, 12))
|| ((n & 0x080) && wave(p, 4))
|| ((n & 0x100) && wave(p, 8))
) spot *= attenuation;
double v = (spot - black) / (white - black);
v = (v - 0.5) * contrast + 0.5;
v *= brightness / 12.0;
y += v;
i += v * std::cos((3.141592653 / 6.0) * (p + hue));
q += v * std::sin((3.141592653 / 6.0) * (p + hue));
}
i *= saturation;
q *= saturation;
auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); };
uint r = uclamp<16>(65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q));
uint g = uclamp<16>(65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q));
uint b = uclamp<16>(65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q));
return interface->videoColor(r, g, b);
}

View File

@@ -1,16 +0,0 @@
struct Video {
Video();
auto reset() -> void;
auto refresh() -> void;
private:
auto generateColor(uint, double, double, double, double, double) -> uint32;
unique_pointer<uint32[]> output;
unique_pointer<uint32[]> paletteLiteral;
unique_pointer<uint32[]> paletteStandard;
unique_pointer<uint32[]> paletteEmulation;
};
extern Video video;

View File

@@ -13,6 +13,7 @@ auto Scheduler::enter(Mode mode_) -> Event {
mode = mode_;
host = co_active();
co_switch(resume);
if(event == Event::Frame) ppu.refresh();
return event;
}

View File

@@ -2,6 +2,7 @@
namespace Famicom {
#include "video.cpp"
#include "serialization.cpp"
System system;
@@ -42,13 +43,20 @@ auto System::power() -> void {
}
auto System::reset() -> void {
Emulator::video.reset();
Emulator::video.setInterface(interface);
configureVideoPalette();
configureVideoEffects();
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
cartridge.reset();
cpu.reset();
apu.reset();
ppu.reset();
input.reset();
scheduler.reset();
video.reset();
}
auto System::init() -> void {

View File

@@ -12,6 +12,11 @@ struct System {
auto init() -> void;
auto term() -> void;
//video.cpp
auto configureVideoPalette() -> void;
auto configureVideoEffects() -> void;
//serialization.cpp
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;

View File

@@ -0,0 +1,6 @@
auto System::configureVideoPalette() -> void {
Emulator::video.setPalette();
}
auto System::configureVideoEffects() -> void {
}

View File

@@ -25,7 +25,12 @@ auto APU::main() -> void {
hipass(sequencer.left, sequencer.leftBias);
hipass(sequencer.right, sequencer.rightBias);
interface->audioSample(sequencer.left, sequencer.right);
if(!system.sgb()) {
stream->sample(sequencer.left / 32768.0, sequencer.right / 32768.0);
} else {
double samples[] = {sequencer.left / 32768.0, sequencer.right / 32768.0};
interface->audioSample(samples, 2);
}
if(cycle == 0) { //512hz
if(phase == 0 || phase == 2 || phase == 4 || phase == 6) { //256hz
@@ -58,6 +63,7 @@ auto APU::hipass(int16& sample, int64& bias) -> void {
auto APU::power() -> void {
create(Enter, 2 * 1024 * 1024);
if(!system.sgb()) stream = Emulator::audio.createStream(2, 2 * 1024 * 1024);
for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
square1.power();

View File

@@ -1,4 +1,6 @@
struct APU : Thread, MMIO {
shared_pointer<Emulator::Stream> stream;
static auto Enter() -> void;
auto main() -> void;
auto hipass(int16& sample, int64& bias) -> void;

View File

@@ -4,6 +4,7 @@ namespace GameBoy {
#include "mbc0/mbc0.cpp"
#include "mbc1/mbc1.cpp"
#include "mbc1m/mbc1m.cpp"
#include "mbc2/mbc2.cpp"
#include "mbc3/mbc3.cpp"
#include "mbc5/mbc5.cpp"
@@ -40,6 +41,7 @@ auto Cartridge::load(System::Revision revision) -> void {
auto mapperid = document["board/mapper"].text();
if(mapperid == "none" ) information.mapper = Mapper::MBC0;
if(mapperid == "MBC1" ) information.mapper = Mapper::MBC1;
if(mapperid == "MBC1M") information.mapper = Mapper::MBC1M;
if(mapperid == "MBC2" ) information.mapper = Mapper::MBC2;
if(mapperid == "MBC3" ) information.mapper = Mapper::MBC3;
if(mapperid == "MBC5" ) information.mapper = Mapper::MBC5;
@@ -70,6 +72,7 @@ auto Cartridge::load(System::Revision revision) -> void {
switch(information.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
case Mapper::MBC1: mapper = &mbc1; break;
case Mapper::MBC1M: mapper = &mbc1m; break;
case Mapper::MBC2: mapper = &mbc2; break;
case Mapper::MBC3: mapper = &mbc3; break;
case Mapper::MBC5: mapper = &mbc5; break;
@@ -139,6 +142,7 @@ auto Cartridge::power() -> void {
mbc0.power();
mbc1.power();
mbc1m.power();
mbc2.power();
mbc3.power();
mbc5.power();

View File

@@ -16,6 +16,7 @@ struct Cartridge : MMIO, property<Cartridge> {
#include "mbc0/mbc0.hpp"
#include "mbc1/mbc1.hpp"
#include "mbc1m/mbc1m.hpp"
#include "mbc2/mbc2.hpp"
#include "mbc3/mbc3.hpp"
#include "mbc5/mbc5.hpp"
@@ -26,6 +27,7 @@ struct Cartridge : MMIO, property<Cartridge> {
enum Mapper : uint {
MBC0,
MBC1,
MBC1M,
MBC2,
MBC3,
MBC5,

View File

@@ -9,7 +9,7 @@ auto Cartridge::HuC3::mmio_read(uint16 addr) -> uint8 {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
return 0xff;
return 0x01; //does not return open collection
}
return 0xff;

View File

@@ -0,0 +1,40 @@
auto Cartridge::MBC1M::mmio_read(uint16 addr) -> uint8 {
if((addr & 0xc000) == 0x0000) { //$0000-3fff
if(!modeSelect) return cartridge.rom_read(addr & 0x3fff);
return cartridge.rom_read((romHi << 4) * 0x4000 + (addr & 0x3fff));
}
if((addr & 0xc000) == 0x4000) { //$4000-7fff
return cartridge.rom_read((romHi << 4 | romLo) * 0x4000 + (addr & 0x3fff));
}
if((addr & 0xe000) == 0xa000) { //$a000-bfff
return cartridge.ram_read(addr & 0x1fff);
}
return 0xff;
}
auto Cartridge::MBC1M::mmio_write(uint16 addr, uint8 data) -> void {
if((addr & 0xe000) == 0x2000) { //$2000-3fff
romLo = data.bits(0,3);
}
if((addr & 0xe000) == 0x4000) { //$4000-5fff
romHi = data.bits(0,1);
}
if((addr & 0xe000) == 0x6000) { //$6000-7fff
modeSelect = data.bit(0);
}
if((addr & 0xe000) == 0xa000) { //$a000-bfff
cartridge.ram_write(addr & 0x1fff, data);
}
}
auto Cartridge::MBC1M::power() -> void {
romHi = 0;
romLo = 1;
modeSelect = 0;
}

View File

@@ -0,0 +1,9 @@
struct MBC1M : MMIO {
auto mmio_read(uint16 addr) -> uint8;
auto mmio_write(uint16 addr, uint8 data) -> void;
auto power() -> void;
uint4 romLo;
uint2 romHi;
uint1 modeSelect;
} mbc1m;

View File

@@ -7,6 +7,10 @@ auto Cartridge::serialize(serializer& s) -> void {
s.integer(mbc1.ram_select);
s.integer(mbc1.mode_select);
s.integer(mbc1m.romLo);
s.integer(mbc1m.romHi);
s.integer(mbc1m.modeSelect);
s.integer(mbc2.ram_enable);
s.integer(mbc2.rom_select);

View File

@@ -14,10 +14,10 @@ auto CPU::Enter() -> void {
auto CPU::main() -> void {
interrupt_test();
exec();
instruction();
}
auto CPU::interrupt_raise(CPU::Interrupt id) -> void {
auto CPU::raise(CPU::Interrupt id) -> void {
if(id == Interrupt::Vblank) {
status.interrupt_request_vblank = 1;
if(status.interrupt_enable_vblank) r.halt = false;
@@ -45,42 +45,32 @@ auto CPU::interrupt_raise(CPU::Interrupt id) -> void {
}
auto CPU::interrupt_test() -> void {
if(r.ime) {
if(status.interrupt_request_vblank && status.interrupt_enable_vblank) {
status.interrupt_request_vblank = 0;
return interrupt_exec(0x0040);
}
if(!r.ime) return;
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);
}
if(status.interrupt_request_vblank && status.interrupt_enable_vblank) {
status.interrupt_request_vblank = 0;
return interrupt(0x0040);
}
}
auto CPU::interrupt_exec(uint16 pc) -> void {
op_io();
op_io();
op_io();
r.ime = 0;
op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0);
r[PC] = pc;
if(status.interrupt_request_stat && status.interrupt_enable_stat) {
status.interrupt_request_stat = 0;
return interrupt(0x0048);
}
if(status.interrupt_request_timer && status.interrupt_enable_timer) {
status.interrupt_request_timer = 0;
return interrupt(0x0050);
}
if(status.interrupt_request_serial && status.interrupt_enable_serial) {
status.interrupt_request_serial = 0;
return interrupt(0x0058);
}
if(status.interrupt_request_joypad && status.interrupt_enable_joypad) {
status.interrupt_request_joypad = 0;
return interrupt(0x0060);
}
}
auto CPU::stop() -> bool {

View File

@@ -3,9 +3,8 @@ struct CPU : Processor::LR35902, Thread, MMIO {
static auto Enter() -> void;
auto main() -> void;
auto interrupt_raise(Interrupt id) -> void;
auto raise(Interrupt id) -> void;
auto interrupt_test() -> void;
auto interrupt_exec(uint16 pc) -> void;
auto stop() -> bool;
auto power() -> void;
@@ -18,9 +17,9 @@ struct CPU : Processor::LR35902, Thread, MMIO {
auto mmio_write(uint16 addr, uint8 data) -> void;
//memory.cpp
auto op_io() -> void;
auto op_read(uint16 addr) -> uint8;
auto op_write(uint16 addr, uint8 data) -> void;
auto io() -> void override;
auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override;
auto cycle_edge() -> void;
auto dma_read(uint16 addr) -> uint8;
auto dma_write(uint16 addr, uint8 data) -> void;

View File

@@ -1,15 +1,15 @@
auto CPU::op_io() -> void {
auto CPU::io() -> void {
cycle_edge();
add_clocks(4);
}
auto CPU::op_read(uint16 addr) -> uint8 {
auto CPU::read(uint16 addr) -> uint8 {
cycle_edge();
add_clocks(4);
return bus.read(addr);
}
auto CPU::op_write(uint16 addr, uint8 data) -> void {
auto CPU::write(uint16 addr, uint8 data) -> void {
cycle_edge();
add_clocks(4);
bus.write(addr, data);

View File

@@ -29,7 +29,7 @@ auto CPU::mmio_joyp_poll() -> void {
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);
if(status.joyp != 0x0f) raise(Interrupt::Joypad);
}
auto CPU::mmio_read(uint16 addr) -> uint8 {

View File

@@ -3,9 +3,7 @@
// 154 scanlines/frame
auto CPU::add_clocks(uint clocks) -> void {
if(system.sgb()) system._clocksExecuted += clocks;
while(clocks--) {
for(auto n : range(clocks)) {
if(++status.clock == 0) {
cartridge.mbc3.second();
}
@@ -25,14 +23,17 @@ auto CPU::add_clocks(uint clocks) -> void {
if(apu.clock < 0) co_switch(apu.thread);
}
if(system.sgb()) scheduler.exit(Scheduler::Event::Step);
if(system.sgb()) {
system._clocksExecuted += clocks;
scheduler.exit(Scheduler::Event::Step);
}
}
auto CPU::timer_262144hz() -> void {
if(status.timer_enable && status.timer_clock == 1) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
raise(Interrupt::Timer);
}
}
}
@@ -41,7 +42,7 @@ auto CPU::timer_65536hz() -> void {
if(status.timer_enable && status.timer_clock == 2) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
raise(Interrupt::Timer);
}
}
}
@@ -50,7 +51,7 @@ auto CPU::timer_16384hz() -> void {
if(status.timer_enable && status.timer_clock == 3) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
raise(Interrupt::Timer);
}
}
}
@@ -59,7 +60,7 @@ auto CPU::timer_8192hz() -> void {
if(status.serial_transfer && status.serial_clock) {
if(--status.serial_bits == 0) {
status.serial_transfer = 0;
interrupt_raise(Interrupt::Serial);
raise(Interrupt::Serial);
}
}
}
@@ -68,7 +69,7 @@ auto CPU::timer_4096hz() -> void {
if(status.timer_enable && status.timer_clock == 0) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
raise(Interrupt::Timer);
}
}
}

View File

@@ -1,22 +1,17 @@
#pragma once
//license: GPLv3
//started: 2010-12-27
#include <emulator/emulator.hpp>
#include <processor/lr35902/lr35902.hpp>
namespace GameBoy {
namespace Info {
static const string Name = "bgb";
static const uint SerializerVersion = 4;
static const uint SerializerVersion = 5;
}
}
/*
bgb - Game Boy, Super Game Boy, and Game Boy Color emulator
author: byuu
license: GPLv3
project started: 2010-12-27
*/
#include <libco/libco.h>
namespace GameBoy {
@@ -27,7 +22,7 @@ namespace GameBoy {
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
if(thread) co_delete(thread);
thread = co_create(65536 * sizeof(void*), entrypoint);
thread = co_create(65'536 * sizeof(void*), entrypoint);
this->frequency = frequency;
clock = 0;
}

View File

@@ -25,19 +25,18 @@ Interface::Interface() {
{
Device device{0, ID::Device, "Controller"};
device.input.append({0, 0, "Up" });
device.input.append({1, 0, "Down" });
device.input.append({2, 0, "Left" });
device.input.append({3, 0, "Right" });
device.input.append({4, 0, "B" });
device.input.append({5, 0, "A" });
device.input.append({6, 0, "Select"});
device.input.append({7, 0, "Start" });
device.order = {0, 1, 2, 3, 4, 5, 6, 7};
this->device.append(device);
device.inputs.append({0, 0, "Up" });
device.inputs.append({1, 0, "Down" });
device.inputs.append({2, 0, "Left" });
device.inputs.append({3, 0, "Right" });
device.inputs.append({4, 0, "B" });
device.inputs.append({5, 0, "A" });
device.inputs.append({6, 0, "Select"});
device.inputs.append({7, 0, "Start" });
devices.append(device);
}
port.append({0, "Device", {device[0]}});
ports.append({0, "Device", {devices[0]}});
}
auto Interface::manifest() -> string {
@@ -52,6 +51,67 @@ auto Interface::videoFrequency() -> double {
return 4194304.0 / (154.0 * 456.0);
}
auto Interface::videoColors() -> uint32 {
return !system.cgb() ? 1 << 2 : 1 << 15;
}
auto Interface::videoColor(uint32 color) -> uint64 {
if(!system.cgb()) {
if(!settings.colorEmulation) {
uint64 L = image::normalize(3 - color, 2, 16);
return L << 32 | L << 16 | L << 0;
} else {
#define DMG_PALETTE_GREEN
//#define DMG_PALETTE_YELLOW
//#define DMG_PALETTE_WHITE
const uint16 monochrome[4][3] = {
#if defined(DMG_PALETTE_GREEN)
{0xaeae, 0xd9d9, 0x2727},
{0x5858, 0xa0a0, 0x2828},
{0x2020, 0x6262, 0x2929},
{0x1a1a, 0x4545, 0x2a2a},
#elif defined(DMG_PALETTE_YELLOW)
{0xffff, 0xf7f7, 0x7b7b},
{0xb5b5, 0xaeae, 0x4a4a},
{0x6b6b, 0x6969, 0x3131},
{0x2121, 0x2020, 0x1010},
#elif defined(DMG_PALETTE_WHITE)
{0xffff, 0xffff, 0xffff},
{0xaaaa, 0xaaaa, 0xaaaa},
{0x5555, 0x5555, 0x5555},
{0x0000, 0x0000, 0x0000},
#endif
};
uint64 R = monochrome[color][0];
uint64 G = monochrome[color][1];
uint64 B = monochrome[color][2];
return R << 32 | G << 16 | B << 0;
}
} else {
uint r = color.bits( 0, 4);
uint g = color.bits( 5, 9);
uint b = color.bits(10,14);
uint64_t R = image::normalize(r, 5, 16);
uint64_t G = image::normalize(g, 5, 16);
uint64_t B = image::normalize(b, 5, 16);
if(settings.colorEmulation) {
R = (r * 26 + g * 4 + b * 2);
G = ( g * 24 + b * 8);
B = (r * 6 + g * 4 + b * 22);
R = image::normalize(min(960, R), 10, 16);
G = image::normalize(min(960, G), 10, 16);
B = image::normalize(min(960, B), 10, 16);
}
return R << 32 | G << 16 | B << 0;
}
}
auto Interface::audioFrequency() -> double {
return 4194304.0 / 2.0;
}
@@ -195,8 +255,18 @@ auto Interface::get(const string& name) -> any {
}
auto Interface::set(const string& name, const any& value) -> bool {
if(name == "Blur Emulation" && value.is<bool>()) return settings.blurEmulation = value.get<bool>(), true;
if(name == "Color Emulation" && value.is<bool>()) return settings.colorEmulation = value.get<bool>(), true;
if(name == "Blur Emulation" && value.is<bool>()) {
settings.blurEmulation = value.get<bool>();
system.configureVideoEffects();
return true;
}
if(name == "Color Emulation" && value.is<bool>()) {
settings.colorEmulation = value.get<bool>();
system.configureVideoPalette();
return true;
}
return false;
}

View File

@@ -30,6 +30,8 @@ struct Interface : Emulator::Interface {
auto manifest() -> string;
auto title() -> string;
auto videoFrequency() -> double;
auto videoColors() -> uint32;
auto videoColor(uint32 color) -> uint64;
auto audioFrequency() -> double;
auto loaded() -> bool;
@@ -67,7 +69,7 @@ struct Interface : Emulator::Interface {
auto joypWrite(bool p15, bool p14) -> void;
private:
vector<Device> device;
vector<Device> devices;
};
struct Settings {

View File

@@ -37,6 +37,7 @@ auto PPU::cgb_read_tile(bool select, uint x, uint y, uint& attr, uint& data) ->
auto PPU::cgb_scanline() -> void {
px = 0;
if(!enabled()) return;
const uint Height = (status.ob_size == 0 ? 8 : 16);
sprites = 0;
@@ -68,7 +69,7 @@ auto PPU::cgb_run() -> void {
ob.priority = 0;
uint color = 0x7fff;
if(status.display_enable) {
if(enabled()) {
cgb_run_bg();
if(status.window_display_enable) cgb_run_window();
if(status.ob_enable) cgb_run_ob();

View File

@@ -19,6 +19,7 @@ auto PPU::dmg_read_tile(bool select, uint x, uint y, uint& data) -> void {
auto PPU::dmg_scanline() -> void {
px = 0;
if(!enabled()) return;
const uint Height = (status.ob_size == 0 ? 8 : 16);
sprites = 0;
@@ -59,7 +60,7 @@ auto PPU::dmg_run() -> void {
ob.palette = 0;
uint color = 0;
if(status.display_enable) {
if(enabled()) {
if(status.bg_enable) dmg_run_bg();
if(status.window_display_enable) dmg_run_window();
if(status.ob_enable) dmg_run_ob();

View File

@@ -24,18 +24,12 @@ auto PPU::mmio_read(uint16 addr) -> uint8 {
}
if(addr == 0xff41) { //STAT
uint 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);
| (status.mode << 0);
}
if(addr == 0xff42) { //SCY
@@ -145,6 +139,13 @@ auto PPU::mmio_write(uint16 addr, uint8 data) -> void {
status.interrupt_oam = data & 0x20;
status.interrupt_vblank = data & 0x10;
status.interrupt_hblank = data & 0x08;
//hardware bug: writes to STAT on DMG,SGB during vblank triggers STAT IRQ
//note: this behavior isn't entirely correct; more research is needed ...
if(!system.cgb() && status.mode == 1) {
cpu.raise(CPU::Interrupt::Stat);
}
return;
}

View File

@@ -1,21 +1,15 @@
#include <gb/gb.hpp>
//LY = 0-153
//Raster = 0-143
//Vblank = 144-153
//LX = 0-455
namespace GameBoy {
PPU ppu;
#include "video.cpp"
#include "mmio.cpp"
#include "dmg.cpp"
#include "cgb.cpp"
#include "serialization.cpp"
auto PPU::enabled() const -> bool { return status.display_enable; }
auto PPU::Enter() -> void {
while(true) scheduler.synchronize(), ppu.main();
}
@@ -24,51 +18,65 @@ auto PPU::main() -> void {
status.lx = 0;
interface->lcdScanline(); //Super Game Boy notification
if(status.display_enable) {
//LYC of zero triggers on LY==153
if((status.lyc && status.ly == status.lyc) || (!status.lyc && status.ly == 153)) {
if(status.interrupt_lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.ly <= 143) {
scanline();
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.ly == 144) {
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
else if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat); //hardware quirk
cpu.interrupt_raise(CPU::Interrupt::Vblank);
}
}
add_clocks(92);
if(status.ly <= 143) {
mode(2);
scanline();
wait(92);
mode(3);
for(auto n : range(160)) {
if(status.display_enable) run();
add_clocks(1);
run();
wait(1);
}
if(status.display_enable) {
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
cpu.hblank();
}
mode(0);
if(enabled()) cpu.hblank();
wait(204);
} else {
add_clocks(160);
mode(1);
wait(456);
}
add_clocks(204);
status.ly++;
if(++status.ly == 154) {
status.ly = 0;
video.refresh();
if(status.ly == 144) {
if(enabled()) cpu.raise(CPU::Interrupt::Vblank);
scheduler.exit(Scheduler::Event::Frame);
}
if(status.ly == 154) {
status.ly = 0;
}
}
auto PPU::add_clocks(uint clocks) -> void {
auto PPU::mode(uint mode) -> void {
status.mode = mode;
}
auto PPU::stat() -> void {
bool irq = status.irq;
status.irq = status.interrupt_hblank && status.mode == 0;
status.irq |= status.interrupt_vblank && status.mode == 1;
status.irq |= status.interrupt_oam && status.mode == 2;
status.irq |= status.interrupt_lyc && coincidence();
if(!irq && status.irq) cpu.raise(CPU::Interrupt::Stat);
}
auto PPU::coincidence() -> bool {
uint ly = status.ly;
if(ly == 153 && status.lx >= 92) ly = 0; //LYC=0 triggers early during LY=153
return ly == status.lyc;
}
auto PPU::refresh() -> void {
if(!system.sgb()) Emulator::video.refresh(screen, 160 * sizeof(uint32), 160, 144);
}
auto PPU::wait(uint clocks) -> void {
while(clocks--) {
stat();
if(status.dma_active) {
uint hi = status.dma_clock++;
uint lo = hi & (cpu.status.speed_double ? 1 : 3);
@@ -92,10 +100,10 @@ auto PPU::add_clocks(uint clocks) -> void {
}
auto PPU::hflip(uint data) const -> uint {
return ((data & 0x8080) >> 7) | ((data & 0x4040) >> 5)
| ((data & 0x2020) >> 3) | ((data & 0x1010) >> 1)
| ((data & 0x0808) << 1) | ((data & 0x0404) << 3)
| ((data & 0x0202) << 5) | ((data & 0x0101) << 7);
return (data & 0x8080) >> 7 | (data & 0x4040) >> 5
| (data & 0x2020) >> 3 | (data & 0x1010) >> 1
| (data & 0x0808) << 1 | (data & 0x0404) << 3
| (data & 0x0202) << 5 | (data & 0x0101) << 7;
}
auto PPU::power() -> void {
@@ -141,6 +149,7 @@ auto PPU::power() -> void {
for(auto& n : bgpd) n = 0x0000;
for(auto& n : obpd) n = 0x0000;
status.irq = false;
status.lx = 0;
status.display_enable = 0;
@@ -156,6 +165,7 @@ auto PPU::power() -> void {
status.interrupt_oam = 0;
status.interrupt_vblank = 0;
status.interrupt_hblank = 0;
status.mode = 0;
status.scy = 0;
status.scx = 0;

View File

@@ -1,9 +1,13 @@
#include "video.hpp"
struct PPU : Thread, MMIO {
auto enabled() const -> bool;
static auto Enter() -> void;
auto main() -> void;
auto add_clocks(uint clocks) -> void;
auto mode(uint) -> void;
auto stat() -> void;
auto coincidence() -> bool;
auto refresh() -> void;
auto wait(uint clocks) -> void;
auto hflip(uint data) const -> uint;
@@ -43,6 +47,7 @@ struct PPU : Thread, MMIO {
function<auto () -> void> run;
struct Status {
bool irq; //STAT IRQ line
uint lx;
//$ff40 LCDC
@@ -60,6 +65,7 @@ struct PPU : Thread, MMIO {
bool interrupt_oam;
bool interrupt_vblank;
bool interrupt_hblank;
uint2 mode;
//$ff42 SCY
uint8 scy;

View File

@@ -9,6 +9,7 @@ auto PPU::serialize(serializer& s) -> void {
s.array(bgpd);
s.array(obpd);
s.integer(status.irq);
s.integer(status.lx);
s.integer(status.display_enable);
@@ -24,6 +25,7 @@ auto PPU::serialize(serializer& s) -> void {
s.integer(status.interrupt_oam);
s.integer(status.interrupt_vblank);
s.integer(status.interrupt_hblank);
s.integer(status.mode);
s.integer(status.scy);
s.integer(status.scx);

View File

@@ -1,105 +0,0 @@
Video video;
Video::Video() {
output = new uint32[160 * 144];
paletteLiteral = new uint32[1 << 15];
paletteStandard = new uint32[1 << 15];
paletteEmulation = new uint32[1 << 15];
}
auto Video::power() -> void {
memory::fill(output(), 160 * 144 * sizeof(uint32));
if(system.dmg()) {
for(auto color : range(1 << 2)) {
paletteLiteral[color] = color;
uint L = image::normalize(3 - color, 2, 16);
paletteStandard[color] = interface->videoColor(L, L, L);
uint R = monochrome[color][0];
uint G = monochrome[color][1];
uint B = monochrome[color][2];
paletteEmulation[color] = interface->videoColor(R, G, B);
}
}
if(system.sgb()) {
for(auto color : range(1 << 2)) {
paletteLiteral[color] = color;
paletteStandard[color] = color;
paletteEmulation[color] = color;
}
}
if(system.cgb()) {
for(auto color : range(1 << 15)) {
paletteLiteral[color] = color;
uint r = (uint5)(color >> 0);
uint g = (uint5)(color >> 5);
uint b = (uint5)(color >> 10);
uint R = image::normalize(r, 5, 16);
uint G = image::normalize(g, 5, 16);
uint B = image::normalize(b, 5, 16);
paletteStandard[color] = interface->videoColor(R, G, B);
R = (r * 26 + g * 4 + b * 2);
G = ( g * 24 + b * 8);
B = (r * 6 + g * 4 + b * 22);
R = image::normalize(min(960, R), 10, 16);
G = image::normalize(min(960, G), 10, 16);
B = image::normalize(min(960, B), 10, 16);
paletteEmulation[color] = interface->videoColor(R, G, B);
}
}
}
auto Video::refresh() -> void {
auto output = this->output();
auto& palette = settings.colorEmulation ? paletteEmulation : paletteStandard;
for(uint y = 0; y < 144; y++) {
auto source = ppu.screen + y * 160;
auto target = output + y * 160;
if(settings.blurEmulation) {
for(uint x = 0; x < 160; x++) {
auto a = palette[*source++];
auto b = *target;
*target++ = (a + b - ((a ^ b) & 0x01010101)) >> 1;
}
} else {
for(uint x = 0; x < 160; x++) {
auto color = palette[*source++];
*target++ = color;
}
}
}
interface->videoRefresh(output, 4 * 160, 160, 144);
}
#define DMG_PALETTE_GREEN
//#define DMG_PALETTE_YELLOW
//#define DMG_PALETTE_WHITE
const uint16 Video::monochrome[4][3] = {
#if defined(DMG_PALETTE_GREEN)
{0xaeae, 0xd9d9, 0x2727},
{0x5858, 0xa0a0, 0x2828},
{0x2020, 0x6262, 0x2929},
{0x1a1a, 0x4545, 0x2a2a},
#elif defined(DMG_PALETTE_YELLOW)
{0xffff, 0xf7f7, 0x7b7b},
{0xb5b5, 0xaeae, 0x4a4a},
{0x6b6b, 0x6969, 0x3131},
{0x2121, 0x2020, 0x1010},
#else //DMG_PALETTE_WHITE
{0xffff, 0xffff, 0xffff},
{0xaaaa, 0xaaaa, 0xaaaa},
{0x5555, 0x5555, 0x5555},
{0x0000, 0x0000, 0x0000},
#endif
};

View File

@@ -1,16 +0,0 @@
struct Video {
Video();
auto power() -> void;
auto refresh() -> void;
private:
unique_pointer<uint32[]> output;
unique_pointer<uint32[]> paletteLiteral;
unique_pointer<uint32[]> paletteStandard;
unique_pointer<uint32[]> paletteEmulation;
static const uint16 monochrome[4][3];
};
extern Video video;

View File

@@ -13,6 +13,7 @@ auto Scheduler::enter(Mode mode_) -> Event {
mode = mode_;
host = co_active();
co_switch(resume);
if(event == Event::Frame) ppu.refresh();
return event;
}

View File

@@ -2,6 +2,7 @@
namespace GameBoy {
#include "video.cpp"
#include "serialization.cpp"
System system;
@@ -59,12 +60,21 @@ auto System::unload() -> void {
}
auto System::power() -> void {
if(!system.sgb()) {
Emulator::video.reset();
Emulator::video.setInterface(interface);
configureVideoPalette();
configureVideoEffects();
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
}
bus.power();
cartridge.power();
cpu.power();
ppu.power();
apu.power();
video.power();
scheduler.power();
_clocksExecuted = 0;

View File

@@ -29,6 +29,10 @@ struct System {
auto unload() -> void;
auto power() -> void;
//video.cpp
auto configureVideoPalette() -> void;
auto configureVideoEffects() -> void;
//serialization.cpp
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;

View File

@@ -0,0 +1,9 @@
auto System::configureVideoPalette() -> void {
if(sgb()) return;
Emulator::video.setPalette();
}
auto System::configureVideoEffects() -> void {
if(sgb()) return;
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
}

View File

@@ -63,7 +63,7 @@ auto APU::main() -> void {
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15;
if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0;
interface->audioSample(sclamp<16>(lsample << 6), sclamp<16>(rsample << 6)); //should be <<5, use <<6 for added volume
stream->sample(sclamp<16>(lsample << 6) / 32768.0, sclamp<16>(rsample << 6) / 32768.0); //should be <<5; use <<6 for added volume
step(512);
}
@@ -74,6 +74,7 @@ auto APU::step(uint clocks) -> void {
auto APU::power() -> void {
create(APU::Enter, 16'777'216);
stream = Emulator::audio.createStream(2, 16'777'216.0 / 512.0);
square1.power();
square2.power();

View File

@@ -1,4 +1,6 @@
struct APU : Thread, MMIO {
shared_pointer<Emulator::Stream> stream;
#include "registers.hpp"
static auto Enter() -> void;

View File

@@ -1,10 +1,10 @@
auto CPU::bus_idle() -> void {
auto CPU::busIdle() -> void {
prefetch_step(1);
}
auto CPU::bus_read(unsigned mode, uint32 addr) -> uint32 {
unsigned wait = bus_wait(mode, addr);
unsigned word = pipeline.fetch.instruction;
auto CPU::busRead(uint mode, uint32 addr) -> uint32 {
uint wait = busWait(mode, addr);
uint word = pipeline.fetch.instruction;
if(addr >= 0x1000'0000) {
prefetch_step(wait);
@@ -35,8 +35,8 @@ auto CPU::bus_read(unsigned mode, uint32 addr) -> uint32 {
return word;
}
auto CPU::bus_write(unsigned mode, uint32 addr, uint32 word) -> void {
unsigned wait = bus_wait(mode, addr);
auto CPU::busWrite(uint mode, uint32 addr, uint32 word) -> void {
uint wait = busWait(mode, addr);
if(addr >= 0x1000'0000) {
prefetch_step(wait);
@@ -57,7 +57,7 @@ auto CPU::bus_write(unsigned mode, uint32 addr, uint32 word) -> void {
}
}
auto CPU::bus_wait(unsigned mode, uint32 addr) -> unsigned {
auto CPU::busWait(uint mode, uint32 addr) -> uint {
if(addr >= 0x1000'0000) return 1; //unmapped
if(addr < 0x0200'0000) return 1;
if(addr < 0x0300'0000) return (16 - regs.memory.control.ewramwait) * (mode & Word ? 2 : 1);
@@ -65,9 +65,9 @@ auto CPU::bus_wait(unsigned mode, uint32 addr) -> unsigned {
if(addr < 0x0700'0000) return mode & Word ? 2 : 1;
if(addr < 0x0800'0000) return 1;
static unsigned timings[] = {5, 4, 3, 9};
unsigned n = timings[regs.wait.control.nwait[addr >> 25 & 3]];
unsigned s = regs.wait.control.swait[addr >> 25 & 3];
static uint timings[] = {5, 4, 3, 9};
uint n = timings[regs.wait.control.nwait[addr >> 25 & 3]];
uint s = regs.wait.control.swait[addr >> 25 & 3];
switch(addr & 0x0e00'0000) {
case 0x0800'0000: s = s ? 2 : 3; break;
@@ -79,7 +79,7 @@ auto CPU::bus_wait(unsigned mode, uint32 addr) -> unsigned {
bool sequential = (mode & Sequential);
if((addr & 0x1fffe) == 0) sequential = false; //N cycle on 16-bit ROM crossing 128KB page boundary (RAM S==N)
unsigned clocks = sequential ? s : n;
uint clocks = sequential ? s : n;
if(mode & Word) clocks += s; //16-bit bus requires two transfers for words
return clocks;
}

View File

@@ -91,12 +91,15 @@ auto CPU::sync_step(uint clocks) -> void {
}
auto CPU::keypad_run() -> void {
if(regs.keypad.control.enable == false) return;
//lookup table to convert button indexes to Emulator::Interface indexes
static const uint lookup[] = {5, 4, 8, 9, 3, 2, 0, 1, 7, 6};
if(!regs.keypad.control.enable) return;
bool test = regs.keypad.control.condition; //0 = OR, 1 = AND
for(auto n : range(10)) {
if(regs.keypad.control.flag[n] == false) continue;
bool input = interface->inputPoll(0, 0, n);
if(!regs.keypad.control.flag[n]) continue;
bool input = interface->inputPoll(0, 0, lookup[n]);
if(regs.keypad.control.condition == 0) test |= input;
if(regs.keypad.control.condition == 1) test &= input;
}

View File

@@ -39,10 +39,10 @@ struct CPU : Processor::ARM, Thread, MMIO {
auto power() -> void;
//bus.cpp
auto bus_idle() -> void override;
auto bus_read(uint mode, uint32 addr) -> uint32 override;
auto bus_write(uint mode, uint32 addr, uint32 word) -> void override;
auto bus_wait(uint mode, uint32 addr) -> uint;
auto busIdle() -> void override;
auto busRead(uint mode, uint32 addr) -> uint32 override;
auto busWrite(uint mode, uint32 addr, uint32 word) -> void override;
auto busWait(uint mode, uint32 addr) -> uint;
//mmio.cpp
auto read(uint32 addr) -> uint8;

View File

@@ -39,7 +39,7 @@ auto CPU::dma_exec(Registers::DMA& dma) -> void {
uint32 addr = dma.run.source;
if(mode & Word) addr &= ~3;
if(mode & Half) addr &= ~1;
dma.data = bus_read(mode, addr);
dma.data = busRead(mode, addr);
}
if(dma.run.target < 0x0200'0000) {
@@ -48,7 +48,7 @@ auto CPU::dma_exec(Registers::DMA& dma) -> void {
uint32 addr = dma.run.target;
if(mode & Word) addr &= ~3;
if(mode & Half) addr &= ~1;
bus_write(mode, addr, dma.data);
busWrite(mode, addr, dma.data);
}
switch(dma.control.sourcemode) {

View File

@@ -61,9 +61,10 @@ auto CPU::read(uint32 addr) -> uint8 {
//KEYINPUT
case 0x04000130: {
static const uint lookup[] = {5, 4, 8, 9, 3, 2, 0, 1};
if(auto result = player.keyinput()) return result() >> 0;
uint8 result = 0;
for(uint n = 0; n < 8; n++) result |= interface->inputPoll(0, 0, n) << n;
for(uint n = 0; n < 8; n++) result |= interface->inputPoll(0, 0, lookup[n]) << n;
if((result & 0xc0) == 0xc0) result &= (uint8)~0xc0; //up+down cannot be pressed simultaneously
if((result & 0x30) == 0x30) result &= (uint8)~0x30; //left+right cannot be pressed simultaneously
return result ^ 0xff;
@@ -71,8 +72,8 @@ auto CPU::read(uint32 addr) -> uint8 {
case 0x04000131: {
if(auto result = player.keyinput()) return result() >> 8;
uint8 result = 0;
result |= interface->inputPoll(0, 0, 8) << 0;
result |= interface->inputPoll(0, 0, 9) << 1;
result |= interface->inputPoll(0, 0, 7) << 0;
result |= interface->inputPoll(0, 0, 6) << 1;
return result ^ 0x03;
}

View File

@@ -3,7 +3,7 @@ auto CPU::prefetch_sync(uint32 addr) -> void {
prefetch.addr = addr;
prefetch.load = addr;
prefetch.wait = bus_wait(Half | Nonsequential, prefetch.load);
prefetch.wait = busWait(Half | Nonsequential, prefetch.load);
}
auto CPU::prefetch_step(uint clocks) -> void {
@@ -14,7 +14,7 @@ auto CPU::prefetch_step(uint clocks) -> void {
if(--prefetch.wait) continue;
prefetch.slot[prefetch.load >> 1 & 7] = cartridge.read(Half, prefetch.load);
prefetch.load += 2;
prefetch.wait = bus_wait(Half | Sequential, prefetch.load);
prefetch.wait = busWait(Half | Sequential, prefetch.load);
}
}
@@ -22,14 +22,14 @@ auto CPU::prefetch_wait() -> void {
if(!regs.wait.control.prefetch || active.dma || prefetch.full()) return;
prefetch_step(prefetch.wait);
prefetch.wait = bus_wait(Half | Nonsequential, prefetch.load);
prefetch.wait = busWait(Half | Nonsequential, prefetch.load);
}
auto CPU::prefetch_read() -> uint16 {
if(prefetch.empty()) prefetch_step(prefetch.wait);
else prefetch_step(1);
if(prefetch.full()) prefetch.wait = bus_wait(Half | Sequential, prefetch.load);
if(prefetch.full()) prefetch.wait = busWait(Half | Sequential, prefetch.load);
uint16 half = prefetch.slot[prefetch.addr >> 1 & 7];
prefetch.addr += 2;

View File

@@ -1,22 +1,17 @@
#pragma once
//license: GPLv3
//started: 2012-03-19
#include <emulator/emulator.hpp>
#include <processor/arm/arm.hpp>
namespace GameBoyAdvance {
namespace Info {
static const string Name = "bgba";
static const uint SerializerVersion = 3;
static const uint SerializerVersion = 4;
}
}
/*
bgba - Game Boy Advance emulator
authors: byuu, Cydrak
license: GPLv3
project started: 2012-03-19
*/
#include <libco/libco.h>
namespace GameBoyAdvance {
@@ -39,7 +34,7 @@ namespace GameBoyAdvance {
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
if(thread) co_delete(thread);
thread = co_create(65536 * sizeof(void*), entrypoint);
thread = co_create(65'536 * sizeof(void*), entrypoint);
this->frequency = frequency;
clock = 0;
}

View File

@@ -22,22 +22,21 @@ Interface::Interface() {
media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba", true});
{ Device device{0, ID::Device, "Controller"};
device.input.append({ 0, 0, "A" });
device.input.append({ 1, 0, "B" });
device.input.append({ 2, 0, "Select"});
device.input.append({ 3, 0, "Start" });
device.input.append({ 4, 0, "Right" });
device.input.append({ 5, 0, "Left" });
device.input.append({ 6, 0, "Up" });
device.input.append({ 7, 0, "Down" });
device.input.append({ 8, 0, "R" });
device.input.append({ 9, 0, "L" });
device.input.append({10, 2, "Rumble"});
device.order = {6, 7, 5, 4, 1, 0, 9, 8, 2, 3, 10};
this->device.append(device);
device.inputs.append({ 0, 0, "Up" });
device.inputs.append({ 1, 0, "Down" });
device.inputs.append({ 2, 0, "Left" });
device.inputs.append({ 3, 0, "Right" });
device.inputs.append({ 4, 0, "B" });
device.inputs.append({ 5, 0, "A" });
device.inputs.append({ 6, 0, "L" });
device.inputs.append({ 7, 0, "R" });
device.inputs.append({ 8, 0, "Select"});
device.inputs.append({ 9, 0, "Start" });
device.inputs.append({10, 2, "Rumble"});
devices.append(device);
}
port.append({0, "Device", {device[0]}});
ports.append({0, "Device", {devices[0]}});
}
auto Interface::manifest() -> string {
@@ -52,6 +51,32 @@ auto Interface::videoFrequency() -> double {
return 16777216.0 / (228.0 * 1232.0);
}
auto Interface::videoColors() -> uint32 {
return 1 << 15;
}
auto Interface::videoColor(uint32 color) -> uint64 {
uint R = color.bits( 0, 4);
uint G = color.bits( 5, 9);
uint B = color.bits(10,14);
uint64 r = image::normalize(R, 5, 16);
uint64 g = image::normalize(G, 5, 16);
uint64 b = image::normalize(B, 5, 16);
if(settings.colorEmulation) {
double lcdGamma = 4.0, outGamma = 2.2;
double lb = pow(B / 31.0, lcdGamma);
double lg = pow(G / 31.0, lcdGamma);
double lr = pow(R / 31.0, lcdGamma);
r = pow(( 0 * lb + 50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
g = pow(( 30 * lb + 230 * lg + 10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
b = pow((220 * lb + 10 * lg + 50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
}
return r << 32 | g << 16 | b << 0;
}
auto Interface::audioFrequency() -> double {
return 16777216.0 / 512.0;
}
@@ -169,8 +194,18 @@ auto Interface::get(const string& name) -> any {
}
auto Interface::set(const string& name, const any& value) -> bool {
if(name == "Blur Emulation" && value.is<bool>()) return settings.blurEmulation = value.get<bool>(), true;
if(name == "Color Emulation" && value.is<bool>()) return settings.colorEmulation = value.get<bool>(), true;
if(name == "Blur Emulation" && value.is<bool>()) {
settings.blurEmulation = value.get<bool>();
system.configureVideoEffects();
return true;
}
if(name == "Color Emulation" && value.is<bool>()) {
settings.colorEmulation = value.get<bool>();
system.configureVideoPalette();
return true;
}
return false;
}

View File

@@ -28,6 +28,8 @@ struct Interface : Emulator::Interface {
auto manifest() -> string;
auto title() -> string;
auto videoFrequency() -> double;
auto videoColors() -> uint32;
auto videoColor(uint32 color) -> uint64;
auto audioFrequency() -> double;
auto loaded() -> bool;
@@ -50,7 +52,7 @@ struct Interface : Emulator::Interface {
auto set(const string& name, const any& value) -> bool override;
private:
vector<Device> device;
vector<Device> devices;
};
struct Settings {

View File

@@ -13,8 +13,6 @@
namespace GameBoyAdvance {
PPU ppu;
#include "video.cpp"
#include "background.cpp"
#include "object.cpp"
#include "mosaic.cpp"
@@ -180,8 +178,11 @@ auto PPU::scanline() -> void {
auto PPU::frame() -> void {
player.frame();
video.refresh();
scheduler.exit(Scheduler::Event::Frame);
}
auto PPU::refresh() -> void {
Emulator::video.refresh(output, 240 * sizeof(uint32), 240, 160);
}
}

View File

@@ -1,5 +1,3 @@
#include "video.hpp"
struct PPU : Thread, MMIO {
#include "registers.hpp"
#include "state.hpp"
@@ -14,6 +12,7 @@ struct PPU : Thread, MMIO {
auto power() -> void;
auto scanline() -> void;
auto frame() -> void;
auto refresh() -> void;
auto read(uint32 addr) -> uint8;
auto write(uint32 addr, uint8 byte) -> void;

View File

@@ -1,59 +0,0 @@
Video video;
Video::Video() {
output = new uint32[240 * 160];
paletteLiteral = new uint32[1 << 15];
paletteStandard = new uint32[1 << 15];
paletteEmulation = new uint32[1 << 15];
}
auto Video::power() -> void {
memory::fill(output(), 240 * 160 * sizeof(uint32));
for(auto color : range(1 << 15)) {
paletteLiteral[color] = color;
uint B = (uint5)(color >> 10);
uint G = (uint5)(color >> 5);
uint R = (uint5)(color >> 0);
uint b = image::normalize(B, 5, 16);
uint g = image::normalize(G, 5, 16);
uint r = image::normalize(R, 5, 16);
paletteStandard[color] = interface->videoColor(r, g, b);
double lcdGamma = 4.0, outGamma = 2.2;
double lb = pow(B / 31.0, lcdGamma);
double lg = pow(G / 31.0, lcdGamma);
double lr = pow(R / 31.0, lcdGamma);
b = pow((220 * lb + 10 * lg + 50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
g = pow(( 30 * lb + 230 * lg + 10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
r = pow(( 0 * lb + 50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
paletteEmulation[color] = interface->videoColor(r, g, b);
}
}
auto Video::refresh() -> void {
auto output = this->output();
auto& palette = settings.colorEmulation ? paletteEmulation : paletteStandard;
for(uint y = 0; y < 160; y++) {
auto source = ppu.output + y * 240;
auto target = output + y * 240;
if(settings.blurEmulation) {
for(uint x = 0; x < 240; x++) {
auto a = palette[*source++];
auto b = *target;
*target++ = (a + b - ((a ^ b) & 0x01010101)) >> 1;
}
} else {
for(uint x = 0; x < 240; x++) {
auto color = palette[*source++];
*target++ = color;
}
}
}
interface->videoRefresh(output, 240 * sizeof(uint32), 240, 160);
}

View File

@@ -1,13 +0,0 @@
struct Video {
Video();
auto power() -> void;
auto refresh() -> void;
unique_pointer<uint32[]> output;
unique_pointer<uint32[]> paletteLiteral;
unique_pointer<uint32[]> paletteStandard;
unique_pointer<uint32[]> paletteEmulation;
};
extern Video video;

View File

@@ -13,6 +13,7 @@ auto Scheduler::enter(Mode mode_) -> Event {
mode = mode_;
host = co_active();
co_switch(resume);
if(event == Event::Frame) ppu.refresh();
return event;
}

View File

@@ -3,6 +3,7 @@
namespace GameBoyAdvance {
#include "bios.cpp"
#include "video.cpp"
#include "serialization.cpp"
BIOS bios;
System system;
@@ -16,13 +17,20 @@ auto System::term() -> void {
}
auto System::power() -> void {
Emulator::video.reset();
Emulator::video.setInterface(interface);
configureVideoPalette();
configureVideoEffects();
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
bus.power();
player.power();
cpu.power();
ppu.power();
apu.power();
cartridge.power();
video.power();
scheduler.power();
}

View File

@@ -25,6 +25,11 @@ struct System {
auto run() -> void;
auto runToSave() -> void;
//video.cpp
auto configureVideoPalette() -> void;
auto configureVideoEffects() -> void;
//serialization.cpp
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;

View File

@@ -0,0 +1,7 @@
auto System::configureVideoPalette() -> void {
Emulator::video.setPalette();
}
auto System::configureVideoEffects() -> void {
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
}

View File

@@ -31,16 +31,16 @@ auto ARM::exec() -> void {
auto ARM::idle() -> void {
pipeline.nonsequential = true;
return bus_idle();
return busIdle();
}
auto ARM::read(unsigned mode, uint32 addr) -> uint32 {
return bus_read(mode, addr);
return busRead(mode, addr);
}
auto ARM::load(unsigned mode, uint32 addr) -> uint32 {
pipeline.nonsequential = true;
uint32 word = bus_read(Load | mode, addr);
uint32 word = busRead(Load | mode, addr);
if(mode & Half) {
addr &= 1;
@@ -64,7 +64,7 @@ auto ARM::load(unsigned mode, uint32 addr) -> uint32 {
auto ARM::write(unsigned mode, uint32 addr, uint32 word) -> void {
pipeline.nonsequential = true;
return bus_write(mode, addr, word);
return busWrite(mode, addr, word);
}
auto ARM::store(unsigned mode, uint32 addr, uint32 word) -> void {
@@ -73,7 +73,7 @@ auto ARM::store(unsigned mode, uint32 addr, uint32 word) -> void {
if(mode & Half) { word &= 0xffff; word |= word << 16; }
if(mode & Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
return bus_write(Store | mode, addr, word);
return busWrite(Store | mode, addr, word);
}
auto ARM::vector(uint32 addr, Processor::Mode mode) -> void {

View File

@@ -25,9 +25,9 @@ struct ARM {
#include "disassembler.hpp"
virtual auto step(unsigned clocks) -> void = 0;
virtual auto bus_idle() -> void = 0;
virtual auto bus_read(unsigned mode, uint32 addr) -> uint32 = 0;
virtual auto bus_write(unsigned mode, uint32 addr, uint32 word) -> void = 0;
virtual auto busIdle() -> void = 0;
virtual auto busRead(unsigned mode, uint32 addr) -> uint32 = 0;
virtual auto busWrite(unsigned mode, uint32 addr, uint32 word) -> void = 0;
//arm.cpp
auto power() -> void;

View File

@@ -406,7 +406,7 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
output.append(load ? "ldm" : "stm", conditions[condition], indices[index], " ");
output.append(registers[rn], writeback ? "!" : "", ",{");
for(unsigned n = 0; n < 16; n++) if(list & (1 << n)) output.append(registers[n], ",");
output.rtrim(",");
output.trimRight(",", 1L);
output.append("}", s ? "^" : "");
return output;
@@ -679,7 +679,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
if(list & (1 << l)) output.append(registers[l], ",");
}
if(branch) output.append(load == 0 ? "lr," : "pc,");
output.rtrim(",");
output.trimRight(",", 1L);
output.append("}");
return output;
@@ -696,7 +696,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
for(unsigned l = 0; l < 8; l++) {
if(list & (1 << l)) output.append(registers[l], ",");
}
output.rtrim(",");
output.trimRight(",", 1L);
output.append("}");
return output;

View File

@@ -27,7 +27,7 @@ auto ARM::arm_opcode(uint32 rm) {
if(exceptionMode() && d == 15 && save) {
cpsr() = spsr();
processor.setMode((Processor::Mode)cpsr().m);
processor.setMode((Processor::Mode)(uint)cpsr().m);
}
}
@@ -48,7 +48,7 @@ auto ARM::arm_move_to_status(uint32 rm) {
psr.f = rm & 0x00000040;
psr.t = rm & 0x00000020;
psr.m = rm & 0x0000001f;
if(source == 0) processor.setMode((Processor::Mode)psr.m);
if(source == 0) processor.setMode((Processor::Mode)(uint)psr.m);
}
}
@@ -548,7 +548,7 @@ auto ARM::arm_op_move_multiple() {
if(s && (list & 0x8000)) {
if(mode() != Processor::Mode::USR && mode() != Processor::Mode::SYS) {
cpsr() = spsr();
processor.setMode((Processor::Mode)cpsr().m);
processor.setMode((Processor::Mode)(uint)cpsr().m);
}
}
} else {

View File

@@ -19,33 +19,24 @@ struct GPR {
};
struct PSR {
inline operator uint32_t() const {
return (n << 31) + (z << 30) + (c << 29) + (v << 28)
+ (i << 7) + (f << 6) + (t << 5) + (m << 0);
}
union {
uint32_t data = 0;
BitField<uint32_t, 31> n; //negative
BitField<uint32_t, 30> z; //zero
BitField<uint32_t, 29> c; //carry
BitField<uint32_t, 28> v; //overflow
BitField<uint32_t, 7> i; //irq
BitField<uint32_t, 6> f; //fiq
BitField<uint32_t, 5> t; //thumb
BitField<uint32_t, 4, 0> m; //mode
};
inline auto operator=(uint32_t d) {
n = d & (1 << 31);
z = d & (1 << 30);
c = d & (1 << 29);
v = d & (1 << 28);
i = d & (1 << 7);
f = d & (1 << 6);
t = d & (1 << 5);
m = d & 31;
return *this;
}
PSR() = default;
PSR(const PSR& value) { data = value.data; }
auto serialize(serializer&) -> void;
bool n = false; //negative
bool z = false; //zero
bool c = false; //carry
bool v = false; //overflow
bool i = false; //irq
bool f = false; //fiq
bool t = false; //thumb
uint m = 0; //mode
inline operator uint() const { return data & 0xf00000ff; }
inline auto& operator=(uint value) { return data = value, *this; }
inline auto& operator=(const PSR& value) { return data = value.data, *this; }
};
struct Pipeline {
@@ -125,6 +116,6 @@ alwaysinline auto cpsr() -> PSR& { return processor.cpsr; }
alwaysinline auto spsr() -> PSR& { return *processor.spsr; }
alwaysinline auto carryout() -> bool& { return processor.carryout; }
alwaysinline auto instruction() -> uint32 { return pipeline.execute.instruction; }
alwaysinline auto mode() -> Processor::Mode { return (Processor::Mode)processor.cpsr.m; }
alwaysinline auto privilegedMode() const -> bool { return (Processor::Mode)processor.cpsr.m != Processor::Mode::USR; }
alwaysinline auto exceptionMode() const -> bool { return privilegedMode() && (Processor::Mode)processor.cpsr.m != Processor::Mode::SYS; }
alwaysinline auto mode() -> Processor::Mode { return (Processor::Mode)(uint)processor.cpsr.m; }
alwaysinline auto privilegedMode() const -> bool { return (Processor::Mode)(uint)processor.cpsr.m != Processor::Mode::USR; }
alwaysinline auto exceptionMode() const -> bool { return privilegedMode() && (Processor::Mode)(uint)processor.cpsr.m != Processor::Mode::SYS; }

View File

@@ -1,14 +1,3 @@
auto ARM::PSR::serialize(serializer& s) -> void {
s.integer(n);
s.integer(z);
s.integer(c);
s.integer(v);
s.integer(i);
s.integer(f);
s.integer(t);
s.integer(m);
}
auto ARM::serialize(serializer& s) -> void {
s.integer(processor.r0.data);
s.integer(processor.r1.data);
@@ -34,26 +23,26 @@ auto ARM::serialize(serializer& s) -> void {
s.integer(processor.fiq.r12.data);
s.integer(processor.fiq.sp.data);
s.integer(processor.fiq.lr.data);
processor.fiq.spsr.serialize(s);
s.integer(processor.fiq.spsr.data);
s.integer(processor.irq.sp.data);
s.integer(processor.irq.lr.data);
processor.irq.spsr.serialize(s);
s.integer(processor.irq.spsr.data);
s.integer(processor.svc.sp.data);
s.integer(processor.svc.lr.data);
processor.svc.spsr.serialize(s);
s.integer(processor.svc.spsr.data);
s.integer(processor.abt.sp.data);
s.integer(processor.abt.lr.data);
processor.abt.spsr.serialize(s);
s.integer(processor.abt.spsr.data);
s.integer(processor.und.sp.data);
s.integer(processor.und.lr.data);
processor.und.spsr.serialize(s);
s.integer(processor.und.spsr.data);
s.integer(processor.pc.data);
processor.cpsr.serialize(s);
s.integer(processor.cpsr.data);
s.integer(processor.carryout);
s.integer(processor.irqline);
@@ -68,5 +57,5 @@ auto ARM::serialize(serializer& s) -> void {
s.integer(crash);
processor.setMode((Processor::Mode)cpsr().m);
processor.setMode((Processor::Mode)(uint)cpsr().m);
}

View File

@@ -1,21 +1,14 @@
auto GSU::disassemble_opcode(char* output) -> void {
auto GSU::disassembleOpcode(char* output) -> void {
*output = 0;
if(!regs.sfr.alt2) {
if(!regs.sfr.alt1) {
disassemble_alt0(output);
} else {
disassemble_alt1(output);
}
} else {
if(!regs.sfr.alt1) {
disassemble_alt2(output);
} else {
disassemble_alt3(output);
}
switch(regs.sfr.alt2 << 1 | regs.sfr.alt1 << 0) {
case 0: disassembleAlt0(output); break;
case 1: disassembleAlt1(output); break;
case 2: disassembleAlt2(output); break;
case 3: disassembleAlt3(output); break;
}
unsigned length = strlen(output);
uint length = strlen(output);
while(length++ < 20) strcat(output, " ");
}
@@ -34,10 +27,10 @@ auto GSU::disassemble_opcode(char* output) -> void {
case id+ 8: case id+ 9: case id+10: case id+11: case id+12: case id+13: case id+14: case id+15
#define op0 regs.pipeline
#define op1 bus_read((regs.pbr << 16) + regs.r[15] + 0)
#define op2 bus_read((regs.pbr << 16) + regs.r[15] + 1)
#define op1 read((regs.pbr << 16) + regs.r[15] + 0)
#define op2 read((regs.pbr << 16) + regs.r[15] + 1)
auto GSU::disassemble_alt0(char* output) -> void {
auto GSU::disassembleAlt0(char* output) -> void {
char t[256] = "";
switch(op0) {
case (0x00): sprintf(t, "stop"); break;
@@ -94,7 +87,7 @@ auto GSU::disassemble_alt0(char* output) -> void {
strcat(output, t);
}
auto GSU::disassemble_alt1(char* output) -> void {
auto GSU::disassembleAlt1(char* output) -> void {
char t[256] = "";
switch(op0) {
case (0x00): sprintf(t, "stop"); break;
@@ -151,7 +144,7 @@ auto GSU::disassemble_alt1(char* output) -> void {
strcat(output, t);
}
auto GSU::disassemble_alt2(char* output) -> void {
auto GSU::disassembleAlt2(char* output) -> void {
char t[256] = "";
switch(op0) {
case (0x00): sprintf(t, "stop"); break;
@@ -208,7 +201,7 @@ auto GSU::disassemble_alt2(char* output) -> void {
strcat(output, t);
}
auto GSU::disassemble_alt3(char* output) -> void {
auto GSU::disassembleAlt3(char* output) -> void {
char t[256] = "";
switch(op0) {
case (0x00): sprintf(t, "stop"); break;

View File

@@ -8,7 +8,7 @@
namespace Processor {
#include "instructions.cpp"
#include "table.cpp"
#include "switch.cpp"
#include "serialization.cpp"
#include "disassembler.cpp"
@@ -16,7 +16,11 @@ auto GSU::power() -> void {
}
auto GSU::reset() -> void {
for(auto& r : regs.r) r = 0x0000;
for(auto& r : regs.r) {
r.data = 0x0000;
r.modified = false;
}
regs.sfr = 0x0000;
regs.pbr = 0x00;
regs.rombr = 0x00;

View File

@@ -5,7 +5,7 @@ namespace Processor {
struct GSU {
#include "registers.hpp"
virtual auto step(unsigned clocks) -> void = 0;
virtual auto step(uint clocks) -> void = 0;
virtual auto stop() -> void = 0;
virtual auto color(uint8 source) -> uint8 = 0;
@@ -13,116 +13,74 @@ struct GSU {
virtual auto rpix(uint8 x, uint8 y) -> uint8 = 0;
virtual auto pipe() -> uint8 = 0;
virtual auto rombuffer_sync() -> void = 0;
virtual auto rombuffer_read() -> uint8 = 0;
virtual auto rambuffer_sync() -> void = 0;
virtual auto rambuffer_read(uint16 addr) -> uint8 = 0;
virtual auto rambuffer_write(uint16 addr, uint8 data) -> void = 0;
virtual auto cache_flush() -> void = 0;
virtual auto syncROMBuffer() -> void = 0;
virtual auto readROMBuffer() -> uint8 = 0;
virtual auto syncRAMBuffer() -> void = 0;
virtual auto readRAMBuffer(uint16 addr) -> uint8 = 0;
virtual auto writeRAMBuffer(uint16 addr, uint8 data) -> void = 0;
virtual auto flushCache() -> void = 0;
virtual auto bus_read(uint24 addr, uint8 data = 0x00) -> uint8 = 0;
virtual auto bus_write(uint24 addr, uint8 data) -> void = 0;
virtual auto read(uint24 addr, uint8 data = 0x00) -> uint8 = 0;
virtual auto write(uint24 addr, uint8 data) -> void = 0;
//gsu.cpp
auto power() -> void;
auto reset() -> void;
//instructions.cpp
template<int> auto op_adc_i();
template<int> auto op_adc_r();
template<int> auto op_add_i();
template<int> auto op_add_r();
auto op_add_adc(uint n);
auto op_alt1();
auto op_alt2();
auto op_alt3();
template<int> auto op_and_i();
template<int> auto op_and_r();
auto op_asr();
auto op_bge();
auto op_bcc();
auto op_bcs();
auto op_beq();
template<int> auto op_bic_i();
template<int> auto op_bic_r();
auto op_blt();
auto op_bmi();
auto op_bne();
auto op_bpl();
auto op_bra();
auto op_bvc();
auto op_bvs();
auto op_and_bic(uint n);
auto op_asr_div2();
auto op_branch(bool c);
auto op_cache();
auto op_cmode();
template<int> auto op_cmp_r();
auto op_color();
template<int> auto op_dec_r();
auto op_div2();
auto op_fmult();
template<int> auto op_from_r();
auto op_color_cmode();
auto op_dec(uint n);
auto op_fmult_lmult();
auto op_from_moves(uint n);
auto op_getb();
auto op_getbl();
auto op_getbh();
auto op_getbs();
auto op_getc();
auto op_getc_ramb_romb();
auto op_hib();
template<int> auto op_ibt_r();
template<int> auto op_inc_r();
template<int> auto op_iwt_r();
template<int> auto op_jmp_r();
template<int> auto op_ldb_ir();
template<int> auto op_ldw_ir();
template<int> auto op_link();
template<int> auto op_ljmp_r();
template<int> auto op_lm_r();
template<int> auto op_lms_r();
auto op_lmult();
auto op_ibt_lms_sms(uint n);
auto op_inc(uint n);
auto op_iwt_lm_sm(uint n);
auto op_jmp_ljmp(uint n);
auto op_link(uint n);
auto op_load(uint n);
auto op_lob();
auto op_loop();
auto op_lsr();
auto op_merge();
template<int> auto op_mult_i();
template<int> auto op_mult_r();
auto op_mult_umult(uint n);
auto op_nop();
auto op_not();
template<int> auto op_or_i();
template<int> auto op_or_r();
auto op_plot();
auto op_ramb();
auto op_or_xor(uint n);
auto op_plot_rpix();
auto op_rol();
auto op_romb();
auto op_ror();
auto op_rpix();
template<int> auto op_sbc_r();
auto op_sbk();
auto op_sex();
template<int> auto op_sm_r();
template<int> auto op_sms_r();
template<int> auto op_stb_ir();
auto op_store(uint n);
auto op_stop();
template<int> auto op_stw_ir();
template<int> auto op_sub_i();
template<int> auto op_sub_r();
auto op_sub_sbc_cmp(uint n);
auto op_swap();
template<int> auto op_to_r();
template<int> auto op_umult_i();
template<int> auto op_umult_r();
template<int> auto op_with_r();
template<int> auto op_xor_i();
template<int> auto op_xor_r();
auto op_to_move(uint n);
auto op_with(uint n);
//table.cpp
auto (GSU::*opcode_table[1024])() -> void;
auto initialize_opcode_table() -> void;
//switch.cpp
auto instruction(uint8 opcode) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
//disassembler.cpp
auto disassemble_opcode(char* output) -> void;
auto disassemble_alt0(char* output) -> void;
auto disassemble_alt1(char* output) -> void;
auto disassemble_alt2(char* output) -> void;
auto disassemble_alt3(char* output) -> void;
auto disassembleOpcode(char* output) -> void;
auto disassembleAlt0(char* output) -> void;
auto disassembleAlt1(char* output) -> void;
auto disassembleAlt2(char* output) -> void;
auto disassembleAlt3(char* output) -> void;
};
}

View File

@@ -4,9 +4,8 @@ auto GSU::op_stop() {
regs.sfr.irq = 1;
stop();
}
regs.sfr.g = 0;
regs.pipeline = 0x01;
regs.pipeline = 0x01; //nop
regs.reset();
}
@@ -19,7 +18,7 @@ auto GSU::op_nop() {
auto GSU::op_cache() {
if(regs.cbr != (regs.r[15] & 0xfff0)) {
regs.cbr = regs.r[15] & 0xfff0;
cache_flush();
flushCache();
}
regs.reset();
}
@@ -44,75 +43,25 @@ auto GSU::op_rol() {
}
//$05 bra e
auto GSU::op_bra() {
regs.r[15] += (int8)pipe();
}
//$06 blt e
auto GSU::op_blt() {
int e = (int8)pipe();
if((regs.sfr.s ^ regs.sfr.ov) == 0) regs.r[15] += e;
}
//$07 bge e
auto GSU::op_bge() {
int e = (int8)pipe();
if((regs.sfr.s ^ regs.sfr.ov) == 1) regs.r[15] += e;
}
//$08 bne e
auto GSU::op_bne() {
int e = (int8)pipe();
if(regs.sfr.z == 0) regs.r[15] += e;
}
//$09 beq e
auto GSU::op_beq() {
int e = (int8)pipe();
if(regs.sfr.z == 1) regs.r[15] += e;
}
//$0a bpl e
auto GSU::op_bpl() {
int e = (int8)pipe();
if(regs.sfr.s == 0) regs.r[15] += e;
}
//$0b bmi e
auto GSU::op_bmi() {
int e = (int8)pipe();
if(regs.sfr.s == 1) regs.r[15] += e;
}
//$0c bcc e
auto GSU::op_bcc() {
int e = (int8)pipe();
if(regs.sfr.cy == 0) regs.r[15] += e;
}
//$0d bcs e
auto GSU::op_bcs() {
int e = (int8)pipe();
if(regs.sfr.cy == 1) regs.r[15] += e;
}
//$0e bvc e
auto GSU::op_bvc() {
int e = (int8)pipe();
if(regs.sfr.ov == 0) regs.r[15] += e;
}
//$0f bvs e
auto GSU::op_bvs() {
int e = (int8)pipe();
if(regs.sfr.ov == 1) regs.r[15] += e;
auto GSU::op_branch(bool c) {
auto d = (int8)pipe();
if(c) regs.r[15] += d;
}
//$10-1f(b0): to rN
//$10-1f(b1): move rN
template<int n>
auto GSU::op_to_r() {
if(regs.sfr.b == 0) {
//$10-1f(b0) to rN
//$10-1f(b1) move rN
auto GSU::op_to_move(uint n) {
if(!regs.sfr.b) {
regs.dreg = n;
} else {
regs.r[n] = regs.sr();
@@ -120,28 +69,19 @@ auto GSU::op_to_r() {
}
}
//$20-2f: with rN
template<int n>
auto GSU::op_with_r() {
//$20-2f with rN
auto GSU::op_with(uint n) {
regs.sreg = n;
regs.dreg = n;
regs.sfr.b = 1;
}
//$30-3b(alt0): stw (rN)
template<int n>
auto GSU::op_stw_ir() {
//$30-3b(alt0) stw (rN)
//$30-3b(alt1) stb (rN)
auto GSU::op_store(uint n) {
regs.ramaddr = regs.r[n];
rambuffer_write(regs.ramaddr ^ 0, regs.sr() >> 0);
rambuffer_write(regs.ramaddr ^ 1, regs.sr() >> 8);
regs.reset();
}
//$30-3b(alt1): stb (rN)
template<int n>
auto GSU::op_stb_ir() {
regs.ramaddr = regs.r[n];
rambuffer_write(regs.ramaddr, regs.sr());
writeRAMBuffer(regs.ramaddr, regs.sr());
if(!regs.sfr.alt1) writeRAMBuffer(regs.ramaddr ^ 1, regs.sr() >> 8);
regs.reset();
}
@@ -173,41 +113,30 @@ auto GSU::op_alt3() {
regs.sfr.alt2 = 1;
}
//$40-4b(alt0): ldw (rN)
template<int n>
auto GSU::op_ldw_ir() {
//$40-4b(alt0) ldw (rN)
//$40-4b(alt1) ldb (rN)
auto GSU::op_load(uint n) {
regs.ramaddr = regs.r[n];
uint16_t data;
data = rambuffer_read(regs.ramaddr ^ 0) << 0;
data |= rambuffer_read(regs.ramaddr ^ 1) << 8;
regs.dr() = data;
regs.dr() = readRAMBuffer(regs.ramaddr);
if(!regs.sfr.alt1) regs.dr() |= readRAMBuffer(regs.ramaddr ^ 1) << 8;
regs.reset();
}
//$40-4b(alt1): ldb (rN)
template<int n>
auto GSU::op_ldb_ir() {
regs.ramaddr = regs.r[n];
regs.dr() = rambuffer_read(regs.ramaddr);
//$4c(alt0) plot
//$4c(alt1) rpix
auto GSU::op_plot_rpix() {
if(!regs.sfr.alt1) {
plot(regs.r[1], regs.r[2]);
regs.r[1]++;
} else {
regs.dr() = rpix(regs.r[1], regs.r[2]);
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
}
regs.reset();
}
//$4c(alt0): plot
auto GSU::op_plot() {
plot(regs.r[1], regs.r[2]);
regs.r[1]++;
regs.reset();
}
//$4c(alt1): rpix
auto GSU::op_rpix() {
regs.dr() = rpix(regs.r[1], regs.r[2]);
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$4d: swap
//$4d swap
auto GSU::op_swap() {
regs.dr() = (regs.sr() >> 8) | (regs.sr() << 8);
regs.sfr.s = (regs.dr() & 0x8000);
@@ -215,19 +144,18 @@ auto GSU::op_swap() {
regs.reset();
}
//$4e(alt0): color
auto GSU::op_color() {
regs.colr = color(regs.sr());
//$4e(alt0) color
//$4e(alt1) cmode
auto GSU::op_color_cmode() {
if(!regs.sfr.alt1) {
regs.colr = color(regs.sr());
} else {
regs.por = regs.sr();
}
regs.reset();
}
//$4e(alt1): cmode
auto GSU::op_cmode() {
regs.por = regs.sr();
regs.reset();
}
//$4f: not
//$4f not
auto GSU::op_not() {
regs.dr() = ~regs.sr();
regs.sfr.s = (regs.dr() & 0x8000);
@@ -235,102 +163,37 @@ auto GSU::op_not() {
regs.reset();
}
//$50-5f(alt0): add rN
template<int n>
auto GSU::op_add_r() {
int r = regs.sr() + regs.r[n];
regs.sfr.ov = ~(regs.sr() ^ regs.r[n]) & (regs.r[n] ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0x10000);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$50-5f(alt1): adc rN
template<int n>
auto GSU::op_adc_r() {
int r = regs.sr() + regs.r[n] + regs.sfr.cy;
regs.sfr.ov = ~(regs.sr() ^ regs.r[n]) & (regs.r[n] ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0x10000);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$50-5f(alt2): add #N
template<int n>
auto GSU::op_add_i() {
int r = regs.sr() + n;
//$50-5f(alt0) add rN
//$50-5f(alt1) adc rN
//$50-5f(alt2) add #N
//$50-5f(alt3) adc #N
auto GSU::op_add_adc(uint n) {
if(!regs.sfr.alt2) n = regs.r[n];
int r = regs.sr() + n + (regs.sfr.alt1 ? regs.sfr.cy : 0);
regs.sfr.ov = ~(regs.sr() ^ n) & (n ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0x10000);
regs.sfr.z = ((uint16_t)r == 0);
regs.sfr.z = ((uint16)r == 0);
regs.dr() = r;
regs.reset();
}
//$50-5f(alt3): adc #N
template<int n>
auto GSU::op_adc_i() {
int r = regs.sr() + n + regs.sfr.cy;
regs.sfr.ov = ~(regs.sr() ^ n) & (n ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0x10000);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$60-6f(alt0): sub rN
template<int n>
auto GSU::op_sub_r() {
int r = regs.sr() - regs.r[n];
regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$60-6f(alt1): sbc rN
template<int n>
auto GSU::op_sbc_r() {
int r = regs.sr() - regs.r[n] - !regs.sfr.cy;
regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.reset();
}
//$60-6f(alt2): sub #N
template<int n>
auto GSU::op_sub_i() {
int r = regs.sr() - n;
//$60-6f(alt0) sub rN
//$60-6f(alt1) sbc rN
//$60-6f(alt2) sub #N
//$60-6f(alt3) cmp rN
auto GSU::op_sub_sbc_cmp(uint n) {
if(!regs.sfr.alt2 || regs.sfr.alt1) n = regs.r[n];
int r = regs.sr() - n - (!regs.sfr.alt2 && regs.sfr.alt1 ? !regs.sfr.cy : 0);
regs.sfr.ov = (regs.sr() ^ n) & (regs.sr() ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0);
regs.sfr.z = ((uint16_t)r == 0);
regs.dr() = r;
regs.sfr.z = ((uint16)r == 0);
if(!regs.sfr.alt2 || !regs.sfr.alt1) regs.dr() = r;
regs.reset();
}
//$60-6f(alt3): cmp rN
template<int n>
auto GSU::op_cmp_r() {
int r = regs.sr() - regs.r[n];
regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000;
regs.sfr.s = (r & 0x8000);
regs.sfr.cy = (r >= 0);
regs.sfr.z = ((uint16_t)r == 0);
regs.reset();
}
//$70: merge
//$70 merge
auto GSU::op_merge() {
regs.dr() = (regs.r[7] & 0xff00) | (regs.r[8] >> 8);
regs.sfr.ov = (regs.dr() & 0xc0c0);
@@ -340,97 +203,45 @@ auto GSU::op_merge() {
regs.reset();
}
//$71-7f(alt0): and rN
template<int n>
auto GSU::op_and_r() {
regs.dr() = regs.sr() & regs.r[n];
//$71-7f(alt0) and rN
//$71-7f(alt1) bic rN
//$71-7f(alt2) and #N
//$71-7f(alt3) bic #N
auto GSU::op_and_bic(uint n) {
if(!regs.sfr.alt2) n = regs.r[n];
regs.dr() = regs.sr() & (regs.sfr.alt1 ? ~n : n);
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$71-7f(alt1): bic rN
template<int n>
auto GSU::op_bic_r() {
regs.dr() = regs.sr() & ~regs.r[n];
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$71-7f(alt2): and #N
template<int n>
auto GSU::op_and_i() {
regs.dr() = regs.sr() & n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$71-7f(alt3): bic #N
template<int n>
auto GSU::op_bic_i() {
regs.dr() = regs.sr() & ~n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$80-8f(alt0): mult rN
template<int n>
auto GSU::op_mult_r() {
regs.dr() = (int8)regs.sr() * (int8)regs.r[n];
//$80-8f(alt0) mult rN
//$80-8f(alt1) umult rN
//$80-8f(alt2) mult #N
//$80-8f(alt3) umult #N
auto GSU::op_mult_umult(uint n) {
if(!regs.sfr.alt2) n = regs.r[n];
regs.dr() = (!regs.sfr.alt1 ? ((int8)regs.sr() * (int8)n) : ((uint8)regs.sr() * (uint8)n));
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2);
}
//$80-8f(alt1): umult rN
template<int n>
auto GSU::op_umult_r() {
regs.dr() = (uint8)regs.sr() * (uint8)regs.r[n];
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2);
}
//$80-8f(alt2): mult #N
template<int n>
auto GSU::op_mult_i() {
regs.dr() = (int8)regs.sr() * (int8)n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2);
}
//$80-8f(alt3): umult #N
template<int n>
auto GSU::op_umult_i() {
regs.dr() = (uint8)regs.sr() * (uint8)n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2);
}
//$90: sbk
//$90 sbk
auto GSU::op_sbk() {
rambuffer_write(regs.ramaddr ^ 0, regs.sr() >> 0);
rambuffer_write(regs.ramaddr ^ 1, regs.sr() >> 8);
writeRAMBuffer(regs.ramaddr ^ 0, regs.sr() >> 0);
writeRAMBuffer(regs.ramaddr ^ 1, regs.sr() >> 8);
regs.reset();
}
//$91-94: link #N
template<int n>
auto GSU::op_link() {
//$91-94 link #N
auto GSU::op_link(uint n) {
regs.r[11] = regs.r[15] + n;
regs.reset();
}
//$95: sex
//$95 sex
auto GSU::op_sex() {
regs.dr() = (int8)regs.sr();
regs.sfr.s = (regs.dr() & 0x8000);
@@ -438,25 +249,17 @@ auto GSU::op_sex() {
regs.reset();
}
//$96(alt0): asr
auto GSU::op_asr() {
//$96(alt0) asr
//$96(alt1) div2
auto GSU::op_asr_div2() {
regs.sfr.cy = (regs.sr() & 1);
regs.dr() = (int16_t)regs.sr() >> 1;
regs.dr() = ((int16)regs.sr() >> 1) + (regs.sfr.alt1 ? ((regs.sr() + 1) >> 16) : 0);
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$96(alt1): div2
auto GSU::op_div2() {
regs.sfr.cy = (regs.sr() & 1);
regs.dr() = ((int16_t)regs.sr() >> 1) + ((regs.sr() + 1) >> 16);
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$97: ror
//$97 ror
auto GSU::op_ror() {
bool carry = (regs.sr() & 1);
regs.dr() = (regs.sfr.cy << 15) | (regs.sr() >> 1);
@@ -466,24 +269,21 @@ auto GSU::op_ror() {
regs.reset();
}
//$98-9d(alt0): jmp rN
template<int n>
auto GSU::op_jmp_r() {
regs.r[15] = regs.r[n];
//$98-9d(alt0) jmp rN
//$98-9d(alt1) ljmp rN
auto GSU::op_jmp_ljmp(uint n) {
if(!regs.sfr.alt1) {
regs.r[15] = regs.r[n];
} else {
regs.pbr = regs.r[n] & 0x7f;
regs.r[15] = regs.sr();
regs.cbr = regs.r[15] & 0xfff0;
flushCache();
}
regs.reset();
}
//$98-9d(alt1): ljmp rN
template<int n>
auto GSU::op_ljmp_r() {
regs.pbr = regs.r[n] & 0x7f;
regs.r[15] = regs.sr();
regs.cbr = regs.r[15] & 0xfff0;
cache_flush();
regs.reset();
}
//$9e: lob
//$9e lob
auto GSU::op_lob() {
regs.dr() = regs.sr() & 0xff;
regs.sfr.s = (regs.dr() & 0x80);
@@ -491,9 +291,11 @@ auto GSU::op_lob() {
regs.reset();
}
//$9f(alt0): fmult
auto GSU::op_fmult() {
uint32_t result = (int16_t)regs.sr() * (int16_t)regs.r[6];
//$9f(alt0) fmult
//$9f(alt1) lmult
auto GSU::op_fmult_lmult() {
uint32 result = (int16)regs.sr() * (int16)regs.r[6];
if(regs.sfr.alt1) regs.r[4] = result;
regs.dr() = result >> 16;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.cy = (result & 0x8000);
@@ -502,50 +304,28 @@ auto GSU::op_fmult() {
step((regs.cfgr.ms0 ? 3 : 7) * (regs.clsr ? 1 : 2));
}
//$9f(alt1): lmult
auto GSU::op_lmult() {
uint32_t result = (int16_t)regs.sr() * (int16_t)regs.r[6];
regs.r[4] = result;
regs.dr() = result >> 16;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.cy = (result & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
step((regs.cfgr.ms0 ? 3 : 7) * (regs.clsr ? 1 : 2));
}
//$a0-af(alt0): ibt rN,#pp
template<int n>
auto GSU::op_ibt_r() {
regs.r[n] = (int8)pipe();
//$a0-af(alt0) ibt rN,#pp
//$a0-af(alt1) lms rN,(yy)
//$a0-af(alt2) sms (yy),rN
auto GSU::op_ibt_lms_sms(uint n) {
if(regs.sfr.alt1) {
regs.ramaddr = pipe() << 1;
uint8 lo = readRAMBuffer(regs.ramaddr ^ 0) << 0;
regs.r[n] = readRAMBuffer(regs.ramaddr ^ 1) << 8 | lo;
} else if(regs.sfr.alt2) {
regs.ramaddr = pipe() << 1;
writeRAMBuffer(regs.ramaddr ^ 0, regs.r[n] >> 0);
writeRAMBuffer(regs.ramaddr ^ 1, regs.r[n] >> 8);
} else {
regs.r[n] = (int8)pipe();
}
regs.reset();
}
//$a0-af(alt1): lms rN,(yy)
template<int n>
auto GSU::op_lms_r() {
regs.ramaddr = pipe() << 1;
uint16_t data;
data = rambuffer_read(regs.ramaddr ^ 0) << 0;
data |= rambuffer_read(regs.ramaddr ^ 1) << 8;
regs.r[n] = data;
regs.reset();
}
//$a0-af(alt2): sms (yy),rN
template<int n>
auto GSU::op_sms_r() {
regs.ramaddr = pipe() << 1;
rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0);
rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8);
regs.reset();
}
//$b0-bf(b0): from rN
//$b0-bf(b1): moves rN
template<int n>
auto GSU::op_from_r() {
if(regs.sfr.b == 0) {
//$b0-bf(b0) from rN
//$b0-bf(b1) moves rN
auto GSU::op_from_moves(uint n) {
if(!regs.sfr.b) {
regs.sreg = n;
} else {
regs.dr() = regs.r[n];
@@ -556,7 +336,7 @@ auto GSU::op_from_r() {
}
}
//$c0: hib
//$c0 hib
auto GSU::op_hib() {
regs.dr() = regs.sr() >> 8;
regs.sfr.s = (regs.dr() & 0x80);
@@ -564,132 +344,81 @@ auto GSU::op_hib() {
regs.reset();
}
//$c1-cf(alt0): or rN
template<int n>
auto GSU::op_or_r() {
regs.dr() = regs.sr() | regs.r[n];
//$c1-cf(alt0) or rN
//$c1-cf(alt1) xor rN
//$c1-cf(alt2) or #N
//$c1-cf(alt3) xor #N
auto GSU::op_or_xor(uint n) {
if(!regs.sfr.alt2) n = regs.r[n];
regs.dr() = (!regs.sfr.alt1 ? (regs.sr() | n) : (regs.sr() ^ n));
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$c1-cf(alt1): xor rN
template<int n>
auto GSU::op_xor_r() {
regs.dr() = regs.sr() ^ regs.r[n];
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$c1-cf(alt2): or #N
template<int n>
auto GSU::op_or_i() {
regs.dr() = regs.sr() | n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$c1-cf(alt3): xor #N
template<int n>
auto GSU::op_xor_i() {
regs.dr() = regs.sr() ^ n;
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
}
//$d0-de: inc rN
template<int n>
auto GSU::op_inc_r() {
//$d0-de inc rN
auto GSU::op_inc(uint n) {
regs.r[n]++;
regs.sfr.s = (regs.r[n] & 0x8000);
regs.sfr.z = (regs.r[n] == 0);
regs.reset();
}
//$df(alt0): getc
auto GSU::op_getc() {
regs.colr = color(rombuffer_read());
//$df(alt0) getc
//$df(alt2) ramb
//$df(alt3) romb
auto GSU::op_getc_ramb_romb() {
if(!regs.sfr.alt2) {
regs.colr = color(readROMBuffer());
} else if(!regs.sfr.alt1) {
syncRAMBuffer();
regs.rambr = regs.sr() & 0x01;
} else {
syncROMBuffer();
regs.rombr = regs.sr() & 0x7f;
}
regs.reset();
}
//$df(alt2): ramb
auto GSU::op_ramb() {
rambuffer_sync();
regs.rambr = regs.sr() & 0x01;
regs.reset();
}
//$df(alt3): romb
auto GSU::op_romb() {
rombuffer_sync();
regs.rombr = regs.sr() & 0x7f;
regs.reset();
}
//$e0-ee: dec rN
template<int n>
auto GSU::op_dec_r() {
//$e0-ee dec rN
auto GSU::op_dec(uint n) {
regs.r[n]--;
regs.sfr.s = (regs.r[n] & 0x8000);
regs.sfr.z = (regs.r[n] == 0);
regs.reset();
}
//$ef(alt0): getb
//$ef(alt0) getb
//$ef(alt1) getbh
//$ef(alt2) getbl
//$ef(alt3) getbs
auto GSU::op_getb() {
regs.dr() = rombuffer_read();
switch(regs.sfr.alt2 << 1 | regs.sfr.alt1 << 0) {
case 0: regs.dr() = readROMBuffer(); break;
case 1: regs.dr() = readROMBuffer() << 8 | (uint8)regs.sr(); break;
case 2: regs.dr() = (regs.sr() & 0xff00) | readROMBuffer(); break;
case 3: regs.dr() = (int8)readROMBuffer(); break;
}
regs.reset();
}
//$ef(alt1): getbh
auto GSU::op_getbh() {
regs.dr() = (rombuffer_read() << 8) | (regs.sr() & 0x00ff);
regs.reset();
}
//$ef(alt2): getbl
auto GSU::op_getbl() {
regs.dr() = (regs.sr() & 0xff00) | (rombuffer_read() << 0);
regs.reset();
}
//$ef(alt3): getbs
auto GSU::op_getbs() {
regs.dr() = (int8)rombuffer_read();
regs.reset();
}
//$f0-ff(alt0): iwt rN,#xx
template<int n>
auto GSU::op_iwt_r() {
uint16_t data;
data = pipe() << 0;
data |= pipe() << 8;
regs.r[n] = data;
regs.reset();
}
//$f0-ff(alt1): lm rN,(xx)
template<int n>
auto GSU::op_lm_r() {
regs.ramaddr = pipe() << 0;
regs.ramaddr |= pipe() << 8;
uint16_t data;
data = rambuffer_read(regs.ramaddr ^ 0) << 0;
data |= rambuffer_read(regs.ramaddr ^ 1) << 8;
regs.r[n] = data;
regs.reset();
}
//$f0-ff(alt2): sm (xx),rN
template<int n>
auto GSU::op_sm_r() {
regs.ramaddr = pipe() << 0;
regs.ramaddr |= pipe() << 8;
rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0);
rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8);
//$f0-ff(alt0) iwt rN,#xx
//$f0-ff(alt1) lm rN,(xx)
//$f0-ff(alt2) sm (xx),rN
auto GSU::op_iwt_lm_sm(uint n) {
if(regs.sfr.alt1) {
regs.ramaddr = pipe() << 0;
regs.ramaddr |= pipe() << 8;
uint8 lo = readRAMBuffer(regs.ramaddr ^ 0) << 0;
regs.r[n] = readRAMBuffer(regs.ramaddr ^ 1) << 8 | lo;
} else if(regs.sfr.alt2) {
regs.ramaddr = pipe() << 0;
regs.ramaddr |= pipe() << 8;
writeRAMBuffer(regs.ramaddr ^ 0, regs.r[n] >> 0);
writeRAMBuffer(regs.ramaddr ^ 1, regs.r[n] >> 8);
} else {
uint8 lo = pipe();
regs.r[n] = pipe() << 8 | lo;
}
regs.reset();
}

View File

@@ -1,87 +1,71 @@
//accepts a callback binding so r14 writes can trigger ROM buffering transparently
struct reg16_t {
struct Register {
uint16 data = 0;
function<auto (uint16) -> void> modify;
bool modified = false;
inline operator unsigned() const {
inline operator uint() const {
return data;
}
inline auto assign(uint16 i) -> uint16 {
if(modify) modify(i);
else data = i;
return data;
inline auto assign(uint value) -> uint16 {
modified = true;
return data = value;
}
inline auto operator++() { return assign(data + 1); }
inline auto operator--() { return assign(data - 1); }
inline auto operator++(int) { unsigned r = data; assign(data + 1); return r; }
inline auto operator--(int) { unsigned r = data; assign(data - 1); return r; }
inline auto operator = (unsigned i) { return assign(i); }
inline auto operator |= (unsigned i) { return assign(data | i); }
inline auto operator ^= (unsigned i) { return assign(data ^ i); }
inline auto operator &= (unsigned i) { return assign(data & i); }
inline auto operator <<= (unsigned i) { return assign(data << i); }
inline auto operator >>= (unsigned i) { return assign(data >> i); }
inline auto operator += (unsigned i) { return assign(data + i); }
inline auto operator -= (unsigned i) { return assign(data - i); }
inline auto operator *= (unsigned i) { return assign(data * i); }
inline auto operator /= (unsigned i) { return assign(data / i); }
inline auto operator %= (unsigned i) { return assign(data % i); }
inline auto operator++(int) { uint r = data; assign(data + 1); return r; }
inline auto operator--(int) { uint r = data; assign(data - 1); return r; }
inline auto operator = (uint i) { return assign(i); }
inline auto operator |= (uint i) { return assign(data | i); }
inline auto operator ^= (uint i) { return assign(data ^ i); }
inline auto operator &= (uint i) { return assign(data & i); }
inline auto operator <<= (uint i) { return assign(data << i); }
inline auto operator >>= (uint i) { return assign(data >> i); }
inline auto operator += (uint i) { return assign(data + i); }
inline auto operator -= (uint i) { return assign(data - i); }
inline auto operator *= (uint i) { return assign(data * i); }
inline auto operator /= (uint i) { return assign(data / i); }
inline auto operator %= (uint i) { return assign(data % i); }
inline auto operator = (const reg16_t& i) { return assign(i); }
inline auto operator = (const Register& value) { return assign(value); }
reg16_t() = default;
reg16_t(const reg16_t&) = delete;
Register() = default;
Register(const Register&) = delete;
};
struct sfr_t {
bool irq; //interrupt flag
bool b; //WITH flag
bool ih; //immediate higher 8-bit flag
bool il; //immediate lower 8-bit flag
bool alt2; //ALT2 mode
bool alt1; //ALT2 instruction mode
bool r; //ROM r14 read flag
bool g; //GO flag
bool ov; //overflow flag
bool s; //sign flag
bool cy; //carry flag
bool z; //zero flag
struct SFR {
union {
uint16_t data = 0;
BitField<uint16_t, 15> irq; //interrupt flag
BitField<uint16_t, 12> b; //with flag
BitField<uint16_t, 11> ih; //immediate higher 8-bit flag
BitField<uint16_t, 10> il; //immediate lower 8-bit flag
BitField<uint16_t, 9> alt2; //alt2 instruction mode
BitField<uint16_t, 8> alt1; //alt1 instruction mode
BitField<uint16_t, 6> r; //ROM r14 read flag
BitField<uint16_t, 5> g; //go flag
BitField<uint16_t, 4> ov; //overflow flag
BitField<uint16_t, 3> s; //sign flag
BitField<uint16_t, 2> cy; //carry flag
BitField<uint16_t, 1> z; //zero flag
BitField<uint16_t, 9, 8> alt; //instruction mode (composite flag)
};
operator unsigned() const {
return (irq << 15) | (b << 12) | (ih << 11) | (il << 10) | (alt2 << 9) | (alt1 << 8)
| (r << 6) | (g << 5) | (ov << 4) | (s << 3) | (cy << 2) | (z << 1);
}
auto& operator=(uint16_t data) {
irq = data & 0x8000;
b = data & 0x1000;
ih = data & 0x0800;
il = data & 0x0400;
alt2 = data & 0x0200;
alt1 = data & 0x0100;
r = data & 0x0040;
g = data & 0x0020;
ov = data & 0x0010;
s = data & 0x0008;
cy = data & 0x0004;
z = data & 0x0002;
return *this;
}
inline operator uint() const { return data & 0x9f7e; }
inline auto& operator=(const uint value) { return data = value, *this; }
};
struct scmr_t {
unsigned ht;
struct SCMR {
uint ht;
bool ron;
bool ran;
unsigned md;
uint md;
operator unsigned() const {
operator uint() const {
return ((ht >> 1) << 5) | (ron << 4) | (ran << 3) | ((ht & 1) << 2) | (md);
}
auto& operator=(uint8 data) {
auto& operator=(uint data) {
ht = (bool)(data & 0x20) << 1;
ht |= (bool)(data & 0x04) << 0;
ron = data & 0x10;
@@ -91,18 +75,18 @@ struct scmr_t {
}
};
struct por_t {
struct POR {
bool obj;
bool freezehigh;
bool highnibble;
bool dither;
bool transparent;
operator unsigned() const {
operator uint() const {
return (obj << 4) | (freezehigh << 3) | (highnibble << 2) | (dither << 1) | (transparent);
}
auto& operator=(uint8 data) {
auto& operator=(uint data) {
obj = data & 0x10;
freezehigh = data & 0x08;
highnibble = data & 0x04;
@@ -112,48 +96,49 @@ struct por_t {
}
};
struct cfgr_t {
struct CFGR {
bool irq;
bool ms0;
operator unsigned() const {
operator uint() const {
return (irq << 7) | (ms0 << 5);
}
auto& operator=(uint8 data) {
auto& operator=(uint data) {
irq = data & 0x80;
ms0 = data & 0x20;
return *this;
}
};
struct regs_t {
struct Registers {
uint8 pipeline;
uint16 ramaddr;
reg16_t r[16]; //general purpose registers
sfr_t sfr; //status flag register
Register r[16]; //general purpose registers
SFR sfr; //status flag register
uint8 pbr; //program bank register
uint8 rombr; //game pack ROM bank register
bool rambr; //game pack RAM bank register
uint16 cbr; //cache base register
uint8 scbr; //screen base register
scmr_t scmr; //screen mode register
SCMR scmr; //screen mode register
uint8 colr; //color register
por_t por; //plot option register
POR por; //plot option register
bool bramr; //back-up RAM register
uint8 vcr; //version code register
cfgr_t cfgr; //config register
CFGR cfgr; //config register
bool clsr; //clock select register
unsigned romcl; //clock ticks until romdr is valid
uint romcl; //clock ticks until romdr is valid
uint8 romdr; //ROM buffer data register
unsigned ramcl; //clock ticks until ramdr is valid
uint ramcl; //clock ticks until ramdr is valid
uint16 ramar; //RAM buffer address register
uint8 ramdr; //RAM buffer data register
unsigned sreg, dreg;
uint sreg;
uint dreg;
auto& sr() { return r[sreg]; } //source register (from)
auto& dr() { return r[dreg]; } //destination register (to)
@@ -167,12 +152,12 @@ struct regs_t {
}
} regs;
struct cache_t {
struct Cache {
uint8 buffer[512];
bool valid[32];
} cache;
struct pixelcache_t {
struct PixelCache {
uint16 offset;
uint8 bitpend;
uint8 data[8];

View File

@@ -2,36 +2,12 @@ auto GSU::serialize(serializer& s) -> void {
s.integer(regs.pipeline);
s.integer(regs.ramaddr);
s.integer(regs.r[ 0].data);
s.integer(regs.r[ 1].data);
s.integer(regs.r[ 2].data);
s.integer(regs.r[ 3].data);
s.integer(regs.r[ 4].data);
s.integer(regs.r[ 5].data);
s.integer(regs.r[ 6].data);
s.integer(regs.r[ 7].data);
s.integer(regs.r[ 8].data);
s.integer(regs.r[ 9].data);
s.integer(regs.r[10].data);
s.integer(regs.r[11].data);
s.integer(regs.r[12].data);
s.integer(regs.r[13].data);
s.integer(regs.r[14].data);
s.integer(regs.r[15].data);
s.integer(regs.sfr.irq);
s.integer(regs.sfr.b);
s.integer(regs.sfr.ih);
s.integer(regs.sfr.il);
s.integer(regs.sfr.alt2);
s.integer(regs.sfr.alt1);
s.integer(regs.sfr.r);
s.integer(regs.sfr.g);
s.integer(regs.sfr.ov);
s.integer(regs.sfr.s);
s.integer(regs.sfr.cy);
s.integer(regs.sfr.z);
for(auto n : range(16)) {
s.integer(regs.r[n].data);
s.integer(regs.r[n].modified);
}
s.integer(regs.sfr.data);
s.integer(regs.pbr);
s.integer(regs.rombr);
s.integer(regs.rambr);
@@ -72,9 +48,9 @@ auto GSU::serialize(serializer& s) -> void {
s.array(cache.buffer);
s.array(cache.valid);
for(unsigned i = 0; i < 2; i++) {
s.integer(pixelcache[i].offset);
s.integer(pixelcache[i].bitpend);
s.array(pixelcache[i].data);
for(uint n : range(2)) {
s.integer(pixelcache[n].offset);
s.integer(pixelcache[n].bitpend);
s.array(pixelcache[n].data);
}
}

View File

@@ -0,0 +1,94 @@
auto GSU::instruction(uint8 opcode) -> void {
#define op(id, name, ...) \
case id: return op_##name(__VA_ARGS__); \
#define op4(id, name) \
case id+ 0: return op_##name((uint4)opcode); \
case id+ 1: return op_##name((uint4)opcode); \
case id+ 2: return op_##name((uint4)opcode); \
case id+ 3: return op_##name((uint4)opcode); \
#define op6(id, name) \
op4(id, name) \
case id+ 4: return op_##name((uint4)opcode); \
case id+ 5: return op_##name((uint4)opcode); \
#define op12(id, name) \
op6(id, name) \
case id+ 6: return op_##name((uint4)opcode); \
case id+ 7: return op_##name((uint4)opcode); \
case id+ 8: return op_##name((uint4)opcode); \
case id+ 9: return op_##name((uint4)opcode); \
case id+10: return op_##name((uint4)opcode); \
case id+11: return op_##name((uint4)opcode); \
#define op15(id, name) \
op12(id, name) \
case id+12: return op_##name((uint4)opcode); \
case id+13: return op_##name((uint4)opcode); \
case id+14: return op_##name((uint4)opcode); \
#define op16(id, name) \
op15(id, name) \
case id+15: return op_##name((uint4)opcode); \
switch(opcode) {
op (0x00, stop)
op (0x01, nop)
op (0x02, cache)
op (0x03, lsr)
op (0x04, rol)
op (0x05, branch, 1) //bra
op (0x06, branch, (regs.sfr.s ^ regs.sfr.ov) == 0) //blt
op (0x07, branch, (regs.sfr.s ^ regs.sfr.ov) == 1) //bge
op (0x08, branch, regs.sfr.z == 0) //bne
op (0x09, branch, regs.sfr.z == 1) //beq
op (0x0a, branch, regs.sfr.s == 0) //bpl
op (0x0b, branch, regs.sfr.s == 1) //bmi
op (0x0c, branch, regs.sfr.cy == 0) //bcc
op (0x0d, branch, regs.sfr.cy == 1) //bcs
op (0x0e, branch, regs.sfr.ov == 0) //bvc
op (0x0f, branch, regs.sfr.ov == 1) //bvs
op16(0x10, to_move)
op16(0x20, with)
op12(0x30, store)
op (0x3c, loop)
op (0x3d, alt1)
op (0x3e, alt2)
op (0x3f, alt3)
op12(0x40, load)
op (0x4c, plot_rpix)
op (0x4d, swap)
op (0x4e, color_cmode)
op (0x4f, not)
op16(0x50, add_adc)
op16(0x60, sub_sbc_cmp)
op (0x70, merge)
op15(0x71, and_bic)
op16(0x80, mult_umult)
op (0x90, sbk)
op4 (0x91, link)
op (0x95, sex)
op (0x96, asr_div2)
op (0x97, ror)
op6 (0x98, jmp_ljmp)
op (0x9e, lob)
op (0x9f, fmult_lmult)
op16(0xa0, ibt_lms_sms)
op16(0xb0, from_moves)
op (0xc0, hib)
op15(0xc1, or_xor)
op15(0xd0, inc)
op15(0xe0, dec)
op (0xdf, getc_ramb_romb)
op (0xef, getb)
op16(0xf0, iwt_lm_sm)
}
#undef op
#undef op4
#undef op6
#undef op12
#undef op15
#undef op16
}

View File

@@ -1,266 +0,0 @@
auto GSU::initialize_opcode_table() -> void {
#define op4(id, name) \
op(id+ 0, name< 1>) op(id+ 1, name< 2>) op(id+ 2, name< 3>) op(id+ 3, name< 4>)
#define op6(id, name) \
op(id+ 0, name< 8>) op(id+ 1, name< 9>) op(id+ 2, name<10>) op(id+ 3, name<11>) \
op(id+ 4, name<12>) op(id+ 5, name<13>)
#define op12(id, name) \
op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \
op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \
op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>)
#define op15l(id, name) \
op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \
op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \
op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) \
op(id+12, name<12>) op(id+13, name<13>) op(id+14, name<14>)
#define op15h(id, name) \
op(id+ 0, name< 1>) op(id+ 1, name< 2>) op(id+ 2, name< 3>) op(id+ 3, name< 4>) \
op(id+ 4, name< 5>) op(id+ 5, name< 6>) op(id+ 6, name< 7>) op(id+ 7, name< 8>) \
op(id+ 8, name< 9>) op(id+ 9, name<10>) op(id+10, name<11>) op(id+11, name<12>) \
op(id+12, name<13>) op(id+13, name<14>) op(id+14, name<15>)
#define op16(id, name) \
op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \
op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \
op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) \
op(id+12, name<12>) op(id+13, name<13>) op(id+14, name<14>) op(id+15, name<15>)
//======
// ALT0
//======
#define op(id, name) opcode_table[ 0 + id] = &GSU::op_##name;
op (0x00, stop)
op (0x01, nop)
op (0x02, cache)
op (0x03, lsr)
op (0x04, rol)
op (0x05, bra)
op (0x06, blt)
op (0x07, bge)
op (0x08, bne)
op (0x09, beq)
op (0x0a, bpl)
op (0x0b, bmi)
op (0x0c, bcc)
op (0x0d, bcs)
op (0x0e, bvc)
op (0x0f, bvs)
op16 (0x10, to_r)
op16 (0x20, with_r)
op12 (0x30, stw_ir)
op (0x3c, loop)
op (0x3d, alt1)
op (0x3e, alt2)
op (0x3f, alt3)
op12 (0x40, ldw_ir)
op (0x4c, plot)
op (0x4d, swap)
op (0x4e, color)
op (0x4f, not)
op16 (0x50, add_r)
op16 (0x60, sub_r)
op (0x70, merge)
op15h(0x71, and_r)
op16 (0x80, mult_r)
op (0x90, sbk)
op4 (0x91, link)
op (0x95, sex)
op (0x96, asr)
op (0x97, ror)
op6 (0x98, jmp_r)
op (0x9e, lob)
op (0x9f, fmult)
op16 (0xa0, ibt_r)
op16 (0xb0, from_r)
op (0xc0, hib)
op15h(0xc1, or_r)
op15l(0xd0, inc_r)
op (0xdf, getc)
op15l(0xe0, dec_r)
op (0xef, getb)
op16 (0xf0, iwt_r)
#undef op
//======
// ALT1
//======
#define op(id, name) opcode_table[256 + id] = &GSU::op_##name;
op (0x00, stop)
op (0x01, nop)
op (0x02, cache)
op (0x03, lsr)
op (0x04, rol)
op (0x05, bra)
op (0x06, blt)
op (0x07, bge)
op (0x08, bne)
op (0x09, beq)
op (0x0a, bpl)
op (0x0b, bmi)
op (0x0c, bcc)
op (0x0d, bcs)
op (0x0e, bvc)
op (0x0f, bvs)
op16 (0x10, to_r)
op16 (0x20, with_r)
op12 (0x30, stb_ir)
op (0x3c, loop)
op (0x3d, alt1)
op (0x3e, alt2)
op (0x3f, alt3)
op12 (0x40, ldb_ir)
op (0x4c, rpix)
op (0x4d, swap)
op (0x4e, cmode)
op (0x4f, not)
op16 (0x50, adc_r)
op16 (0x60, sbc_r)
op (0x70, merge)
op15h(0x71, bic_r)
op16 (0x80, umult_r)
op (0x90, sbk)
op4 (0x91, link)
op (0x95, sex)
op (0x96, div2)
op (0x97, ror)
op6 (0x98, ljmp_r)
op (0x9e, lob)
op (0x9f, lmult)
op16 (0xa0, lms_r)
op16 (0xb0, from_r)
op (0xc0, hib)
op15h(0xc1, xor_r)
op15l(0xd0, inc_r)
op (0xdf, getc)
op15l(0xe0, dec_r)
op (0xef, getbh)
op16 (0xf0, lm_r)
#undef op
//======
// ALT2
//======
#define op(id, name) opcode_table[512 + id] = &GSU::op_##name;
op (0x00, stop)
op (0x01, nop)
op (0x02, cache)
op (0x03, lsr)
op (0x04, rol)
op (0x05, bra)
op (0x06, blt)
op (0x07, bge)
op (0x08, bne)
op (0x09, beq)
op (0x0a, bpl)
op (0x0b, bmi)
op (0x0c, bcc)
op (0x0d, bcs)
op (0x0e, bvc)
op (0x0f, bvs)
op16 (0x10, to_r)
op16 (0x20, with_r)
op12 (0x30, stw_ir)
op (0x3c, loop)
op (0x3d, alt1)
op (0x3e, alt2)
op (0x3f, alt3)
op12 (0x40, ldw_ir)
op (0x4c, plot)
op (0x4d, swap)
op (0x4e, color)
op (0x4f, not)
op16 (0x50, add_i)
op16 (0x60, sub_i)
op (0x70, merge)
op15h(0x71, and_i)
op16 (0x80, mult_i)
op (0x90, sbk)
op4 (0x91, link)
op (0x95, sex)
op (0x96, asr)
op (0x97, ror)
op6 (0x98, jmp_r)
op (0x9e, lob)
op (0x9f, fmult)
op16 (0xa0, sms_r)
op16 (0xb0, from_r)
op (0xc0, hib)
op15h(0xc1, or_i)
op15l(0xd0, inc_r)
op (0xdf, ramb)
op15l(0xe0, dec_r)
op (0xef, getbl)
op16 (0xf0, sm_r)
#undef op
//======
// ALT3
//======
#define op(id, name) opcode_table[768 + id] = &GSU::op_##name;
op (0x00, stop)
op (0x01, nop)
op (0x02, cache)
op (0x03, lsr)
op (0x04, rol)
op (0x05, bra)
op (0x06, blt)
op (0x07, bge)
op (0x08, bne)
op (0x09, beq)
op (0x0a, bpl)
op (0x0b, bmi)
op (0x0c, bcc)
op (0x0d, bcs)
op (0x0e, bvc)
op (0x0f, bvs)
op16 (0x10, to_r)
op16 (0x20, with_r)
op12 (0x30, stb_ir)
op (0x3c, loop)
op (0x3d, alt1)
op (0x3e, alt2)
op (0x3f, alt3)
op12 (0x40, ldb_ir)
op (0x4c, rpix)
op (0x4d, swap)
op (0x4e, cmode)
op (0x4f, not)
op16 (0x50, adc_i)
op16 (0x60, cmp_r)
op (0x70, merge)
op15h(0x71, bic_i)
op16 (0x80, umult_i)
op (0x90, sbk)
op4 (0x91, link)
op (0x95, sex)
op (0x96, div2)
op (0x97, ror)
op6 (0x98, ljmp_r)
op (0x9e, lob)
op (0x9f, lmult)
op16 (0xa0, lms_r)
op16 (0xb0, from_r)
op (0xc0, hib)
op15h(0xc1, xor_i)
op15l(0xd0, inc_r)
op (0xdf, romb)
op15l(0xe0, dec_r)
op (0xef, getbs)
op16 (0xf0, lm_r)
#undef op
#undef op4
#undef op6
#undef op12
#undef op15l
#undef op15h
#undef op16
}

View File

@@ -10,8 +10,8 @@ namespace Processor {
auto HG51B::exec(uint24 addr) -> void {
if(regs.halt) return;
addr = addr + regs.pc * 2;
opcode = bus_read(addr++) << 0;
opcode |= bus_read(addr++) << 8;
opcode = read(addr++) << 0;
opcode |= read(addr++) << 8;
regs.pc = (regs.pc & 0xffff00) | ((regs.pc + 1) & 0x0000ff);
instruction();
}

View File

@@ -1,13 +1,13 @@
//Hitachi HG51B169 (HG51BS family/derivative?)
#pragma once
//Hitachi HG51B169 (HG51BS family/derivative?)
namespace Processor {
struct HG51B {
auto exec(uint24 addr) -> void;
virtual auto bus_read(uint24 addr) -> uint8 = 0;
virtual auto bus_write(uint24 addr, uint8 data) -> void = 0;
virtual auto read(uint24 addr) -> uint8 = 0;
virtual auto write(uint24 addr, uint8 data) -> void = 0;
auto power() -> void;
auto serialize(serializer&) -> void;
@@ -25,8 +25,8 @@ protected:
auto instruction() -> void;
//registers.cpp
auto reg_read(uint8 addr) const -> uint24;
auto reg_write(uint8 addr, uint24 data) -> void;
auto regRead(uint8 addr) const -> uint24;
auto regWrite(uint8 addr, uint24 data) -> void;
struct Registers {
bool halt;

View File

@@ -34,7 +34,7 @@ auto HG51B::sa() -> uint {
//Register-or-Immediate: most opcodes can load from a register or immediate
auto HG51B::ri() -> uint {
if(opcode & 0x0400) return opcode & 0xff;
return reg_read(opcode & 0xff);
return regRead(opcode & 0xff);
}
//New-PC: determine jump target address; opcode.d9 = long jump flag (1 = yes)
@@ -115,7 +115,7 @@ auto HG51B::instruction() -> void {
else if((opcode & 0xffff) == 0x4000) {
//0100 0000 0000 0000
//rdbus
regs.busdata = bus_read(regs.busaddr++);
regs.busdata = read(regs.busaddr++);
}
else if((opcode & 0xf800) == 0x4800) {
@@ -306,7 +306,7 @@ auto HG51B::instruction() -> void {
else if((opcode & 0xff00) == 0xe000) {
//1110 0000 .... ....
//st r,a
reg_write(opcode & 0xff, regs.a);
regWrite(opcode & 0xff, regs.a);
}
else if((opcode & 0xfb00) == 0xe800) {
@@ -333,10 +333,10 @@ auto HG51B::instruction() -> void {
else if((opcode & 0xff00) == 0xf000) {
//1111 0000 .... ....
//swap a,r
uint24 source = reg_read(opcode & 0xff);
uint24 source = regRead(opcode & 0xff);
uint24 target = regs.a;
regs.a = source;
reg_write(opcode & 0xff, target);
regWrite(opcode & 0xff, target);
}
else if((opcode & 0xffff) == 0xfc00) {

View File

@@ -1,4 +1,4 @@
auto HG51B::reg_read(uint8 addr) const -> uint24 {
auto HG51B::regRead(uint8 addr) const -> uint24 {
switch(addr) {
case 0x00: return regs.a;
case 0x01: return regs.acch;
@@ -44,7 +44,7 @@ auto HG51B::reg_read(uint8 addr) const -> uint24 {
return 0x000000;
}
auto HG51B::reg_write(uint8 addr, uint24 data) -> void {
auto HG51B::regWrite(uint8 addr, uint24 data) -> void {
switch(addr) {
case 0x00: regs.a = data; return;
case 0x01: regs.acch = data; return;

View File

@@ -20,10 +20,10 @@ auto LR35902::disassemble(uint16 pc) -> string {
}
auto LR35902::disassembleOpcode(uint16 pc) -> string {
uint8 opcode = debugger_read(pc);
uint8 p0 = debugger_read(pc + 1);
uint8 p1 = debugger_read(pc + 2);
uint8 p2 = debugger_read(pc + 3);
uint8 opcode = debuggerRead(pc);
uint8 p0 = debuggerRead(pc + 1);
uint8 p1 = debuggerRead(pc + 2);
uint8 p2 = debuggerRead(pc + 3);
switch(opcode) {
case 0x00: return { "nop" };
@@ -288,10 +288,10 @@ auto LR35902::disassembleOpcode(uint16 pc) -> string {
}
auto LR35902::disassembleOpcodeCB(uint16 pc) -> string {
uint8 opcode = debugger_read(pc);
uint8 p0 = debugger_read(pc + 1);
uint8 p1 = debugger_read(pc + 2);
uint8 p2 = debugger_read(pc + 3);
uint8 opcode = debuggerRead(pc);
uint8 p0 = debuggerRead(pc + 1);
uint8 p1 = debuggerRead(pc + 2);
uint8 p2 = debuggerRead(pc + 3);
switch(opcode) {
case 0x00: return { "rlc b" };

View File

@@ -2,115 +2,115 @@ auto LR35902::op_xx() {
}
auto LR35902::op_cb() {
execCB();
instructionCB();
}
//8-bit load commands
template<uint x, uint y> auto LR35902::op_ld_r_r() {
auto LR35902::op_ld_r_r(uint x, uint y) {
r[x] = r[y];
}
template<uint x> auto LR35902::op_ld_r_n() {
r[x] = op_read(r[PC]++);
auto LR35902::op_ld_r_n(uint x) {
r[x] = read(r[PC]++);
}
template<uint x> auto LR35902::op_ld_r_hl() {
r[x] = op_read(r[HL]);
auto LR35902::op_ld_r_hl(uint x) {
r[x] = read(r[HL]);
}
template<uint x> auto LR35902::op_ld_hl_r() {
op_write(r[HL], r[x]);
auto LR35902::op_ld_hl_r(uint x) {
write(r[HL], r[x]);
}
auto LR35902::op_ld_hl_n() {
op_write(r[HL], op_read(r[PC]++));
write(r[HL], read(r[PC]++));
}
template<uint x> auto LR35902::op_ld_a_rr() {
r[A] = op_read(r[x]);
auto LR35902::op_ld_a_rr(uint x) {
r[A] = read(r[x]);
}
auto LR35902::op_ld_a_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
r[A] = op_read((hi << 8) | (lo << 0));
uint8 lo = read(r[PC]++);
uint8 hi = read(r[PC]++);
r[A] = read((hi << 8) | (lo << 0));
}
template<uint x> auto LR35902::op_ld_rr_a() {
op_write(r[x], r[A]);
auto LR35902::op_ld_rr_a(uint x) {
write(r[x], r[A]);
}
auto LR35902::op_ld_nn_a() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
op_write((hi << 8) | (lo << 0), r[A]);
uint8 lo = read(r[PC]++);
uint8 hi = read(r[PC]++);
write((hi << 8) | (lo << 0), r[A]);
}
auto LR35902::op_ld_a_ffn() {
r[A] = op_read(0xff00 + op_read(r[PC]++));
r[A] = read(0xff00 + read(r[PC]++));
}
auto LR35902::op_ld_ffn_a() {
op_write(0xff00 + op_read(r[PC]++), r[A]);
write(0xff00 + read(r[PC]++), r[A]);
}
auto LR35902::op_ld_a_ffc() {
r[A] = op_read(0xff00 + r[C]);
r[A] = read(0xff00 + r[C]);
}
auto LR35902::op_ld_ffc_a() {
op_write(0xff00 + r[C], r[A]);
write(0xff00 + r[C], r[A]);
}
auto LR35902::op_ldi_hl_a() {
op_write(r[HL], r[A]);
write(r[HL], r[A]);
r[HL]++;
}
auto LR35902::op_ldi_a_hl() {
r[A] = op_read(r[HL]);
r[A] = read(r[HL]);
r[HL]++;
}
auto LR35902::op_ldd_hl_a() {
op_write(r[HL], r[A]);
write(r[HL], r[A]);
r[HL]--;
}
auto LR35902::op_ldd_a_hl() {
r[A] = op_read(r[HL]);
r[A] = read(r[HL]);
r[HL]--;
}
//16-bit load commands
template<uint x> auto LR35902::op_ld_rr_nn() {
r[x] = op_read(r[PC]++) << 0;
r[x] |= op_read(r[PC]++) << 8;
auto LR35902::op_ld_rr_nn(uint x) {
r[x] = read(r[PC]++) << 0;
r[x] |= read(r[PC]++) << 8;
}
auto LR35902::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);
uint16 addr = read(r[PC]++) << 0;
addr |= read(r[PC]++) << 8;
write(addr + 0, r[SP] >> 0);
write(addr + 1, r[SP] >> 8);
}
auto LR35902::op_ld_sp_hl() {
r[SP] = r[HL];
op_io();
io();
}
template<uint x> auto LR35902::op_push_rr() {
op_io();
op_write(--r[SP], r[x] >> 8);
op_write(--r[SP], r[x] >> 0);
auto LR35902::op_push_rr(uint x) {
io();
write(--r[SP], r[x] >> 8);
write(--r[SP], r[x] >> 0);
}
template<uint x> auto LR35902::op_pop_rr() {
r[x] = op_read(r[SP]++) << 0;
r[x] |= op_read(r[SP]++) << 8;
auto LR35902::op_pop_rr(uint x) {
r[x] = read(r[SP]++) << 0;
r[x] |= read(r[SP]++) << 8;
}
//8-bit arithmetic commands
@@ -125,9 +125,9 @@ auto LR35902::opi_add_a(uint8 x) {
r.f.c = rh > 0xff;
}
template<uint x> auto LR35902::op_add_a_r() { opi_add_a(r[x]); }
auto LR35902::op_add_a_n() { opi_add_a(op_read(r[PC]++)); }
auto LR35902::op_add_a_hl() { opi_add_a(op_read(r[HL])); }
auto LR35902::op_add_a_r(uint x) { opi_add_a(r[x]); }
auto LR35902::op_add_a_n() { opi_add_a(read(r[PC]++)); }
auto LR35902::op_add_a_hl() { opi_add_a(read(r[HL])); }
auto LR35902::opi_adc_a(uint8 x) {
uint16 rh = r[A] + x + r.f.c;
@@ -139,9 +139,9 @@ auto LR35902::opi_adc_a(uint8 x) {
r.f.c = rh > 0xff;
}
template<uint x> auto LR35902::op_adc_a_r() { opi_adc_a(r[x]); }
auto LR35902::op_adc_a_n() { opi_adc_a(op_read(r[PC]++)); }
auto LR35902::op_adc_a_hl() { opi_adc_a(op_read(r[HL])); }
auto LR35902::op_adc_a_r(uint x) { opi_adc_a(r[x]); }
auto LR35902::op_adc_a_n() { opi_adc_a(read(r[PC]++)); }
auto LR35902::op_adc_a_hl() { opi_adc_a(read(r[HL])); }
auto LR35902::opi_sub_a(uint8 x) {
uint16 rh = r[A] - x;
@@ -153,9 +153,9 @@ auto LR35902::opi_sub_a(uint8 x) {
r.f.c = rh > 0xff;
}
template<uint x> auto LR35902::op_sub_a_r() { opi_sub_a(r[x]); }
auto LR35902::op_sub_a_n() { opi_sub_a(op_read(r[PC]++)); }
auto LR35902::op_sub_a_hl() { opi_sub_a(op_read(r[HL])); }
auto LR35902::op_sub_a_r(uint x) { opi_sub_a(r[x]); }
auto LR35902::op_sub_a_n() { opi_sub_a(read(r[PC]++)); }
auto LR35902::op_sub_a_hl() { opi_sub_a(read(r[HL])); }
auto LR35902::opi_sbc_a(uint8 x) {
uint16 rh = r[A] - x - r.f.c;
@@ -167,9 +167,9 @@ auto LR35902::opi_sbc_a(uint8 x) {
r.f.c = rh > 0xff;
}
template<uint x> auto LR35902::op_sbc_a_r() { opi_sbc_a(r[x]); }
auto LR35902::op_sbc_a_n() { opi_sbc_a(op_read(r[PC]++)); }
auto LR35902::op_sbc_a_hl() { opi_sbc_a(op_read(r[HL])); }
auto LR35902::op_sbc_a_r(uint x) { opi_sbc_a(r[x]); }
auto LR35902::op_sbc_a_n() { opi_sbc_a(read(r[PC]++)); }
auto LR35902::op_sbc_a_hl() { opi_sbc_a(read(r[HL])); }
auto LR35902::opi_and_a(uint8 x) {
r[A] &= x;
@@ -179,9 +179,9 @@ auto LR35902::opi_and_a(uint8 x) {
r.f.c = 0;
}
template<uint x> auto LR35902::op_and_a_r() { opi_and_a(r[x]); }
auto LR35902::op_and_a_n() { opi_and_a(op_read(r[PC]++)); }
auto LR35902::op_and_a_hl() { opi_and_a(op_read(r[HL])); }
auto LR35902::op_and_a_r(uint x) { opi_and_a(r[x]); }
auto LR35902::op_and_a_n() { opi_and_a(read(r[PC]++)); }
auto LR35902::op_and_a_hl() { opi_and_a(read(r[HL])); }
auto LR35902::opi_xor_a(uint8 x) {
r[A] ^= x;
@@ -191,9 +191,9 @@ auto LR35902::opi_xor_a(uint8 x) {
r.f.c = 0;
}
template<uint x> auto LR35902::op_xor_a_r() { opi_xor_a(r[x]); }
auto LR35902::op_xor_a_n() { opi_xor_a(op_read(r[PC]++)); }
auto LR35902::op_xor_a_hl() { opi_xor_a(op_read(r[HL])); }
auto LR35902::op_xor_a_r(uint x) { opi_xor_a(r[x]); }
auto LR35902::op_xor_a_n() { opi_xor_a(read(r[PC]++)); }
auto LR35902::op_xor_a_hl() { opi_xor_a(read(r[HL])); }
auto LR35902::opi_or_a(uint8 x) {
r[A] |= x;
@@ -203,9 +203,9 @@ auto LR35902::opi_or_a(uint8 x) {
r.f.c = 0;
}
template<uint x> auto LR35902::op_or_a_r() { opi_or_a(r[x]); }
auto LR35902::op_or_a_n() { opi_or_a(op_read(r[PC]++)); }
auto LR35902::op_or_a_hl() { opi_or_a(op_read(r[HL])); }
auto LR35902::op_or_a_r(uint x) { opi_or_a(r[x]); }
auto LR35902::op_or_a_n() { opi_or_a(read(r[PC]++)); }
auto LR35902::op_or_a_hl() { opi_or_a(read(r[HL])); }
auto LR35902::opi_cp_a(uint8 x) {
uint16 rh = r[A] - x;
@@ -216,11 +216,11 @@ auto LR35902::opi_cp_a(uint8 x) {
r.f.c = rh > 0xff;
}
template<uint x> auto LR35902::op_cp_a_r() { opi_cp_a(r[x]); }
auto LR35902::op_cp_a_n() { opi_cp_a(op_read(r[PC]++)); }
auto LR35902::op_cp_a_hl() { opi_cp_a(op_read(r[HL])); }
auto LR35902::op_cp_a_r(uint x) { opi_cp_a(r[x]); }
auto LR35902::op_cp_a_n() { opi_cp_a(read(r[PC]++)); }
auto LR35902::op_cp_a_hl() { opi_cp_a(read(r[HL])); }
template<uint x> auto LR35902::op_inc_r() {
auto LR35902::op_inc_r(uint x) {
r[x]++;
r.f.z = r[x] == 0;
r.f.n = 0;
@@ -228,14 +228,14 @@ template<uint x> auto LR35902::op_inc_r() {
}
auto LR35902::op_inc_hl() {
uint8 n = op_read(r[HL]);
op_write(r[HL], ++n);
uint8 n = read(r[HL]);
write(r[HL], ++n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = (n & 0x0f) == 0x00;
}
template<uint x> auto LR35902::op_dec_r() {
auto LR35902::op_dec_r(uint x) {
r[x]--;
r.f.z = r[x] == 0;
r.f.n = 1;
@@ -243,8 +243,8 @@ template<uint x> auto LR35902::op_dec_r() {
}
auto LR35902::op_dec_hl() {
uint8 n = op_read(r[HL]);
op_write(r[HL], --n);
uint8 n = read(r[HL]);
write(r[HL], --n);
r.f.z = n == 0;
r.f.n = 1;
r.f.h = (n & 0x0f) == 0x0f;
@@ -276,8 +276,8 @@ auto LR35902::op_cpl() {
//16-bit arithmetic commands
template<uint x> auto LR35902::op_add_hl_rr() {
op_io();
auto LR35902::op_add_hl_rr(uint x) {
io();
uint32 rb = (r[HL] + r[x]);
uint32 rn = (r[HL] & 0xfff) + (r[x] & 0xfff);
r[HL] = rb;
@@ -286,35 +286,35 @@ template<uint x> auto LR35902::op_add_hl_rr() {
r.f.c = rb > 0xffff;
}
template<uint x> auto LR35902::op_inc_rr() {
op_io();
auto LR35902::op_inc_rr(uint x) {
io();
r[x]++;
}
template<uint x> auto LR35902::op_dec_rr() {
op_io();
auto LR35902::op_dec_rr(uint x) {
io();
r[x]--;
}
auto LR35902::op_add_sp_n() {
int n = (int8)op_read(r[PC]++);
int n = (int8)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;
op_io();
op_io();
io();
io();
}
auto LR35902::op_ld_hl_sp_n() {
int n = (int8)op_read(r[PC]++);
int n = (int8)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;
op_io();
io();
}
//rotate/shift commands
@@ -353,7 +353,7 @@ auto LR35902::op_rra() {
r.f.c = c;
}
template<uint x> auto LR35902::op_rlc_r() {
auto LR35902::op_rlc_r(uint x) {
r[x] = (r[x] << 1) | (r[x] >> 7);
r.f.z = r[x] == 0;
r.f.n = 0;
@@ -362,16 +362,16 @@ template<uint x> auto LR35902::op_rlc_r() {
}
auto LR35902::op_rlc_hl() {
uint8 n = op_read(r[HL]);
uint8 n = read(r[HL]);
n = (n << 1) | (n >> 7);
op_write(r[HL], n);
write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = n & 0x01;
}
template<uint x> auto LR35902::op_rl_r() {
auto LR35902::op_rl_r(uint x) {
bool c = r[x] & 0x80;
r[x] = (r[x] << 1) | (r.f.c << 0);
r.f.z = r[x] == 0;
@@ -381,17 +381,17 @@ template<uint x> auto LR35902::op_rl_r() {
}
auto LR35902::op_rl_hl() {
uint8 n = op_read(r[HL]);
uint8 n = read(r[HL]);
bool c = n & 0x80;
n = (n << 1) | (r.f.c << 0);
op_write(r[HL], n);
write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
template<uint x> auto LR35902::op_rrc_r() {
auto LR35902::op_rrc_r(uint x) {
r[x] = (r[x] >> 1) | (r[x] << 7);
r.f.z = r[x] == 0;
r.f.n = 0;
@@ -400,16 +400,16 @@ template<uint x> auto LR35902::op_rrc_r() {
}
auto LR35902::op_rrc_hl() {
uint8 n = op_read(r[HL]);
uint8 n = read(r[HL]);
n = (n >> 1) | (n << 7);
op_write(r[HL], n);
write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = n & 0x80;
}
template<uint x> auto LR35902::op_rr_r() {
auto LR35902::op_rr_r(uint x) {
bool c = r[x] & 0x01;
r[x] = (r[x] >> 1) | (r.f.c << 7);
r.f.z = r[x] == 0;
@@ -419,17 +419,17 @@ template<uint x> auto LR35902::op_rr_r() {
}
auto LR35902::op_rr_hl() {
uint8 n = op_read(r[HL]);
uint8 n = read(r[HL]);
bool c = n & 0x01;
n = (n >> 1) | (r.f.c << 7);
op_write(r[HL], n);
write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
template<uint x> auto LR35902::op_sla_r() {
auto LR35902::op_sla_r(uint x) {
bool c = r[x] & 0x80;
r[x] <<= 1;
r.f.z = r[x] == 0;
@@ -439,17 +439,17 @@ template<uint x> auto LR35902::op_sla_r() {
}
auto LR35902::op_sla_hl() {
uint8 n = op_read(r[HL]);
uint8 n = read(r[HL]);
bool c = n & 0x80;
n <<= 1;
op_write(r[HL], n);
write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
template<uint x> auto LR35902::op_swap_r() {
auto LR35902::op_swap_r(uint x) {
r[x] = (r[x] << 4) | (r[x] >> 4);
r.f.z = r[x] == 0;
r.f.n = 0;
@@ -458,16 +458,16 @@ template<uint x> auto LR35902::op_swap_r() {
}
auto LR35902::op_swap_hl() {
uint8 n = op_read(r[HL]);
uint8 n = read(r[HL]);
n = (n << 4) | (n >> 4);
op_write(r[HL], n);
write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = 0;
}
template<uint x> auto LR35902::op_sra_r() {
auto LR35902::op_sra_r(uint x) {
bool c = r[x] & 0x01;
r[x] = (int8)r[x] >> 1;
r.f.z = r[x] == 0;
@@ -477,17 +477,17 @@ template<uint x> auto LR35902::op_sra_r() {
}
auto LR35902::op_sra_hl() {
uint8 n = op_read(r[HL]);
uint8 n = read(r[HL]);
bool c = n & 0x01;
n = (int8)n >> 1;
op_write(r[HL], n);
write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = c;
}
template<uint x> auto LR35902::op_srl_r() {
auto LR35902::op_srl_r(uint x) {
bool c = r[x] & 0x01;
r[x] >>= 1;
r.f.z = r[x] == 0;
@@ -497,10 +497,10 @@ template<uint x> auto LR35902::op_srl_r() {
}
auto LR35902::op_srl_hl() {
uint8 n = op_read(r[HL]);
uint8 n = read(r[HL]);
bool c = n & 0x01;
n >>= 1;
op_write(r[HL], n);
write(r[HL], n);
r.f.z = n == 0;
r.f.n = 0;
r.f.h = 0;
@@ -509,37 +509,37 @@ auto LR35902::op_srl_hl() {
//single-bit commands
template<uint b, uint x> auto LR35902::op_bit_n_r() {
auto LR35902::op_bit_n_r(uint b, uint x) {
r.f.z = (r[x] & (1 << b)) == 0;
r.f.n = 0;
r.f.h = 1;
}
template<uint b> auto LR35902::op_bit_n_hl() {
uint8 n = op_read(r[HL]);
auto LR35902::op_bit_n_hl(uint b) {
uint8 n = read(r[HL]);
r.f.z = (n & (1 << b)) == 0;
r.f.n = 0;
r.f.h = 1;
}
template<uint b, uint x> auto LR35902::op_set_n_r() {
auto LR35902::op_set_n_r(uint b, uint x) {
r[x] |= 1 << b;
}
template<uint b> auto LR35902::op_set_n_hl() {
uint8 n = op_read(r[HL]);
auto LR35902::op_set_n_hl(uint b) {
uint8 n = read(r[HL]);
n |= 1 << b;
op_write(r[HL], n);
write(r[HL], n);
}
template<uint b, uint x> auto LR35902::op_res_n_r() {
auto LR35902::op_res_n_r(uint b, uint x) {
r[x] &= ~(1 << b);
}
template<uint b> auto LR35902::op_res_n_hl() {
uint n = op_read(r[HL]);
auto LR35902::op_res_n_hl(uint b) {
uint n = read(r[HL]);
n &= ~(1 << b);
op_write(r[HL], n);
write(r[HL], n);
}
//control commands
@@ -561,13 +561,13 @@ auto LR35902::op_nop() {
auto LR35902::op_halt() {
r.halt = true;
while(r.halt == true) op_io();
while(r.halt) io();
}
auto LR35902::op_stop() {
if(stop()) return;
r.stop = true;
while(r.stop == true) op_io();
while(r.stop) io();
}
auto LR35902::op_di() {
@@ -582,87 +582,87 @@ auto LR35902::op_ei() {
//jump commands
auto LR35902::op_jp_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
uint8 lo = read(r[PC]++);
uint8 hi = read(r[PC]++);
r[PC] = (hi << 8) | (lo << 0);
op_io();
io();
}
auto LR35902::op_jp_hl() {
r[PC] = r[HL];
}
template<uint x, bool y> auto LR35902::op_jp_f_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
auto LR35902::op_jp_f_nn(uint x, bool y) {
uint8 lo = read(r[PC]++);
uint8 hi = read(r[PC]++);
if(r.f[x] == y) {
r[PC] = (hi << 8) | (lo << 0);
op_io();
io();
}
}
auto LR35902::op_jr_n() {
int8 n = op_read(r[PC]++);
int8 n = read(r[PC]++);
r[PC] += n;
op_io();
io();
}
template<uint x, bool y> auto LR35902::op_jr_f_n() {
int8 n = op_read(r[PC]++);
auto LR35902::op_jr_f_n(uint x, bool y) {
int8 n = read(r[PC]++);
if(r.f[x] == y) {
r[PC] += n;
op_io();
io();
}
}
auto LR35902::op_call_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
op_io();
op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0);
uint8 lo = read(r[PC]++);
uint8 hi = read(r[PC]++);
io();
write(--r[SP], r[PC] >> 8);
write(--r[SP], r[PC] >> 0);
r[PC] = (hi << 8) | (lo << 0);
}
template<uint x, bool y> auto LR35902::op_call_f_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
auto LR35902::op_call_f_nn(uint x, bool y) {
uint8 lo = read(r[PC]++);
uint8 hi = read(r[PC]++);
if(r.f[x] == y) {
op_io();
op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0);
io();
write(--r[SP], r[PC] >> 8);
write(--r[SP], r[PC] >> 0);
r[PC] = (hi << 8) | (lo << 0);
}
}
auto LR35902::op_ret() {
uint8 lo = op_read(r[SP]++);
uint8 hi = op_read(r[SP]++);
uint8 lo = read(r[SP]++);
uint8 hi = read(r[SP]++);
r[PC] = (hi << 8) | (lo << 0);
op_io();
io();
}
template<uint x, bool y> auto LR35902::op_ret_f() {
op_io();
auto LR35902::op_ret_f(uint x, bool y) {
io();
if(r.f[x] == y) {
uint8 lo = op_read(r[SP]++);
uint8 hi = op_read(r[SP]++);
uint8 lo = read(r[SP]++);
uint8 hi = read(r[SP]++);
r[PC] = (hi << 8) | (lo << 0);
op_io();
io();
}
}
auto LR35902::op_reti() {
uint8 lo = op_read(r[SP]++);
uint8 hi = op_read(r[SP]++);
uint8 lo = read(r[SP]++);
uint8 hi = read(r[SP]++);
r[PC] = (hi << 8) | (lo << 0);
op_io();
io();
r.ime = 1;
}
template<uint n> auto LR35902::op_rst_n() {
op_io();
op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0);
auto LR35902::op_rst_n(uint n) {
io();
write(--r[SP], r[PC] >> 8);
write(--r[SP], r[PC] >> 0);
r[PC] = n;
}

View File

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

View File

@@ -5,15 +5,16 @@
namespace Processor {
struct LR35902 {
virtual auto op_io() -> void = 0;
virtual auto op_read(uint16 addr) -> uint8 = 0;
virtual auto op_write(uint16 addr, uint8 data) -> void = 0;
virtual auto io() -> void = 0;
virtual auto read(uint16 addr) -> uint8 = 0;
virtual auto write(uint16 addr, uint8 data) -> void = 0;
virtual auto stop() -> bool = 0;
virtual auto debugger_read(uint16 addr) -> uint8 { return 0u; }
virtual auto debuggerRead(uint16 addr) -> uint8 { return 0; }
auto power() -> void;
auto exec() -> void;
auto execCB() -> void;
auto interrupt(uint16 vector) -> void;
auto instruction() -> void;
auto instructionCB() -> void;
auto serialize(serializer&) -> void;
@@ -24,14 +25,14 @@ privileged:
auto op_cb();
//8-bit load commands
template<uint x, uint y> auto op_ld_r_r();
template<uint x> auto op_ld_r_n();
template<uint x> auto op_ld_r_hl();
template<uint x> auto op_ld_hl_r();
auto op_ld_r_r(uint x, uint y);
auto op_ld_r_n(uint x);
auto op_ld_r_hl(uint x);
auto op_ld_hl_r(uint x);
auto op_ld_hl_n();
template<uint x> auto op_ld_a_rr();
auto op_ld_a_rr(uint x);
auto op_ld_a_nn();
template<uint x> auto op_ld_rr_a();
auto op_ld_rr_a(uint x);
auto op_ld_nn_a();
auto op_ld_a_ffn();
auto op_ld_ffn_a();
@@ -43,64 +44,64 @@ privileged:
auto op_ldd_a_hl();
//16-bit load commands
template<uint x> auto op_ld_rr_nn();
auto op_ld_rr_nn(uint x);
auto op_ld_nn_sp();
auto op_ld_sp_hl();
template<uint x> auto op_push_rr();
template<uint x> auto op_pop_rr();
auto op_push_rr(uint x);
auto op_pop_rr(uint x);
//8-bit arithmetic commands
auto opi_add_a(uint8 x);
template<uint x> auto op_add_a_r();
auto op_add_a_r(uint x);
auto op_add_a_n();
auto op_add_a_hl();
auto opi_adc_a(uint8 x);
template<uint x> auto op_adc_a_r();
auto op_adc_a_r(uint x);
auto op_adc_a_n();
auto op_adc_a_hl();
auto opi_sub_a(uint8 x);
template<uint x> auto op_sub_a_r();
auto op_sub_a_r(uint x);
auto op_sub_a_n();
auto op_sub_a_hl();
auto opi_sbc_a(uint8 x);
template<uint x> auto op_sbc_a_r();
auto op_sbc_a_r(uint x);
auto op_sbc_a_n();
auto op_sbc_a_hl();
auto opi_and_a(uint8 x);
template<uint x> auto op_and_a_r();
auto op_and_a_r(uint x);
auto op_and_a_n();
auto op_and_a_hl();
auto opi_xor_a(uint8 x);
template<uint x> auto op_xor_a_r();
auto op_xor_a_r(uint x);
auto op_xor_a_n();
auto op_xor_a_hl();
auto opi_or_a(uint8 x);
template<uint x> auto op_or_a_r();
auto op_or_a_r(uint x);
auto op_or_a_n();
auto op_or_a_hl();
auto opi_cp_a(uint8 x);
template<uint x> auto op_cp_a_r();
auto op_cp_a_r(uint x);
auto op_cp_a_n();
auto op_cp_a_hl();
template<uint x> auto op_inc_r();
auto op_inc_r(uint x);
auto op_inc_hl();
template<uint x> auto op_dec_r();
auto op_dec_r(uint x);
auto op_dec_hl();
auto op_daa();
auto op_cpl();
//16-bit arithmetic commands
template<uint x> auto op_add_hl_rr();
template<uint x> auto op_inc_rr();
template<uint x> auto op_dec_rr();
auto op_add_hl_rr(uint x);
auto op_inc_rr(uint x);
auto op_dec_rr(uint x);
auto op_add_sp_n();
auto op_ld_hl_sp_n();
@@ -109,30 +110,30 @@ privileged:
auto op_rla();
auto op_rrca();
auto op_rra();
template<uint x> auto op_rlc_r();
auto op_rlc_r(uint x);
auto op_rlc_hl();
template<uint x> auto op_rl_r();
auto op_rl_r(uint x);
auto op_rl_hl();
template<uint x> auto op_rrc_r();
auto op_rrc_r(uint x);
auto op_rrc_hl();
template<uint x> auto op_rr_r();
auto op_rr_r(uint x);
auto op_rr_hl();
template<uint x> auto op_sla_r();
auto op_sla_r(uint x);
auto op_sla_hl();
template<uint x> auto op_swap_r();
auto op_swap_r(uint x);
auto op_swap_hl();
template<uint x> auto op_sra_r();
auto op_sra_r(uint x);
auto op_sra_hl();
template<uint x> auto op_srl_r();
auto op_srl_r(uint x);
auto op_srl_hl();
//single-bit commands
template<uint b, uint x> auto op_bit_n_r();
template<uint b> auto op_bit_n_hl();
template<uint b, uint x> auto op_set_n_r();
template<uint b> auto op_set_n_hl();
template<uint b, uint x> auto op_res_n_r();
template<uint b> auto op_res_n_hl();
auto op_bit_n_r(uint b, uint x);
auto op_bit_n_hl(uint b);
auto op_set_n_r(uint b, uint x);
auto op_set_n_hl(uint b);
auto op_res_n_r(uint b, uint x);
auto op_res_n_hl(uint b);
//control commands
auto op_ccf();
@@ -146,15 +147,15 @@ privileged:
//jump commands
auto op_jp_nn();
auto op_jp_hl();
template<uint x, bool y> auto op_jp_f_nn();
auto op_jp_f_nn(uint x, bool y);
auto op_jr_n();
template<uint x, bool y> auto op_jr_f_n();
auto op_jr_f_n(uint x, bool y);
auto op_call_nn();
template<uint x, bool y> auto op_call_f_nn();
auto op_call_f_nn(uint x, bool y);
auto op_ret();
template<uint x, bool y> auto op_ret_f();
auto op_ret_f(uint x, bool y);
auto op_reti();
template<uint n> auto op_rst_n();
auto op_rst_n(uint n);
//disassembler.cpp
auto disassemble(uint16 pc) -> string;

View File

@@ -99,7 +99,7 @@ struct Registers {
bool ime;
Register& operator[](unsigned r) {
static Register* table[] = {&a, &f, &af, &b, &c, &bc, &d, &e, &de, &h, &l, &hl, &sp, &pc};
static Register* const table[] = {&a, &f, &af, &b, &c, &bc, &d, &e, &de, &h, &l, &hl, &sp, &pc};
return *table[r];
}

View File

@@ -165,9 +165,9 @@ L op_readpc();
}
}
auto R6502::opi_clear_flag(bool& flag) {
auto R6502::opi_clear_flag(uint bit) {
L op_readpc();
flag = 0;
regs.p &= ~(1 << bit);
}
auto R6502::opi_decrement(uint8& r) {
@@ -197,16 +197,14 @@ auto R6502::opi_push(uint8& r) {
L op_writesp(r);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_read_absolute() {
auto R6502::opi_read_absolute(fp op) {
abs.l = op_readpci();
abs.h = op_readpci();
L rd = op_read(abs.w);
call(op);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_read_absolute_x() {
auto R6502::opi_read_absolute_x(fp op) {
abs.l = op_readpci();
abs.h = op_readpci();
op_page(abs.w, abs.w + regs.x);
@@ -214,8 +212,7 @@ L rd = op_read(abs.w + regs.x);
call(op);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_read_absolute_y() {
auto R6502::opi_read_absolute_y(fp op) {
abs.l = op_readpci();
abs.h = op_readpci();
op_page(abs.w, abs.w + regs.y);
@@ -223,14 +220,12 @@ L rd = op_read(abs.w + regs.y);
call(op);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_read_immediate() {
auto R6502::opi_read_immediate(fp op) {
L rd = op_readpci();
call(op);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_read_indirect_zero_page_x() {
auto R6502::opi_read_indirect_zero_page_x(fp op) {
zp = op_readpci();
op_readzp(zp);
abs.l = op_readzp(zp++ + regs.x);
@@ -239,8 +234,7 @@ L rd = op_read(abs.w);
call(op);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_read_indirect_zero_page_y() {
auto R6502::opi_read_indirect_zero_page_y(fp op) {
rd = op_readpci();
abs.l = op_readzp(rd++);
abs.h = op_readzp(rd++);
@@ -249,31 +243,27 @@ L rd = op_read(abs.w + regs.y);
call(op);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_read_zero_page() {
auto R6502::opi_read_zero_page(fp op) {
zp = op_readpci();
L rd = op_readzp(zp);
call(op);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_read_zero_page_x() {
auto R6502::opi_read_zero_page_x(fp op) {
zp = op_readpci();
op_readzp(zp);
L rd = op_readzp(zp + regs.x);
call(op);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_read_zero_page_y() {
auto R6502::opi_read_zero_page_y(fp op) {
zp = op_readpci();
op_readzp(zp);
L rd = op_readzp(zp + regs.y);
call(op);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_rmw_absolute() {
auto R6502::opi_rmw_absolute(fp op) {
abs.l = op_readpci();
abs.h = op_readpci();
rd = op_read(abs.w);
@@ -282,8 +272,7 @@ auto R6502::opi_rmw_absolute() {
L op_write(abs.w, rd);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_rmw_absolute_x() {
auto R6502::opi_rmw_absolute_x(fp op) {
abs.l = op_readpci();
abs.h = op_readpci();
op_page_always(abs.w, abs.w + regs.x);
@@ -293,8 +282,7 @@ auto R6502::opi_rmw_absolute_x() {
L op_write(abs.w + regs.x, rd);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_rmw_zero_page() {
auto R6502::opi_rmw_zero_page(fp op) {
zp = op_readpci();
rd = op_readzp(zp);
op_writezp(zp, rd);
@@ -302,8 +290,7 @@ auto R6502::opi_rmw_zero_page() {
L op_writezp(zp, rd);
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_rmw_zero_page_x() {
auto R6502::opi_rmw_zero_page_x(fp op) {
zp = op_readpci();
op_readzp(zp);
rd = op_readzp(zp + regs.x);
@@ -312,13 +299,12 @@ auto R6502::opi_rmw_zero_page_x() {
L op_writezp(zp + regs.x, rd);
}
auto R6502::opi_set_flag(bool& flag) {
auto R6502::opi_set_flag(uint bit) {
L op_readpc();
flag = 1;
regs.p |= 1 << bit;
}
template<auto (R6502::*op)() -> void>
auto R6502::opi_shift() {
auto R6502::opi_shift(fp op) {
L op_readpc();
call(op);
}

View File

@@ -49,94 +49,94 @@ auto R6502::exec() -> void {
uint8 opcode = op_readpci();
switch(opcode) {
case 0x00: return op_brk();
case 0x01: return opi_read_indirect_zero_page_x<&R6502::opf_ora>();
case 0x01: return opi_read_indirect_zero_page_x(&R6502::opf_ora);
I case 0x04: return opill_nop_zero_page();
case 0x05: return opi_read_zero_page<&R6502::opf_ora>();
case 0x06: return opi_rmw_zero_page<&R6502::opf_asl>();
case 0x05: return opi_read_zero_page(&R6502::opf_ora);
case 0x06: return opi_rmw_zero_page(&R6502::opf_asl);
case 0x08: return op_php();
case 0x09: return opi_read_immediate<&R6502::opf_ora>();
case 0x0a: return opi_shift<&R6502::opf_sla>();
case 0x09: return opi_read_immediate(&R6502::opf_ora);
case 0x0a: return opi_shift(&R6502::opf_sla);
I case 0x0c: return opill_nop_absolute();
case 0x0d: return opi_read_absolute<&R6502::opf_ora>();
case 0x0e: return opi_rmw_absolute<&R6502::opf_asl>();
case 0x0d: return opi_read_absolute(&R6502::opf_ora);
case 0x0e: return opi_rmw_absolute(&R6502::opf_asl);
case 0x10: return opi_branch(regs.p.n == 0);
case 0x11: return opi_read_indirect_zero_page_y<&R6502::opf_ora>();
case 0x11: return opi_read_indirect_zero_page_y(&R6502::opf_ora);
I case 0x14: return opill_nop_zero_page_x();
case 0x15: return opi_read_zero_page_x<&R6502::opf_ora>();
case 0x16: return opi_rmw_zero_page_x<&R6502::opf_asl>();
case 0x18: return opi_clear_flag(regs.p.c);
case 0x19: return opi_read_absolute_y<&R6502::opf_ora>();
case 0x15: return opi_read_zero_page_x(&R6502::opf_ora);
case 0x16: return opi_rmw_zero_page_x(&R6502::opf_asl);
case 0x18: return opi_clear_flag(regs.p.c.bit);
case 0x19: return opi_read_absolute_y(&R6502::opf_ora);
I case 0x1a: return opill_nop_implied();
I case 0x1c: return opill_nop_absolute_x();
case 0x1d: return opi_read_absolute_x<&R6502::opf_ora>();
case 0x1e: return opi_rmw_absolute_x<&R6502::opf_asl>();
case 0x1d: return opi_read_absolute_x(&R6502::opf_ora);
case 0x1e: return opi_rmw_absolute_x(&R6502::opf_asl);
case 0x20: return op_jsr_absolute();
case 0x21: return opi_read_indirect_zero_page_x<&R6502::opf_and>();
case 0x24: return opi_read_zero_page<&R6502::opf_bit>();
case 0x25: return opi_read_zero_page<&R6502::opf_and>();
case 0x26: return opi_rmw_zero_page<&R6502::opf_rol>();
case 0x21: return opi_read_indirect_zero_page_x(&R6502::opf_and);
case 0x24: return opi_read_zero_page(&R6502::opf_bit);
case 0x25: return opi_read_zero_page(&R6502::opf_and);
case 0x26: return opi_rmw_zero_page(&R6502::opf_rol);
case 0x28: return op_plp();
case 0x29: return opi_read_immediate<&R6502::opf_and>();
case 0x2a: return opi_shift<&R6502::opf_rla>();
case 0x2c: return opi_read_absolute<&R6502::opf_bit>();
case 0x2d: return opi_read_absolute<&R6502::opf_and>();
case 0x2e: return opi_rmw_absolute<&R6502::opf_rol>();
case 0x29: return opi_read_immediate(&R6502::opf_and);
case 0x2a: return opi_shift(&R6502::opf_rla);
case 0x2c: return opi_read_absolute(&R6502::opf_bit);
case 0x2d: return opi_read_absolute(&R6502::opf_and);
case 0x2e: return opi_rmw_absolute(&R6502::opf_rol);
case 0x30: return opi_branch(regs.p.n == 1);
case 0x31: return opi_read_indirect_zero_page_y<&R6502::opf_and>();
case 0x31: return opi_read_indirect_zero_page_y(&R6502::opf_and);
I case 0x34: return opill_nop_zero_page_x();
case 0x35: return opi_read_zero_page_x<&R6502::opf_and>();
case 0x36: return opi_rmw_zero_page_x<&R6502::opf_rol>();
case 0x38: return opi_set_flag(regs.p.c);
case 0x39: return opi_read_absolute_y<&R6502::opf_and>();
case 0x35: return opi_read_zero_page_x(&R6502::opf_and);
case 0x36: return opi_rmw_zero_page_x(&R6502::opf_rol);
case 0x38: return opi_set_flag(regs.p.c.bit);
case 0x39: return opi_read_absolute_y(&R6502::opf_and);
I case 0x3a: return opill_nop_implied();
I case 0x3c: return opill_nop_absolute_x();
case 0x3d: return opi_read_absolute_x<&R6502::opf_and>();
case 0x3e: return opi_rmw_absolute_x<&R6502::opf_rol>();
case 0x3d: return opi_read_absolute_x(&R6502::opf_and);
case 0x3e: return opi_rmw_absolute_x(&R6502::opf_rol);
case 0x40: return op_rti();
case 0x41: return opi_read_indirect_zero_page_x<&R6502::opf_eor>();
case 0x41: return opi_read_indirect_zero_page_x(&R6502::opf_eor);
I case 0x44: return opill_nop_zero_page();
case 0x45: return opi_read_zero_page<&R6502::opf_eor>();
case 0x46: return opi_rmw_zero_page<&R6502::opf_lsr>();
case 0x45: return opi_read_zero_page(&R6502::opf_eor);
case 0x46: return opi_rmw_zero_page(&R6502::opf_lsr);
case 0x48: return opi_push(regs.a);
case 0x49: return opi_read_immediate<&R6502::opf_eor>();
case 0x4a: return opi_shift<&R6502::opf_sra>();
case 0x49: return opi_read_immediate(&R6502::opf_eor);
case 0x4a: return opi_shift(&R6502::opf_sra);
case 0x4c: return op_jmp_absolute();
case 0x4d: return opi_read_absolute<&R6502::opf_eor>();
case 0x4e: return opi_rmw_absolute<&R6502::opf_lsr>();
case 0x4d: return opi_read_absolute(&R6502::opf_eor);
case 0x4e: return opi_rmw_absolute(&R6502::opf_lsr);
case 0x50: return opi_branch(regs.p.v == 0);
case 0x51: return opi_read_indirect_zero_page_y<&R6502::opf_eor>();
case 0x51: return opi_read_indirect_zero_page_y(&R6502::opf_eor);
I case 0x54: return opill_nop_zero_page_x();
case 0x55: return opi_read_zero_page_x<&R6502::opf_eor>();
case 0x56: return opi_rmw_zero_page_x<&R6502::opf_lsr>();
case 0x58: return opi_clear_flag(regs.p.i);
case 0x59: return opi_read_absolute_y<&R6502::opf_eor>();
case 0x55: return opi_read_zero_page_x(&R6502::opf_eor);
case 0x56: return opi_rmw_zero_page_x(&R6502::opf_lsr);
case 0x58: return opi_clear_flag(regs.p.i.bit);
case 0x59: return opi_read_absolute_y(&R6502::opf_eor);
I case 0x5a: return opill_nop_implied();
I case 0x5c: return opill_nop_absolute_x();
case 0x5d: return opi_read_absolute_x<&R6502::opf_eor>();
case 0x5e: return opi_rmw_absolute_x<&R6502::opf_lsr>();
case 0x5d: return opi_read_absolute_x(&R6502::opf_eor);
case 0x5e: return opi_rmw_absolute_x(&R6502::opf_lsr);
case 0x60: return op_rts();
case 0x61: return opi_read_indirect_zero_page_x<&R6502::opf_adc>();
case 0x61: return opi_read_indirect_zero_page_x(&R6502::opf_adc);
I case 0x64: return opill_nop_zero_page();
case 0x65: return opi_read_zero_page<&R6502::opf_adc>();
case 0x66: return opi_rmw_zero_page<&R6502::opf_ror>();
case 0x65: return opi_read_zero_page(&R6502::opf_adc);
case 0x66: return opi_rmw_zero_page(&R6502::opf_ror);
case 0x68: return opi_pull(regs.a);
case 0x69: return opi_read_immediate<&R6502::opf_adc>();
case 0x6a: return opi_shift<&R6502::opf_rra>();
case 0x69: return opi_read_immediate(&R6502::opf_adc);
case 0x6a: return opi_shift(&R6502::opf_rra);
I case 0x6b: return opill_arr_immediate();
case 0x6c: return op_jmp_indirect_absolute();
case 0x6d: return opi_read_absolute<&R6502::opf_adc>();
case 0x6e: return opi_rmw_absolute<&R6502::opf_ror>();
case 0x6d: return opi_read_absolute(&R6502::opf_adc);
case 0x6e: return opi_rmw_absolute(&R6502::opf_ror);
case 0x70: return opi_branch(regs.p.v == 1);
I case 0x74: return opill_nop_zero_page_x();
case 0x71: return opi_read_indirect_zero_page_y<&R6502::opf_adc>();
case 0x75: return opi_read_zero_page_x<&R6502::opf_adc>();
case 0x76: return opi_rmw_zero_page_x<&R6502::opf_ror>();
case 0x78: return opi_set_flag(regs.p.i);
case 0x79: return opi_read_absolute_y<&R6502::opf_adc>();
case 0x71: return opi_read_indirect_zero_page_y(&R6502::opf_adc);
case 0x75: return opi_read_zero_page_x(&R6502::opf_adc);
case 0x76: return opi_rmw_zero_page_x(&R6502::opf_ror);
case 0x78: return opi_set_flag(regs.p.i.bit);
case 0x79: return opi_read_absolute_y(&R6502::opf_adc);
I case 0x7a: return opill_nop_implied();
I case 0x7c: return opill_nop_absolute_x();
case 0x7d: return opi_read_absolute_x<&R6502::opf_adc>();
case 0x7e: return opi_rmw_absolute_x<&R6502::opf_ror>();
case 0x7d: return opi_read_absolute_x(&R6502::opf_adc);
case 0x7e: return opi_rmw_absolute_x(&R6502::opf_ror);
I case 0x80: return opill_nop_absolute();
case 0x81: return opi_store_indirect_zero_page_x(regs.a);
I case 0x82: return opill_nop_immediate();
@@ -158,76 +158,76 @@ I case 0x89: return opill_nop_immediate();
case 0x99: return opi_store_absolute_y(regs.a);
case 0x9a: return opi_transfer(regs.x, regs.s, 0);
case 0x9d: return opi_store_absolute_x(regs.a);
case 0xa0: return opi_read_immediate<&R6502::opf_ldy>();
case 0xa1: return opi_read_indirect_zero_page_x<&R6502::opf_lda>();
case 0xa2: return opi_read_immediate<&R6502::opf_ldx>();
case 0xa4: return opi_read_zero_page<&R6502::opf_ldy>();
case 0xa5: return opi_read_zero_page<&R6502::opf_lda>();
case 0xa6: return opi_read_zero_page<&R6502::opf_ldx>();
case 0xa0: return opi_read_immediate(&R6502::opf_ldy);
case 0xa1: return opi_read_indirect_zero_page_x(&R6502::opf_lda);
case 0xa2: return opi_read_immediate(&R6502::opf_ldx);
case 0xa4: return opi_read_zero_page(&R6502::opf_ldy);
case 0xa5: return opi_read_zero_page(&R6502::opf_lda);
case 0xa6: return opi_read_zero_page(&R6502::opf_ldx);
case 0xa8: return opi_transfer(regs.a, regs.y, 1);
case 0xa9: return opi_read_immediate<&R6502::opf_lda>();
case 0xa9: return opi_read_immediate(&R6502::opf_lda);
case 0xaa: return opi_transfer(regs.a, regs.x, 1);
case 0xac: return opi_read_absolute<&R6502::opf_ldy>();
case 0xad: return opi_read_absolute<&R6502::opf_lda>();
case 0xae: return opi_read_absolute<&R6502::opf_ldx>();
case 0xac: return opi_read_absolute(&R6502::opf_ldy);
case 0xad: return opi_read_absolute(&R6502::opf_lda);
case 0xae: return opi_read_absolute(&R6502::opf_ldx);
case 0xb0: return opi_branch(regs.p.c == 1);
case 0xb1: return opi_read_indirect_zero_page_y<&R6502::opf_lda>();
case 0xb4: return opi_read_zero_page_x<&R6502::opf_ldy>();
case 0xb5: return opi_read_zero_page_x<&R6502::opf_lda>();
case 0xb6: return opi_read_zero_page_y<&R6502::opf_ldx>();
case 0xb8: return opi_clear_flag(regs.p.v);
case 0xb9: return opi_read_absolute_y<&R6502::opf_lda>();
case 0xb1: return opi_read_indirect_zero_page_y(&R6502::opf_lda);
case 0xb4: return opi_read_zero_page_x(&R6502::opf_ldy);
case 0xb5: return opi_read_zero_page_x(&R6502::opf_lda);
case 0xb6: return opi_read_zero_page_y(&R6502::opf_ldx);
case 0xb8: return opi_clear_flag(regs.p.v.bit);
case 0xb9: return opi_read_absolute_y(&R6502::opf_lda);
case 0xba: return opi_transfer(regs.s, regs.x, 1);
case 0xbc: return opi_read_absolute_x<&R6502::opf_ldy>();
case 0xbd: return opi_read_absolute_x<&R6502::opf_lda>();
case 0xbe: return opi_read_absolute_y<&R6502::opf_ldx>();
case 0xc0: return opi_read_immediate<&R6502::opf_cpy>();
case 0xc1: return opi_read_indirect_zero_page_x<&R6502::opf_cmp>();
case 0xbc: return opi_read_absolute_x(&R6502::opf_ldy);
case 0xbd: return opi_read_absolute_x(&R6502::opf_lda);
case 0xbe: return opi_read_absolute_y(&R6502::opf_ldx);
case 0xc0: return opi_read_immediate(&R6502::opf_cpy);
case 0xc1: return opi_read_indirect_zero_page_x(&R6502::opf_cmp);
I case 0xc2: return opill_nop_immediate();
case 0xc4: return opi_read_zero_page<&R6502::opf_cpy>();
case 0xc5: return opi_read_zero_page<&R6502::opf_cmp>();
case 0xc6: return opi_rmw_zero_page<&R6502::opf_dec>();
case 0xc4: return opi_read_zero_page(&R6502::opf_cpy);
case 0xc5: return opi_read_zero_page(&R6502::opf_cmp);
case 0xc6: return opi_rmw_zero_page(&R6502::opf_dec);
case 0xc8: return opi_increment(regs.y);
case 0xc9: return opi_read_immediate<&R6502::opf_cmp>();
case 0xc9: return opi_read_immediate(&R6502::opf_cmp);
case 0xca: return opi_decrement(regs.x);
case 0xcc: return opi_read_absolute<&R6502::opf_cpy>();
case 0xcd: return opi_read_absolute<&R6502::opf_cmp>();
case 0xce: return opi_rmw_absolute<&R6502::opf_dec>();
case 0xcc: return opi_read_absolute(&R6502::opf_cpy);
case 0xcd: return opi_read_absolute(&R6502::opf_cmp);
case 0xce: return opi_rmw_absolute(&R6502::opf_dec);
case 0xd0: return opi_branch(regs.p.z == 0);
case 0xd1: return opi_read_indirect_zero_page_y<&R6502::opf_cmp>();
case 0xd1: return opi_read_indirect_zero_page_y(&R6502::opf_cmp);
I case 0xd4: return opill_nop_zero_page_x();
case 0xd5: return opi_read_zero_page_x<&R6502::opf_cmp>();
case 0xd6: return opi_rmw_zero_page_x<&R6502::opf_dec>();
case 0xd8: return opi_clear_flag(regs.p.d);
case 0xd9: return opi_read_absolute_y<&R6502::opf_cmp>();
case 0xd5: return opi_read_zero_page_x(&R6502::opf_cmp);
case 0xd6: return opi_rmw_zero_page_x(&R6502::opf_dec);
case 0xd8: return opi_clear_flag(regs.p.d.bit);
case 0xd9: return opi_read_absolute_y(&R6502::opf_cmp);
I case 0xda: return opill_nop_implied();
I case 0xdc: return opill_nop_absolute_x();
case 0xdd: return opi_read_absolute_x<&R6502::opf_cmp>();
case 0xde: return opi_rmw_absolute_x<&R6502::opf_dec>();
case 0xe0: return opi_read_immediate<&R6502::opf_cpx>();
case 0xe1: return opi_read_indirect_zero_page_x<&R6502::opf_sbc>();
case 0xdd: return opi_read_absolute_x(&R6502::opf_cmp);
case 0xde: return opi_rmw_absolute_x(&R6502::opf_dec);
case 0xe0: return opi_read_immediate(&R6502::opf_cpx);
case 0xe1: return opi_read_indirect_zero_page_x(&R6502::opf_sbc);
I case 0xe2: return opill_nop_immediate();
case 0xe4: return opi_read_zero_page<&R6502::opf_cpx>();
case 0xe5: return opi_read_zero_page<&R6502::opf_sbc>();
case 0xe6: return opi_rmw_zero_page<&R6502::opf_inc>();
case 0xe4: return opi_read_zero_page(&R6502::opf_cpx);
case 0xe5: return opi_read_zero_page(&R6502::opf_sbc);
case 0xe6: return opi_rmw_zero_page(&R6502::opf_inc);
case 0xe8: return opi_increment(regs.x);
case 0xe9: return opi_read_immediate<&R6502::opf_sbc>();
case 0xe9: return opi_read_immediate(&R6502::opf_sbc);
case 0xea: return op_nop();
I case 0xeb: return opi_read_immediate<&R6502::opf_sbc>();
case 0xec: return opi_read_absolute<&R6502::opf_cpx>();
case 0xed: return opi_read_absolute<&R6502::opf_sbc>();
case 0xee: return opi_rmw_absolute<&R6502::opf_inc>();
I case 0xeb: return opi_read_immediate(&R6502::opf_sbc);
case 0xec: return opi_read_absolute(&R6502::opf_cpx);
case 0xed: return opi_read_absolute(&R6502::opf_sbc);
case 0xee: return opi_rmw_absolute(&R6502::opf_inc);
case 0xf0: return opi_branch(regs.p.z == 1);
case 0xf1: return opi_read_indirect_zero_page_y<&R6502::opf_sbc>();
case 0xf1: return opi_read_indirect_zero_page_y(&R6502::opf_sbc);
I case 0xf4: return opill_nop_zero_page_x();
case 0xf5: return opi_read_zero_page_x<&R6502::opf_sbc>();
case 0xf6: return opi_rmw_zero_page_x<&R6502::opf_inc>();
case 0xf8: return opi_set_flag(regs.p.d);
case 0xf9: return opi_read_absolute_y<&R6502::opf_sbc>();
case 0xf5: return opi_read_zero_page_x(&R6502::opf_sbc);
case 0xf6: return opi_rmw_zero_page_x(&R6502::opf_inc);
case 0xf8: return opi_set_flag(regs.p.d.bit);
case 0xf9: return opi_read_absolute_y(&R6502::opf_sbc);
I case 0xfa: return opill_nop_implied();
I case 0xfc: return opill_nop_absolute_x();
case 0xfd: return opi_read_absolute_x<&R6502::opf_sbc>();
case 0xfe: return opi_rmw_absolute_x<&R6502::opf_inc>();
case 0xfd: return opi_read_absolute_x(&R6502::opf_sbc);
case 0xfe: return opi_rmw_absolute_x(&R6502::opf_inc);
}
//unimplemented opcode

View File

@@ -57,27 +57,29 @@ struct R6502 {
auto opf_sla();
auto opf_sra();
using fp = auto (R6502::*)() -> void;
auto opi_branch(bool condition);
auto opi_clear_flag(bool& flag);
auto opi_clear_flag(uint bit);
auto opi_decrement(uint8& r);
auto opi_increment(uint8& r);
auto opi_pull(uint8& r);
auto opi_push(uint8& r);
template<auto (R6502::*op)() -> void> auto opi_read_absolute();
template<auto (R6502::*op)() -> void> auto opi_read_absolute_x();
template<auto (R6502::*op)() -> void> auto opi_read_absolute_y();
template<auto (R6502::*op)() -> void> auto opi_read_immediate();
template<auto (R6502::*op)() -> void> auto opi_read_indirect_zero_page_x();
template<auto (R6502::*op)() -> void> auto opi_read_indirect_zero_page_y();
template<auto (R6502::*op)() -> void> auto opi_read_zero_page();
template<auto (R6502::*op)() -> void> auto opi_read_zero_page_x();
template<auto (R6502::*op)() -> void> auto opi_read_zero_page_y();
template<auto (R6502::*op)() -> void> auto opi_rmw_absolute();
template<auto (R6502::*op)() -> void> auto opi_rmw_absolute_x();
template<auto (R6502::*op)() -> void> auto opi_rmw_zero_page();
template<auto (R6502::*op)() -> void> auto opi_rmw_zero_page_x();
auto opi_set_flag(bool& flag);
template<auto (R6502::*op)() -> void> auto opi_shift();
auto opi_read_absolute(fp);
auto opi_read_absolute_x(fp);
auto opi_read_absolute_y(fp);
auto opi_read_immediate(fp);
auto opi_read_indirect_zero_page_x(fp);
auto opi_read_indirect_zero_page_y(fp);
auto opi_read_zero_page(fp);
auto opi_read_zero_page_x(fp);
auto opi_read_zero_page_y(fp);
auto opi_rmw_absolute(fp);
auto opi_rmw_absolute_x(fp);
auto opi_rmw_zero_page(fp);
auto opi_rmw_zero_page_x(fp);
auto opi_set_flag(uint bit);
auto opi_shift(fp);
auto opi_store_absolute(uint8& r);
auto opi_store_absolute_x(uint8& r);
auto opi_store_absolute_y(uint8& r);

View File

@@ -1,15 +1,19 @@
struct Flags {
inline operator uint() {
return (n << 7) | (v << 6) | (d << 3) | (i << 2) | (z << 1) | (c << 0);
}
union {
uint8_t data = 0;
BitField<uint8_t, 7> n;
BitField<uint8_t, 6> v;
BitField<uint8_t, 3> d;
BitField<uint8_t, 2> i;
BitField<uint8_t, 1> z;
BitField<uint8_t, 0> c;
};
inline auto operator=(uint8 data) -> Flags& {
n = data & 0x80; v = data & 0x40;
d = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01;
return *this;
}
bool n, v, d, i, z, c;
inline operator uint() { return data; }
inline auto& operator =(uint value) { return data = value, *this; }
inline auto& operator&=(uint value) { return data &= value, *this; }
inline auto& operator|=(uint value) { return data |= value, *this; }
inline auto& operator^=(uint value) { return data ^= value, *this; }
};
struct Registers {
@@ -22,7 +26,8 @@ struct Registers {
struct Register16 {
union {
uint16_t w;
struct { uint8_t order_lsb2(l, h); };
BitField<uint16_t, 0, 7> l;
BitField<uint16_t, 8, 15> h;
};
} abs, iabs;

View File

@@ -5,12 +5,7 @@ auto R6502::serialize(serializer& s) -> void {
s.integer(regs.x);
s.integer(regs.y);
s.integer(regs.s);
s.integer(regs.p.n);
s.integer(regs.p.v);
s.integer(regs.p.d);
s.integer(regs.p.i);
s.integer(regs.p.z);
s.integer(regs.p.c);
s.integer(regs.p.data);
s.integer(abs.w);
s.integer(iabs.w);

View File

@@ -1,327 +1,327 @@
auto R65816::op_adc_b() {
int result;
if(!regs.p.d) {
result = regs.a.l + rd.l + regs.p.c;
if(!r.p.d) {
result = r.a.l + rd.l + r.p.c;
} else {
result = (regs.a.l & 0x0f) + (rd.l & 0x0f) + (regs.p.c << 0);
result = (r.a.l & 0x0f) + (rd.l & 0x0f) + (r.p.c << 0);
if(result > 0x09) result += 0x06;
regs.p.c = result > 0x0f;
result = (regs.a.l & 0xf0) + (rd.l & 0xf0) + (regs.p.c << 4) + (result & 0x0f);
r.p.c = result > 0x0f;
result = (r.a.l & 0xf0) + (rd.l & 0xf0) + (r.p.c << 4) + (result & 0x0f);
}
regs.p.v = ~(regs.a.l ^ rd.l) & (regs.a.l ^ result) & 0x80;
if(regs.p.d && result > 0x9f) result += 0x60;
regs.p.c = result > 0xff;
regs.p.n = result & 0x80;
regs.p.z = (uint8_t)result == 0;
r.p.v = ~(r.a.l ^ rd.l) & (r.a.l ^ result) & 0x80;
if(r.p.d && result > 0x9f) result += 0x60;
r.p.c = result > 0xff;
r.p.n = result & 0x80;
r.p.z = (uint8)result == 0;
regs.a.l = result;
r.a.l = result;
}
auto R65816::op_adc_w() {
int result;
if(!regs.p.d) {
result = regs.a.w + rd.w + regs.p.c;
if(!r.p.d) {
result = r.a.w + rd.w + r.p.c;
} else {
result = (regs.a.w & 0x000f) + (rd.w & 0x000f) + (regs.p.c << 0);
result = (r.a.w & 0x000f) + (rd.w & 0x000f) + (r.p.c << 0);
if(result > 0x0009) result += 0x0006;
regs.p.c = result > 0x000f;
result = (regs.a.w & 0x00f0) + (rd.w & 0x00f0) + (regs.p.c << 4) + (result & 0x000f);
r.p.c = result > 0x000f;
result = (r.a.w & 0x00f0) + (rd.w & 0x00f0) + (r.p.c << 4) + (result & 0x000f);
if(result > 0x009f) result += 0x0060;
regs.p.c = result > 0x00ff;
result = (regs.a.w & 0x0f00) + (rd.w & 0x0f00) + (regs.p.c << 8) + (result & 0x00ff);
r.p.c = result > 0x00ff;
result = (r.a.w & 0x0f00) + (rd.w & 0x0f00) + (r.p.c << 8) + (result & 0x00ff);
if(result > 0x09ff) result += 0x0600;
regs.p.c = result > 0x0fff;
result = (regs.a.w & 0xf000) + (rd.w & 0xf000) + (regs.p.c << 12) + (result & 0x0fff);
r.p.c = result > 0x0fff;
result = (r.a.w & 0xf000) + (rd.w & 0xf000) + (r.p.c << 12) + (result & 0x0fff);
}
regs.p.v = ~(regs.a.w ^ rd.w) & (regs.a.w ^ result) & 0x8000;
if(regs.p.d && result > 0x9fff) result += 0x6000;
regs.p.c = result > 0xffff;
regs.p.n = result & 0x8000;
regs.p.z = (uint16_t)result == 0;
r.p.v = ~(r.a.w ^ rd.w) & (r.a.w ^ result) & 0x8000;
if(r.p.d && result > 0x9fff) result += 0x6000;
r.p.c = result > 0xffff;
r.p.n = result & 0x8000;
r.p.z = (uint16)result == 0;
regs.a.w = result;
r.a.w = result;
}
auto R65816::op_and_b() {
regs.a.l &= rd.l;
regs.p.n = regs.a.l & 0x80;
regs.p.z = regs.a.l == 0;
r.a.l &= rd.l;
r.p.n = r.a.l & 0x80;
r.p.z = r.a.l == 0;
}
auto R65816::op_and_w() {
regs.a.w &= rd.w;
regs.p.n = regs.a.w & 0x8000;
regs.p.z = regs.a.w == 0;
r.a.w &= rd.w;
r.p.n = r.a.w & 0x8000;
r.p.z = r.a.w == 0;
}
auto R65816::op_bit_b() {
regs.p.n = rd.l & 0x80;
regs.p.v = rd.l & 0x40;
regs.p.z = (rd.l & regs.a.l) == 0;
r.p.n = rd.l & 0x80;
r.p.v = rd.l & 0x40;
r.p.z = (rd.l & r.a.l) == 0;
}
auto R65816::op_bit_w() {
regs.p.n = rd.w & 0x8000;
regs.p.v = rd.w & 0x4000;
regs.p.z = (rd.w & regs.a.w) == 0;
r.p.n = rd.w & 0x8000;
r.p.v = rd.w & 0x4000;
r.p.z = (rd.w & r.a.w) == 0;
}
auto R65816::op_cmp_b() {
int r = regs.a.l - rd.l;
regs.p.n = r & 0x80;
regs.p.z = (uint8)r == 0;
regs.p.c = r >= 0;
int result = r.a.l - rd.l;
r.p.n = result & 0x80;
r.p.z = (uint8)result == 0;
r.p.c = result >= 0;
}
auto R65816::op_cmp_w() {
int r = regs.a.w - rd.w;
regs.p.n = r & 0x8000;
regs.p.z = (uint16)r == 0;
regs.p.c = r >= 0;
int result = r.a.w - rd.w;
r.p.n = result & 0x8000;
r.p.z = (uint16)result == 0;
r.p.c = result >= 0;
}
auto R65816::op_cpx_b() {
int r = regs.x.l - rd.l;
regs.p.n = r & 0x80;
regs.p.z = (uint8)r == 0;
regs.p.c = r >= 0;
int result = r.x.l - rd.l;
r.p.n = result & 0x80;
r.p.z = (uint8)result == 0;
r.p.c = result >= 0;
}
auto R65816::op_cpx_w() {
int r = regs.x.w - rd.w;
regs.p.n = r & 0x8000;
regs.p.z = (uint16)r == 0;
regs.p.c = r >= 0;
int result = r.x.w - rd.w;
r.p.n = result & 0x8000;
r.p.z = (uint16)result == 0;
r.p.c = result >= 0;
}
auto R65816::op_cpy_b() {
int r = regs.y.l - rd.l;
regs.p.n = r & 0x80;
regs.p.z = (uint8)r == 0;
regs.p.c = r >= 0;
int result = r.y.l - rd.l;
r.p.n = result & 0x80;
r.p.z = (uint8)result == 0;
r.p.c = result >= 0;
}
auto R65816::op_cpy_w() {
int r = regs.y.w - rd.w;
regs.p.n = r & 0x8000;
regs.p.z = (uint16)r == 0;
regs.p.c = r >= 0;
int result = r.y.w - rd.w;
r.p.n = result & 0x8000;
r.p.z = (uint16)result == 0;
r.p.c = result >= 0;
}
auto R65816::op_eor_b() {
regs.a.l ^= rd.l;
regs.p.n = regs.a.l & 0x80;
regs.p.z = regs.a.l == 0;
r.a.l ^= rd.l;
r.p.n = r.a.l & 0x80;
r.p.z = r.a.l == 0;
}
auto R65816::op_eor_w() {
regs.a.w ^= rd.w;
regs.p.n = regs.a.w & 0x8000;
regs.p.z = regs.a.w == 0;
r.a.w ^= rd.w;
r.p.n = r.a.w & 0x8000;
r.p.z = r.a.w == 0;
}
auto R65816::op_lda_b() {
regs.a.l = rd.l;
regs.p.n = regs.a.l & 0x80;
regs.p.z = regs.a.l == 0;
r.a.l = rd.l;
r.p.n = r.a.l & 0x80;
r.p.z = r.a.l == 0;
}
auto R65816::op_lda_w() {
regs.a.w = rd.w;
regs.p.n = regs.a.w & 0x8000;
regs.p.z = regs.a.w == 0;
r.a.w = rd.w;
r.p.n = r.a.w & 0x8000;
r.p.z = r.a.w == 0;
}
auto R65816::op_ldx_b() {
regs.x.l = rd.l;
regs.p.n = regs.x.l & 0x80;
regs.p.z = regs.x.l == 0;
r.x.l = rd.l;
r.p.n = r.x.l & 0x80;
r.p.z = r.x.l == 0;
}
auto R65816::op_ldx_w() {
regs.x.w = rd.w;
regs.p.n = regs.x.w & 0x8000;
regs.p.z = regs.x.w == 0;
r.x.w = rd.w;
r.p.n = r.x.w & 0x8000;
r.p.z = r.x.w == 0;
}
auto R65816::op_ldy_b() {
regs.y.l = rd.l;
regs.p.n = regs.y.l & 0x80;
regs.p.z = regs.y.l == 0;
r.y.l = rd.l;
r.p.n = r.y.l & 0x80;
r.p.z = r.y.l == 0;
}
auto R65816::op_ldy_w() {
regs.y.w = rd.w;
regs.p.n = regs.y.w & 0x8000;
regs.p.z = regs.y.w == 0;
r.y.w = rd.w;
r.p.n = r.y.w & 0x8000;
r.p.z = r.y.w == 0;
}
auto R65816::op_ora_b() {
regs.a.l |= rd.l;
regs.p.n = regs.a.l & 0x80;
regs.p.z = regs.a.l == 0;
r.a.l |= rd.l;
r.p.n = r.a.l & 0x80;
r.p.z = r.a.l == 0;
}
auto R65816::op_ora_w() {
regs.a.w |= rd.w;
regs.p.n = regs.a.w & 0x8000;
regs.p.z = regs.a.w == 0;
r.a.w |= rd.w;
r.p.n = r.a.w & 0x8000;
r.p.z = r.a.w == 0;
}
auto R65816::op_sbc_b() {
int result;
rd.l ^= 0xff;
if(!regs.p.d) {
result = regs.a.l + rd.l + regs.p.c;
if(!r.p.d) {
result = r.a.l + rd.l + r.p.c;
} else {
result = (regs.a.l & 0x0f) + (rd.l & 0x0f) + (regs.p.c << 0);
result = (r.a.l & 0x0f) + (rd.l & 0x0f) + (r.p.c << 0);
if(result <= 0x0f) result -= 0x06;
regs.p.c = result > 0x0f;
result = (regs.a.l & 0xf0) + (rd.l & 0xf0) + (regs.p.c << 4) + (result & 0x0f);
r.p.c = result > 0x0f;
result = (r.a.l & 0xf0) + (rd.l & 0xf0) + (r.p.c << 4) + (result & 0x0f);
}
regs.p.v = ~(regs.a.l ^ rd.l) & (regs.a.l ^ result) & 0x80;
if(regs.p.d && result <= 0xff) result -= 0x60;
regs.p.c = result > 0xff;
regs.p.n = result & 0x80;
regs.p.z = (uint8_t)result == 0;
r.p.v = ~(r.a.l ^ rd.l) & (r.a.l ^ result) & 0x80;
if(r.p.d && result <= 0xff) result -= 0x60;
r.p.c = result > 0xff;
r.p.n = result & 0x80;
r.p.z = (uint8_t)result == 0;
regs.a.l = result;
r.a.l = result;
}
auto R65816::op_sbc_w() {
int result;
rd.w ^= 0xffff;
if(!regs.p.d) {
result = regs.a.w + rd.w + regs.p.c;
if(!r.p.d) {
result = r.a.w + rd.w + r.p.c;
} else {
result = (regs.a.w & 0x000f) + (rd.w & 0x000f) + (regs.p.c << 0);
result = (r.a.w & 0x000f) + (rd.w & 0x000f) + (r.p.c << 0);
if(result <= 0x000f) result -= 0x0006;
regs.p.c = result > 0x000f;
result = (regs.a.w & 0x00f0) + (rd.w & 0x00f0) + (regs.p.c << 4) + (result & 0x000f);
r.p.c = result > 0x000f;
result = (r.a.w & 0x00f0) + (rd.w & 0x00f0) + (r.p.c << 4) + (result & 0x000f);
if(result <= 0x00ff) result -= 0x0060;
regs.p.c = result > 0x00ff;
result = (regs.a.w & 0x0f00) + (rd.w & 0x0f00) + (regs.p.c << 8) + (result & 0x00ff);
r.p.c = result > 0x00ff;
result = (r.a.w & 0x0f00) + (rd.w & 0x0f00) + (r.p.c << 8) + (result & 0x00ff);
if(result <= 0x0fff) result -= 0x0600;
regs.p.c = result > 0x0fff;
result = (regs.a.w & 0xf000) + (rd.w & 0xf000) + (regs.p.c << 12) + (result & 0x0fff);
r.p.c = result > 0x0fff;
result = (r.a.w & 0xf000) + (rd.w & 0xf000) + (r.p.c << 12) + (result & 0x0fff);
}
regs.p.v = ~(regs.a.w ^ rd.w) & (regs.a.w ^ result) & 0x8000;
if(regs.p.d && result <= 0xffff) result -= 0x6000;
regs.p.c = result > 0xffff;
regs.p.n = result & 0x8000;
regs.p.z = (uint16_t)result == 0;
r.p.v = ~(r.a.w ^ rd.w) & (r.a.w ^ result) & 0x8000;
if(r.p.d && result <= 0xffff) result -= 0x6000;
r.p.c = result > 0xffff;
r.p.n = result & 0x8000;
r.p.z = (uint16_t)result == 0;
regs.a.w = result;
r.a.w = result;
}
auto R65816::op_inc_b() {
rd.l++;
regs.p.n = rd.l & 0x80;
regs.p.z = rd.l == 0;
r.p.n = rd.l & 0x80;
r.p.z = rd.l == 0;
}
auto R65816::op_inc_w() {
rd.w++;
regs.p.n = rd.w & 0x8000;
regs.p.z = rd.w == 0;
r.p.n = rd.w & 0x8000;
r.p.z = rd.w == 0;
}
auto R65816::op_dec_b() {
rd.l--;
regs.p.n = rd.l & 0x80;
regs.p.z = rd.l == 0;
r.p.n = rd.l & 0x80;
r.p.z = rd.l == 0;
}
auto R65816::op_dec_w() {
rd.w--;
regs.p.n = rd.w & 0x8000;
regs.p.z = rd.w == 0;
r.p.n = rd.w & 0x8000;
r.p.z = rd.w == 0;
}
auto R65816::op_asl_b() {
regs.p.c = rd.l & 0x80;
r.p.c = rd.l & 0x80;
rd.l <<= 1;
regs.p.n = rd.l & 0x80;
regs.p.z = rd.l == 0;
r.p.n = rd.l & 0x80;
r.p.z = rd.l == 0;
}
auto R65816::op_asl_w() {
regs.p.c = rd.w & 0x8000;
r.p.c = rd.w & 0x8000;
rd.w <<= 1;
regs.p.n = rd.w & 0x8000;
regs.p.z = rd.w == 0;
r.p.n = rd.w & 0x8000;
r.p.z = rd.w == 0;
}
auto R65816::op_lsr_b() {
regs.p.c = rd.l & 1;
r.p.c = rd.l & 1;
rd.l >>= 1;
regs.p.n = rd.l & 0x80;
regs.p.z = rd.l == 0;
r.p.n = rd.l & 0x80;
r.p.z = rd.l == 0;
}
auto R65816::op_lsr_w() {
regs.p.c = rd.w & 1;
r.p.c = rd.w & 1;
rd.w >>= 1;
regs.p.n = rd.w & 0x8000;
regs.p.z = rd.w == 0;
r.p.n = rd.w & 0x8000;
r.p.z = rd.w == 0;
}
auto R65816::op_rol_b() {
unsigned carry = (unsigned)regs.p.c;
regs.p.c = rd.l & 0x80;
auto carry = (uint)r.p.c;
r.p.c = rd.l & 0x80;
rd.l = (rd.l << 1) | carry;
regs.p.n = rd.l & 0x80;
regs.p.z = rd.l == 0;
r.p.n = rd.l & 0x80;
r.p.z = rd.l == 0;
}
auto R65816::op_rol_w() {
unsigned carry = (unsigned)regs.p.c;
regs.p.c = rd.w & 0x8000;
auto carry = (uint)r.p.c;
r.p.c = rd.w & 0x8000;
rd.w = (rd.w << 1) | carry;
regs.p.n = rd.w & 0x8000;
regs.p.z = rd.w == 0;
r.p.n = rd.w & 0x8000;
r.p.z = rd.w == 0;
}
auto R65816::op_ror_b() {
unsigned carry = (unsigned)regs.p.c << 7;
regs.p.c = rd.l & 1;
auto carry = (uint)r.p.c << 7;
r.p.c = rd.l & 1;
rd.l = carry | (rd.l >> 1);
regs.p.n = rd.l & 0x80;
regs.p.z = rd.l == 0;
r.p.n = rd.l & 0x80;
r.p.z = rd.l == 0;
}
auto R65816::op_ror_w() {
unsigned carry = (unsigned)regs.p.c << 15;
regs.p.c = rd.w & 1;
auto carry = (uint)r.p.c << 15;
r.p.c = rd.w & 1;
rd.w = carry | (rd.w >> 1);
regs.p.n = rd.w & 0x8000;
regs.p.z = rd.w == 0;
r.p.n = rd.w & 0x8000;
r.p.z = rd.w == 0;
}
auto R65816::op_trb_b() {
regs.p.z = (rd.l & regs.a.l) == 0;
rd.l &= ~regs.a.l;
r.p.z = (rd.l & r.a.l) == 0;
rd.l &= ~r.a.l;
}
auto R65816::op_trb_w() {
regs.p.z = (rd.w & regs.a.w) == 0;
rd.w &= ~regs.a.w;
r.p.z = (rd.w & r.a.w) == 0;
rd.w &= ~r.a.w;
}
auto R65816::op_tsb_b() {
regs.p.z = (rd.l & regs.a.l) == 0;
rd.l |= regs.a.l;
r.p.z = (rd.l & r.a.l) == 0;
rd.l |= r.a.l;
}
auto R65816::op_tsb_w() {
regs.p.z = (rd.w & regs.a.w) == 0;
rd.w |= regs.a.w;
r.p.z = (rd.w & r.a.w) == 0;
rd.w |= r.a.w;
}

View File

@@ -23,93 +23,93 @@ auto R65816::dreadl(uint24 addr) -> uint24 {
}
auto R65816::decode(uint8 mode, uint24 addr) -> uint24 {
uint24 r = 0;
uint24 a = 0;
switch(mode) {
case OPTYPE_DP:
r = (regs.d + (addr & 0xffff)) & 0xffff;
a = (r.d + (addr & 0xffff)) & 0xffff;
break;
case OPTYPE_DPX:
r = (regs.d + regs.x + (addr & 0xffff)) & 0xffff;
a = (r.d + r.x + (addr & 0xffff)) & 0xffff;
break;
case OPTYPE_DPY:
r = (regs.d + regs.y + (addr & 0xffff)) & 0xffff;
a = (r.d + r.y + (addr & 0xffff)) & 0xffff;
break;
case OPTYPE_IDP:
addr = (regs.d + (addr & 0xffff)) & 0xffff;
r = (regs.db << 16) + dreadw(addr);
addr = (r.d + (addr & 0xffff)) & 0xffff;
a = (r.db << 16) + dreadw(addr);
break;
case OPTYPE_IDPX:
addr = (regs.d + regs.x + (addr & 0xffff)) & 0xffff;
r = (regs.db << 16) + dreadw(addr);
addr = (r.d + r.x + (addr & 0xffff)) & 0xffff;
a = (r.db << 16) + dreadw(addr);
break;
case OPTYPE_IDPY:
addr = (regs.d + (addr & 0xffff)) & 0xffff;
r = (regs.db << 16) + dreadw(addr) + regs.y;
addr = (r.d + (addr & 0xffff)) & 0xffff;
a = (r.db << 16) + dreadw(addr) + r.y;
break;
case OPTYPE_ILDP:
addr = (regs.d + (addr & 0xffff)) & 0xffff;
r = dreadl(addr);
addr = (r.d + (addr & 0xffff)) & 0xffff;
a = dreadl(addr);
break;
case OPTYPE_ILDPY:
addr = (regs.d + (addr & 0xffff)) & 0xffff;
r = dreadl(addr) + regs.y;
addr = (r.d + (addr & 0xffff)) & 0xffff;
a = dreadl(addr) + r.y;
break;
case OPTYPE_ADDR:
r = (regs.db << 16) + (addr & 0xffff);
a = (r.db << 16) + (addr & 0xffff);
break;
case OPTYPE_ADDR_PC:
r = (regs.pc.b << 16) + (addr & 0xffff);
a = (r.pc.b << 16) + (addr & 0xffff);
break;
case OPTYPE_ADDRX:
r = (regs.db << 16) + (addr & 0xffff) + regs.x;
a = (r.db << 16) + (addr & 0xffff) + r.x;
break;
case OPTYPE_ADDRY:
r = (regs.db << 16) + (addr & 0xffff) + regs.y;
a = (r.db << 16) + (addr & 0xffff) + r.y;
break;
case OPTYPE_IADDR_PC:
r = (regs.pc.b << 16) + (addr & 0xffff);
a = (r.pc.b << 16) + (addr & 0xffff);
break;
case OPTYPE_IADDRX:
r = (regs.pc.b << 16) + ((addr + regs.x) & 0xffff);
a = (r.pc.b << 16) + ((addr + r.x) & 0xffff);
break;
case OPTYPE_ILADDR:
r = addr;
a = addr;
break;
case OPTYPE_LONG:
r = addr;
a = addr;
break;
case OPTYPE_LONGX:
r = (addr + regs.x);
a = (addr + r.x);
break;
case OPTYPE_SR:
r = (regs.s + (addr & 0xff)) & 0xffff;
a = (r.s + (addr & 0xff)) & 0xffff;
break;
case OPTYPE_ISRY:
addr = (regs.s + (addr & 0xff)) & 0xffff;
r = (regs.db << 16) + dreadw(addr) + regs.y;
addr = (r.s + (addr & 0xff)) & 0xffff;
a = (r.db << 16) + dreadw(addr) + r.y;
break;
case OPTYPE_RELB:
r = (regs.pc.b << 16) + ((regs.pc.w + 2) & 0xffff);
r += int8(addr);
a = (r.pc.b << 16) + ((r.pc.w + 2) & 0xffff);
a += int8(addr);
break;
case OPTYPE_RELW:
r = (regs.pc.b << 16) + ((regs.pc.w + 3) & 0xffff);
r += (int16)addr;
a = (r.pc.b << 16) + ((r.pc.w + 3) & 0xffff);
a += (int16)addr;
break;
}
return r;
return a;
}
auto R65816::disassemble() -> string {
return disassemble(regs.pc.d, regs.e, regs.p.m, regs.p.x);
return disassemble(r.pc.d, r.e, r.p.m, r.p.x);
}
auto R65816::disassemble(uint24 addr, bool e, bool m, bool x) -> string {
string s;
reg24_t pc;
Reg24 pc;
pc.d = addr;
s = {hex(pc, 6), " "};
@@ -403,23 +403,23 @@ auto R65816::disassemble(uint24 addr, bool e, bool m, bool x) -> string {
#undef x8
s.append(t, " A:{0} X:{1} Y:{2} S:{3} D:{4} B:{5} ", format{
hex(regs.a.w, 4), hex(regs.x.w, 4), hex(regs.y.w, 4),
hex(regs.s.w, 4), hex(regs.d.w, 4), hex(regs.db, 2)
hex(r.a.w, 4), hex(r.x.w, 4), hex(r.y.w, 4),
hex(r.s.w, 4), hex(r.d.w, 4), hex(r.db, 2)
});
if(regs.e) {
if(r.e) {
s.append(
regs.p.n ? 'N' : 'n', regs.p.v ? 'V' : 'v',
regs.p.m ? '1' : '0', regs.p.x ? 'B' : 'b',
regs.p.d ? 'D' : 'd', regs.p.i ? 'I' : 'i',
regs.p.z ? 'Z' : 'z', regs.p.c ? 'C' : 'c'
r.p.n ? 'N' : 'n', r.p.v ? 'V' : 'v',
r.p.m ? '1' : '0', r.p.x ? 'B' : 'b',
r.p.d ? 'D' : 'd', r.p.i ? 'I' : 'i',
r.p.z ? 'Z' : 'z', r.p.c ? 'C' : 'c'
);
} else {
s.append(
regs.p.n ? 'N' : 'n', regs.p.v ? 'V' : 'v',
regs.p.m ? 'M' : 'm', regs.p.x ? 'X' : 'x',
regs.p.d ? 'D' : 'd', regs.p.i ? 'I' : 'i',
regs.p.z ? 'Z' : 'z', regs.p.c ? 'C' : 'c'
r.p.n ? 'N' : 'n', r.p.v ? 'V' : 'v',
r.p.m ? 'M' : 'm', r.p.x ? 'X' : 'x',
r.p.d ? 'D' : 'd', r.p.i ? 'I' : 'i',
r.p.z ? 'Z' : 'z', r.p.c ? 'C' : 'c'
);
}

View File

@@ -1,77 +1,81 @@
alwaysinline auto readpc() -> uint8 {
return read((regs.pc.b << 16) + regs.pc.w++);
alwaysinline auto readPC() -> uint8 {
return read(r.pc.b << 16 | uint16(r.pc.w++));
}
alwaysinline auto readstack() -> uint8 {
regs.e ? regs.s.l++ : regs.s.w++;
return read(regs.s.w);
alwaysinline auto readSP() -> uint8 {
r.e ? r.s.l++ : r.s.w++;
return read(r.s.w);
}
alwaysinline auto readstackn() -> uint8 {
return read(++regs.s.w);
alwaysinline auto readSPn() -> uint8 {
return read(++r.s.w);
}
alwaysinline auto readaddr(uint32 addr) -> uint8 {
return read(addr & 0xffff);
alwaysinline auto readAddr(uint addr) -> uint8 {
return read(uint16(addr));
}
alwaysinline auto readlong(uint32 addr) -> uint8 {
return read(addr & 0xffffff);
alwaysinline auto readLong(uint addr) -> uint8 {
return read(uint24(addr));
}
alwaysinline auto readdbr(uint32 addr) -> uint8 {
return read(((regs.db << 16) + addr) & 0xffffff);
alwaysinline auto readDB(uint addr) -> uint8 {
return read(r.db << 16 | uint16(addr));
}
alwaysinline auto readpbr(uint32 addr) -> uint8 {
return read((regs.pc.b << 16) + (addr & 0xffff));
alwaysinline auto readPB(uint addr) -> uint8 {
return read(r.pc.b << 16 | uint16(addr));
}
alwaysinline auto readdp(uint32 addr) -> uint8 {
if(regs.e && regs.d.l == 0x00) {
return read((regs.d & 0xff00) + ((regs.d + (addr & 0xffff)) & 0xff));
alwaysinline auto readDP(uint addr) -> uint8 {
if(r.e && r.d.l == 0x00) {
return read(r.d | uint8(addr));
} else {
return read((regs.d + (addr & 0xffff)) & 0xffff);
return read(uint16(r.d + addr));
}
}
alwaysinline auto readsp(uint32 addr) -> uint8 {
return read((regs.s + (addr & 0xffff)) & 0xffff);
alwaysinline auto readDPn(uint addr) -> uint8 {
return read(uint16(r.d + addr));
}
alwaysinline auto writestack(uint8 data) -> void {
write(regs.s.w, data);
regs.e ? regs.s.l-- : regs.s.w--;
alwaysinline auto readSP(uint addr) -> uint8 {
return read(uint16(r.s + addr));
}
alwaysinline auto writestackn(uint8 data) -> void {
write(regs.s.w--, data);
alwaysinline auto writeSP(uint8 data) -> void {
write(r.s.w, data);
r.e ? r.s.l-- : r.s.w--;
}
alwaysinline auto writeaddr(uint32 addr, uint8 data) -> void {
write(addr & 0xffff, data);
alwaysinline auto writeSPn(uint8 data) -> void {
write(r.s.w--, data);
}
alwaysinline auto writelong(uint32 addr, uint8 data) -> void {
write(addr & 0xffffff, data);
alwaysinline auto writeAddr(uint addr, uint8 data) -> void {
write(uint16(addr), data);
}
alwaysinline auto writedbr(uint32 addr, uint8 data) -> void {
write(((regs.db << 16) + addr) & 0xffffff, data);
alwaysinline auto writeLong(uint addr, uint8 data) -> void {
write(uint24(addr), data);
}
alwaysinline auto writepbr(uint32 addr, uint8 data) -> void {
write((regs.pc.b << 16) + (addr & 0xffff), data);
alwaysinline auto writeDB(uint addr, uint8 data) -> void {
write(r.db << 16 | uint16(addr), data);
}
alwaysinline auto writedp(uint32 addr, uint8 data) -> void {
if(regs.e && regs.d.l == 0x00) {
write((regs.d & 0xff00) + ((regs.d + (addr & 0xffff)) & 0xff), data);
alwaysinline auto writePB(uint addr, uint8 data) -> void {
write(r.pc.b << 16 | uint16(addr), data);
}
alwaysinline auto writeDP(uint addr, uint8 data) -> void {
if(r.e && r.d.l == 0x00) {
write(r.d | uint8(addr), data);
} else {
write((regs.d + (addr & 0xffff)) & 0xffff, data);
write(uint16(r.d + addr), data);
}
}
alwaysinline auto writesp(uint32 addr, uint8 data) -> void {
write((regs.s + (addr & 0xffff)) & 0xffff, data);
alwaysinline auto writeSP(uint addr, uint8 data) -> void {
write(uint16(r.s + addr), data);
}

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