Compare commits

...

223 Commits
v087 ... v098

Author SHA1 Message Date
Tim Allen
e846c83d47 Update to v098 release.
byuu says:

This release most notably adds WonderSwan and WonderSwan Color
emulation.

It is also the final release that will include the balanced and
performance profiles for bsnes.

Changelog (since v097):
- higan: added WonderSwan and WonderSwan Color emulation
- higan: simplified the coooperative-thread schedulers for all emulation
  cores
- higan: moved from native (u)int[8,16,32,64]_t types to
  Natural<T>/Integer<T> classes
- higan: major cleanups to the Makefiles; including auto-selection of
  processor cores
- loki: very barebones skeleton in place now; does absolutely nothing
  - these allow the removal of huge amounts of manual bit-twiddling with
    more readable alternatives
- FC: fixed PPU OAM reads (mask the correct bits when writing) [hex_usr]
- SFC: fixed expansion port device mapping on game load
- SFC: reworked the way SGB games were loaded -* SFC core can now be
  compiled without GB core (and thus without SGB support)
- SFC: added Super Disc expansion port device (although it's just
  a non-functional skeleton so far)
- SFC: bugfix to SharpRTC emulation regarding leap year extra day counts
  (Dai Kaijuu Monogatari II)
- SFC: major code cleanups to the CPU core and the R65816 processor base
  class
- SFC: added 21fx emulation (not the old 21fx that became MSu1; reusing
  the name for a new idea)
  - basic idea is to move the serial USART to the expansion port along
    with a reset vector hijack
- SFC: emulate reset vector pushing PC onto the stack on system soft
  reset
- GB: pass gekkio's if_ie_registers and boot_hwio-G test ROMs
- GBA: reworked all handling of MMIO functions: removed the get/set
  class functions
- nall: improved edge case return values for
  (basename,pathname,dirname,...)
- ruby: fixed ~AudioXAudio2() typo (now calls destructor on exit)
- ruby: if DirectSoundCreate fails (no sound device present), return
  false from init instead of crashing
- tomoko: added "All" option to filetype dropdown for ROM loading
  - allows loading GBC games in SGB mode
- tomoko: locate() updated to search multiple paths [2]
- tomoko: fixed some oddities when changing the audio frequency/latency
  settings
- icarus: can now work with WonderSwan and WonderSwan Color games

Note 1: 90% of the changelog for this release was related to the
WonderSwan emulation being in development. Doesn't make a lot of sense
to post about fixes since the code didn't exist publicly prior to this
release.
2016-04-09 12:38:50 +10:00
Tim Allen
06d44b4878 Update to v097r32 release.
byuu says:

Changelog:
- bsnes-accuracy emulates reset vector properly[1]
- bsnes-balanced compiles once more
- bsnes-performance compiles once more

The balanced and performance profiles are fixed for the last time. They
will be removed for v098r01.

Please test this WIP as much as you can. I intend to release v098 soon.
I know save states are a little unstable for the WS/WSC, but they work
well enough for a release. If I can't figure it out soon, I'm going to
post v098 anyway.

[1] this one's been a really long time coming, but ... one of the bugs
I found when I translated Tekkaman Blade was that my translation patch
would crash every now and again when you hit the reset button on a real
SNES, but it always worked upon power on.

Turns out that while power-on initializes the stack register to $01ff,
reset does things a little bit differently. Reset actually triggers the
reset interrupt vector after putting the CPU into emulation mode, but it
doesn't initialize the stack pointer. The net effect is that the stack
high byte is set to $01, and the low byte is left as it was. And then
the reset vector runs, which pushes the low 16-bits of the program
counter, plus the processor flags, onto the stack frame. So you can
actually tell where the game was at when the system was reset ... sort
of.

It's a really weird behavior to be sure. But here's the catch: say
you're hacking a game, and so you hook the reset vector with jsl
showMyTranslationCreditsSplashScreen, and inside this new subroutine,
you then perform whatever bytes you hijacked, and then initialize the
stack frame to go about your business drawing the screen, and when
you're done, you return via rtl.

Generally, this works fine. But if S={0100, 0101, or 0102}, then the
stack will wrap due to being in emulation mode at reset. So it will
write to {0100, 01ff, 01fe}. But now in your subroutine, you enable
native mode. So when you return from your subroutine hijack, it reads
the return address from {01ff, 0200, 0201} instead of the expected
{01ff, 0100, 0101}. Thus, you get an invalid address back, and you
"return" to the wrong location, and your program dies.

The odds of this happening depend on how the game handles S, but
generally speaking, it's a ~1:85 chance.

By emulating this behavior, I'll likely expose this bug in many ROM
hacks that do splash screen hooks like this, including my own Tekkaman
Blade translation. And it's also very possible that there are commercial
games that screw this up as well.

But, it's what the real system does. So if any crashes start happening
as of this WIP upon resetting the game, well ... it'd happen on real
hardware, too.
2016-04-03 21:17:20 +10:00
Tim Allen
25eaaa82f4 Update to v097r31 release.
byuu says:

Changelog:
- WS: fixed sprite window clipping (again)
- WS: don't set IRQ status bits of IRQ enable bits are clear
- SFC: signed/unsigned -> int/uint for DSP core
- SFC: removed eBoot
- SFC: added 21fx (not the same as the old precursor to MSU1; just
  reusing the name)

Note: XI Little doesn't seem to be fixed after all ... but the other
three are. So I guess we're at 13 bugs :( And holy shit that music when
you choose a menu option is one of the worst sounds I've ever heard in
my life >_<
2016-03-29 20:15:01 +11:00
Tim Allen
2d83300235 Update to v097r30 release.
byuu says:

Changelog:
- fixed sprite window attribute bit (Final Fantasy, Tekken Card
  Challenge, etc)
- rewrote renderer to support 2bpp color mode (Dark Eyes, Dokodemo
  Hamster, Flash Koibito-kun, etc)
2016-03-29 19:44:03 +11:00
Tim Allen
680d16561e Update to v097r29 release.
byuu says:

Changelog:
- fixed DAS instruction (Judgment Silversword score)
- fixed [VH]TMR_FREQ writes (Judgement Silversword audio after area 20)
- fixed initialization of SP (fixes seven games that were hanging on
  startup)
- added SER_STATUS and SER_DATA stubs (fixes four games that were
  hanging on startup)
- initialized IEEP data (fixes Super Robot Taisen Compact 2 series)
  - note: you'll need to delete your internal.com in WonderSwan
    (Color).sys folders
- fixed CMPS and SCAS termination condition (fixes serious bugs in four
  games)
- set read/writeCompleted flags for EEPROM status (fixes Tetsujin 28
  Gou)
- major code cleanups to SFC/R65816 and SFC/CPU
  - mostly refactored disassembler to output strings instead of using
    char* buffer
  - unrolled all the subfolders on sfc/cpu to a single directory
  - corrected casing for all of sfc/cpu and a large portion of
    processor/r65816

I kind of went overboard on the code cleanup with this WIP. Hopefully
nothing broke. Any testing one can do with the SFC accuracy core would
be greatly appreciated.

There's still an absolutely huge amount of work left to go, but I do
want to eventually refresh the entire codebase to my current coding
style, which is extremely different from stuff that's been in higan
mostly untouched since ~2006 or so. It's dangerous and fickle work, but
if I don't do it, then the code will be a jumbled mess of several
different styles.
2016-03-26 12:56:15 +11:00
Tim Allen
379ab6991f Update to v097r28 release.
byuu says:

Changelog: (all WSC unless otherwise noted)
- fixed LINECMP=0 interrupt case (fixes FF4 world map during airship
  sequence)
- improved CPU timing (fixes Magical Drop flickering and FF1 battle
  music)
- added per-frame OAM caching (fixes sprite glitchiness in Magical Drop,
  Riviera, etc.)
- added RTC emulation (fixes Dicing Knight and Judgement Silversword)
- added save state support
- added cheat code support (untested because I don't know of any cheat
  codes that exist for this system)
- icarus: can now detect games with RTC chips
- SFC: bugfix to SharpRTC emulation (Dai Kaijuu Monogatari II)
  - ( I was adding the extra leap year day to all 12 months instead of
    just February ... >_< )

Note that the RTC emulation is very incomplete. It's not really
documented at all, and the two games I've tried that use it never even
ask you to set the date/time (so they're probably just using it to count
seconds.) I'm not even sure if I've implement the level-sensitive
behavior correctly (actually, now that I think about it, I need to mask
the clear bit in INT_ACK for the level-sensitive interrupts ...)

A bit worried about the RTC alarm, because it seems like it'll fire
continuously for a full minute. Or even if you turn it off after it
fires, then that doesn't seem to be lowering the line until the next
second ticks on the RTC, so that likely needs to happen when changing
the alarm flag.

Also not sure on this RTC's weekday byte. On the SharpRTC, it actually
computes this for you. Because it's not at all an easy thing to
calculate yourself in 65816 or V30MZ assembler. About 40 lines of code
to do it in C. For now, I'm requiring the program to calculate the value
itself.

Also note that there's some gibberish tiles in Judgement Silversword,
sadly. Not sure what's up there, but the game's still fully playable at
least.

Finally, no surprise: Beat-Mania doesn't run :P
2016-03-25 17:19:08 +11:00
Tim Allen
d3413db04a Update to v097r27 release.
byuu says:

Absolutely major improvements to the WS/C emulation today.

Changelog: (all WS/C related)
- fixed channel 3 sweep pitch adjustment
- fixed channel 3 sweep value sign extension
- removed errant channel 5 speed setting (not what's really going on)
- fixed sign extension on channel 5 samples
- improved DAC mixing of all five audio channels
- fixed r26 regression with PPU timing loop
- fixed sprite windowing behavior (sprite attribute flag is window mode;
  not window enable)
- added per-scanline register latching to the PPU
- IRQs should terminate HLT even when the IRQ enable register bits are
  clear
- fixed PALMONO reads
- added blur emulation
- added color emulation (based on GBA, so it heavily desaturates colors;
  not entirely correct, but it helps a lot)
- no longer decimating audio to 24KHz; running at full 3.072MHz through
  the windowed sinc filter [1]
- cleaned up PPU portRead / portWrite functions significantly
- emulated a weird quirk as mentioned by trap15 regarding timer
  frequency writes enabling said timers [2]
- emulated LCD_CTRL sleep bit; screen can now be disabled (always draws
  black in this case for now)
- improved OAM caching; but it's still disabled because it causes huge
  amounts of sprite glitches (unsure why)
- fixed rendering of sprites that wrap around the screen edges back to
  the top/left of the display
- emulated keypad interrupts
- icarus: detect orientation bit in game header
- higan: use orientation setting in manifest to set default screen
  rotation

[1] the 24KHz -> 3.072MHz sound change is huge. Sound is substantially
improved over the previous WIPs. It does come at a pretty major speed
penalty, though. This is the highest frequency of any system in higan
running through an incredibly (amazing, yet) demanding sinc resampler.
Frame rate dropped from around 240fps to 150fps with the sinc filter on.
If you choose a different audio filter, you'll get most of that speed
back, but audio will sound worse again.

[2] we aren't sure if this is correct hardware behavior or not. It seems
to very slightly help Magical Drop, but not much.

The blur emulation is brutal. It's absolutely required for Riviera's
translucency simulation of selected menu items, but it causes serious
headaches due to the WS's ~75hz refresh rate running on ~60hz monitors
without vsync. It's probably best to leave it off and just deal with the
awful flickering on Riviera's menu options.

Overall, WS/C emulation is starting to get quite usable indeed. Couple
of major bugs that I'd really like to get fixed before releasing it,
though. But they're getting harder and harder to fix ...

Major Bugs:
- Final Fantasy battle background music is absent. Sound effects still
  work. Very weird.
- Final Fantasy IV scrolling during airship flight opening sequence is
  horribly broken. Scrolls one screen at a time.
- Magical Drop flickers like crazy in-game. Basically unplayable like
  this.
- Star Hearts character names don't appear in the smaller dialog box
  that pops up.

Minor Bugs:
- Occasional flickering during Riviera opening scenes.
- One-frame flicker of Leda's sprite at the start of the first stage.
2016-03-19 18:35:25 +11:00
Tim Allen
a7f7985581 Update to v097r26 release.
byuu says:

Changelog:
- WS: fixed 8-bit sign-extended imul (fixes Star Hearts completely,
  Final Fantasy world map)
- WS: fixed rcl/rcr carry shifting (fixes Crazy Climber, others)
- WS: added sound DMA emulation (Star Hearts rain sound for one example)
- WS: added OAM caching, but it's forced every line for now because
  otherwise there are too many sprite glitches
- WS: use headphoneEnable bit instead of speakerEnable bit (fixes muted
  audio in games)
- WS: various code cleanups (I/O mapping, audio channel naming, etc)

The hypervoice channel doesn't sound all that great just yet. But I'm
not sure how it's supposed to sound. I need a better example of some
more complex music.

What's left are some unknown register status bits (especially in the
sound area), keypad interrupts, RTC emulation, CPU prefetch emulation.
And then it's all just bugs. Lots and lots of bugs that need to be
fixed.

EDIT: oops, bad typo in the code.

ws/ppu/ppu.cpp line 20: change range(256) to range(224).

Also, delete the r.speed stuff from channel5.cpp to make the rain sound
a lot better in Star Hearts. Apparently that's outdated and not what the
bits really do.
2016-03-17 22:28:15 +11:00
Tim Allen
b586471562 Update to v097r25 release.
byuu says:

Changelog:
- WS: added HblankTimer and VblankTimer IRQs; although they don't appear
  to have any effect on any games that use them :/
- WS: added sound emulation; works perfectly in some games (eg Riviera);
  is completely silent in most games (eg GunPey)

The sound emulation only partially supports the hypervoice (headphone
only) channel. I need to implement the SDMA before it'll actually do
anything useful. I'm a bit confused about how exactly things work. It
looks like the speaker volume shift and clamp only applies to speaker
mode and not headphone mode, which is very weird. Then there's the
software possibility of muting the headphones and/or the speaker.
Preferably, I want to leave the emulator always in headphone mode for
the extra audio channel. If there are games that force-mute the
headphones, but not speakers, then I may need to force headphones back
on but with the hypervoice channel disabled. I guess we'll see how
things go.

Rough guess is probably that the channels default to enabled after the
IPLROM, and games aren't bothering to manually enable them or something.
2016-03-14 22:03:32 +11:00
Tim Allen
c33065fbd1 Update to v097r24 release.
byuu says:

Changelog:
- WS: fixed bug when IRQs triggered during a rep string instruction
- WS: added sprite attribute caching (per-scanline); absolutely massive
  speed-up
- WS: emulated limit of 32 sprites per scanline
- WS: emulated the extended PPU register bit behavior based on the
  DISP_CTRL tile bit-depth setting
- WS: added "Rotate" key binding; can be used to flip the WS display
  between horizontal and vertical in real-time

The prefix emulation may not be 100% hardware-accurate, but the edge
cases should be extreme enough to not come up in the WS library. No way
to get the emulation 100% down without intensive hardware testing.
trap15 pointed me at a workflow diagram for it, but that diagram is
impossible without a magic internal stack frame that grows with every
IRQ, and can thus grow infinitely large.

The rotation thing isn't exactly the most friendly set-up, but oh well.
I'll see about adding a default rotation setting to manifests, so that
games like GunPey can start in the correct orientation. After that, if
the LCD orientation icon turns out to be reliable, then I'll start using
that. But if there are cases where it's not reliable, then I'll leave it
to manual button presses.

Speaking of icons, I'll need a set of icons to render on the screen.
Going to put them to the top right on vertical orientation, and on the
bottom left for horizontal orientation. Just outside of the video
output, of course.

Overall, WS is getting pretty far along, but still some major bugs in
various games. I really need sound emulation, though. Nobody's going to
use this at all without that.
2016-03-13 11:22:15 +11:00
Tim Allen
79e7e6ab9e Update to v097r23 release.
byuu says:

Changelog:
- emulated SuperDisc $21e1 basic interface (NEC 4-bit MCU); all hardware
  tests pass now (but they don't test much)
- WS/V30MZ: fixed inc/dec reg flag calculation
- WS/V30MZ: fixed lds/les instructions

WS/C compatibility should be way up now. SuperDisc BIOS passes all tests
now (but they only test for the presence of the interface, nothing
more.)
2016-03-13 11:22:14 +11:00
Tim Allen
3d3ac8c1db Update to v097r22 release.
byuu says:

Changelog:
- WS: fixed lods, scas instructions
- WS: implemented missing GRP4 instructions
- WS: fixed transparency for screen one
- WSC: added color-mode PPU rendering
- WS+WSC: added packed pixel mode support
- WS+WSC: added dummy sound register reads/writes
- SFC: added threading to SuperDisc (it's hanging for right now; need to
  clear IRQ on $21e2 writes)

SuperDisc Timer and Sound Check were failing before due to not turning
off IRQs on $21e4 clear, so I'm happy that's fixed now.

Riviera starts now, and displays the first intro screen before crashing.
Huge, huge amounts of corrupted graphics, though. This game's really
making me work for it :(

No color games seem fully playable yet, but a lot of monochrome and
color games are now at least showing more intro screen graphics before
dying.

This build defaults to horizontal orientation, but I left the inputs
bound to vertical orientation. Whoops. I still need to implement
a screen flip key binding.
2016-03-13 11:22:14 +11:00
Tim Allen
b0d2f5033e Update to v097r21 release.
byuu says:

Changelog:
- icarus: WS/C detects RAM type/size heuristically now
- icarus: WS/C uses ram type=$type instead of $type
- WS: use back color instead of white for backdrop
- WS: fixed sprite count limit; removes all the garbled sprites from
  GunPey
- WS: hopefully fixed sprite priority with screen 2
- WS: implemented keypad polling; GunPey is now fully playable
- SNES: added Super Disc expansion port device (doesn't do anything,
  just for testing)

Note: WS is hard-coded to vertical orientation right now. But there's
basic code in there for all the horizontal stuff.
2016-03-13 11:22:14 +11:00
Tim Allen
570eb9c5f5 Update to v097r20 release.
byuu says:

Changelog:
- WS: fixed a major CPU bug where I was using the wrong bits for
  ModR/M's memory mode
- WS: added grayscale PPU emulation (exceptionally buggy)

GunPey now runs, as long as you add:

    eeprom name=save.ram size=0x800

to the manifest after importing with icarus.

Right now, you can't control the game due to missing keypad polling.
There's also a lot of glitchiness with the sprites. Seems like they're
not getting properly cleared sometimes or something.

Also, the PPU emulation is totally unrealistic bullshit. I decode and
evaluate every single tile and sprite on every single pixel of output.
No way in hell the hardware could ever come close to that. The speed's
around 500fps without the insane sprite evaluations, and around 90fps
with it. Obviously, I'll fix this in time.

Nothing else seems to run that I've tried. Not even far enough to
display any output whatsoever. Tried Langrisser Millenium, Rockman
& Forte and Riviera. I really need to update icarus to try and encode
eeprom/sram sizes, because that's going to break a lot of stuff if it's
missing.
2016-03-13 11:22:14 +11:00
Tim Allen
7dc62e3a69 Update to v097r19 release.
byuu says:

Changelog:
- fixed nall/windows/guard.hpp
- fixed hiro/(windows,gtk)/header.hpp
- fixed Famicom PPU OAM reads (mask the correct bits when writing)
  [hex_usr]
- removed the need for (system := system) lines from higan/GNUmakefile
- added "All" option to filetype dropdown for ROM loading
  - allows loading GBC games in SGB mode (and technically non-GB(C)
    games, which will obviously fail to do anything)
- loki can load and play game folders now (command-line only) (extremely
  unimpressive; don't waste your time :P)
  - the input is extremely hacked in as a quick placeholder; not sure
    how I'm going to do mapping yet for it
2016-03-13 11:22:14 +11:00
Tim Allen
fc7d5991ce Update to v097r18 release.
byuu says:

Changelog:
- fixed SNES sprite priority regression from r17
- added nall/windows/guard.hpp to guard against global namespace
  pollution (similar to nall/xorg/guard.hpp)
- almost fixed Windows compilation (still accuracy profile only, sorry)
- finished porting all of gba/ppu's registers over to the new .bit,.bits
  format ... all GBA registers.cpp files gone now
- the "processors :=" line in the target-$(ui)/GNUmakefile is no longer
  required
  - processors += added to each emulator core
  - duplicates are removed using the new nall/GNUmakefile's $(unique)
    function
- SFC core can be compiled without the GB core now
  - "-DSFC_SUPERGAMEBOY" is required to build in SGB support now (it's
    set in target-tomoko/GNUmakefile)
- started once again on loki (higan/target-loki/) [as before, loki is
  Linux/BSD only on account of needing hiro::Console]

loki shouldn't be too horrendous ... I hope. I just have the base
skeleton ready for now. But the code from v094r08 should be mostly
copyable over to it. It's just that it's about 50KiB of incredibly
tricky code that has to be just perfect, so it's not going to be quick.
But at least with the skeleton, it'll be a lot easier to pick away at it
as I want.

Windows compilation fix: move hiro/windows/header.hpp line 18 (header
guard) to line 16 instead.
2016-03-13 11:22:14 +11:00
Tim Allen
29be18ce0c Update to v097r17 release.
byuu says:

Changelog:
- ruby: if DirectSoundCreate fails (no sound device present), return
  false from init instead of crashing
- nall: improved edge case return values for
  (basename,pathname,dirname,...)
- nall: renamed file_system_object class to inode
- nall: varuint_t replaced with VariadicNatural; which contains
  .bit,.bits,.byte ala Natural/Integer
- nall: fixed boolean compilation error on Windows
- WS: popa should not restore SP
- GBA: rewrote the CPU/APU cores to use the .bit,.bits functions;
  removed registers.cpp from each

Note that the GBA changes are extremely major. This is about five hours
worth of extremely delicate work. Any slight errors could break
emulation in extremely bad ways. Let's hold off on extensive testing
until the next WIP, after I do the same to the PPU.

So far ... endrift's SOUNDCNT_X I/O test is failing, although that code
didn't change, so clearly I messed up SOUNDCNT_H somehow ...

To compile on Windows:

1. change nall/string/platform.hpp line 47 to

    return slice(result, 0, 3);

2. change ruby/video.wgl.cpp line 72 to

    auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {

3. add this line to the very top of hiro/windows/header.cpp:

    #define boolean FuckYouMicrosoft
2016-03-13 11:22:14 +11:00
Tim Allen
810cbdafb4 Update to v097r16 release.
byuu says:

Changelog:
- sfc/ppu/sprite updated to use new .bit(s) functions; masked sizes
  better; added valid flags instead of using magic numbers
- ws/ppu updates to use new .bit(s) functions
- ws/ppu: added line compare interrupt support
- added ws/eeprom; emulation of WS/WSC internal EEPROM and cartridge
  EEPROM (1kbit - 16kbit supported)
- added basic read/write handlers for remaining WS/WSC PPU registers

WS EEPROM emulation is basically a direct copy of trap15's code. Still
some unknown areas in there, but hopefully it's enough to get further
into games that depend on EEPROM support. Note that you'll have to
manually add the eeprom line to the manifest for now, as icarus doesn't
know how to detect EEPROM/sizes yet.

I figured the changes to the SNES PPU sprites would slow it down a tad,
but it actually sped it up. Most of the impact from the integer classes
are gone now.
2016-03-13 11:22:10 +11:00
Tim Allen
4b29f4bad7 Update to v097r15 release.
byuu says:

Changelog:
- higan now uses Natural<Size>/Integer<Size> for its internal types
- Super Famicom emulation now uses uint24 instead of uint for bus
  addresses (it's a 24-bit bus)
- cleaned up gb/apu MMIO writes
- cleaned up sfc/coprocessor/msu1 MMIO writes
- ~3% speed penalty

I've wanted to do that 24-bit bus thing for so long, but have always
been afraid of the speed impact. It's probably going to hurt
balanced/performance once they compile again, but it wasn't significant
enough to harm the accuracy core's frame rate, thankfully. Only lost one
frame per second.

The GBA core handlers are clearly going to take a lot more work. The
bit-ranges will make it substantially easier to handle, though. Lots of
32-bit registers where certain values span multiple bytes, but we have
to be able to read/write at byte-granularity.
2016-02-16 20:32:49 +11:00
Tim Allen
ef65bb862a Update to 20160215 release.
byuu says:

Got it. Wow, that didn't hurt nearly as much as I thought it was going
to.

Dropped from 127.5fps to 123.5fps to use Natural/Integer for
(u)int(8,16,32,64).

That's totally worth the cost.
2016-02-16 20:27:55 +11:00
Tim Allen
0d0af39b44 Update to v097r14 release.
byuu says:

This is a few days old, but oh well.

This WIP changes nall,hiro,ruby,icarus back to (u)int(8,16,32,64)_t.

I'm slowly pushing for (u)int(8,16,32,64) to use my custom
Integer<Size>/Natural<Size> classes instead. But it's going to be one
hell of a struggle to get that into higan.
2016-02-16 20:11:58 +11:00
Tim Allen
6c83329cae Update to v097r13 release.
byuu says:

I refactored my schedulers. Added about ten lines to each scheduler, and
removed about 100 lines of calling into internal state in the scheduler
for the FC,SFC cores and about 30-40 lines for the other cores. All of
its state is now private.

Also reworked all of the entry points to static auto Enter() and auto
main(). Where Enter() handles all the synchronization stuff, and main()
doesn't need the while(true); loop forcing another layer of indentation
everywhere.

Took a few hours to do, but totally worth it. I'm surprised I didn't do
this sooner.

Also updated icarus gmake install rule to copy over the database.
2016-02-09 22:51:12 +11:00
Tim Allen
32a95a9761 Update to v097r12 release.
byuu says:

Nothing WS-related this time.

First, I fixed expansion port device mapping. On first load, it was
mapping the expansion port device too late, so it ended up not taking
effect. I had to spin out the logic for that into
Program::connectDevices(). This was proving to be quite annoying while
testing eBoot (SNES-Hook simulation.)

Second, I fixed the audio->set(Frequency, Latency) functions to take
(uint) parameters from the configuration file, so the weird behavior
around changing settings in the audio panel should hopefully be gone
now.

Third, I rewrote the interface->load,unload functions to call into the
(Emulator)::System::load,unload functions. And I have those call out to
Cartridge::load,unload. Before, this was inverted, and Cartridge::load()
was invoking System::load(), which I felt was kind of backward.

The Super Game Boy really didn't like this change, however. And it took
me a few hours to power through it. Before, I had the Game Boy core
dummying out all the interface->(load,save)Request calls, and having the
SNES core make them for it. This is because the folder paths and IDs
will be different between the two cores.

I've redesigned things so that ICD2's Emulator::Interface overloads
loadRequest and saveRequest, and translates the requests into new
requests for the SuperFamicom core. This allows the Game Boy code to do
its own loading for everything without a bunch of Super Game Boy special
casing, and without any awkwardness around powering on with no cartridge
inserted.

This also lets the SNES side of things simply call into higher-level
GameBoy::interface->load,save(id, stream) functions instead of stabbing
at the raw underlying state inside of various Game Boy core emulation
classes. So things are a lot better abstracted now.
2016-02-08 14:17:59 +11:00
Tim Allen
a89a3da77a Update to v097r11 release.
byuu says:

Alright, well interrupts are in. At least Vblank is.

I also fixed a bug in vector() indexing, MoDRM mod!=3&&reg==6 using SS
instead of DS, opcodes a0-a3 allowing segment override, and added the
"irq_disable" stuff to the relevant opcodes to suppress IRQs after
certain instructions.

But unfortunately ... still no go on Riviera. It's not reading any
unmapped ports, and although it enables Vblank IRQs and they set port
$b4's status bit, the game never sets the IE flag, so no interrupts ever
actually fire. The game does indeed appear to be sitting in a rather
huge loop, which is probably dependent upon some RAM variable being set
from the Vblank IRQ, but I don't know how I'm supposed to be triggering
it.

... I'm really quite stumped here >_>
2016-02-05 08:18:06 +11:00
Tim Allen
7a748e093e Update to v097r10 release.
byuu says:

All 256 instructions implemented fully. Fixed a major bug with
instructions that both read and write to ModRM with displacement.
Riviera now runs into an infinite loop ... possibly crashed, possibly
waiting on interrupts or in to return something. Added a bunch of PPU
settings registers, but nothing's actually rendering with them yet.
2016-02-04 21:29:08 +11:00
Tim Allen
d158c8f293 Update to v097r09 release.
244 of 256 opcodes implemented now, although the interrupt triggering
portions are missing from them still. Much better handling of prefixes
now.

I definitely have a newfound hatepreciation for x86 now >_>
2016-02-03 21:24:58 +11:00
Tim Allen
71bda4144a Update to v097r08 release.
byuu says:

Up to 211 opcodes implemented, with the caveat that the four opcodes
that make up group 3 and group 4 don't do anything yet. Both groups seem
to have some "illegal" instructions in them, so that'll be "fun".

I have a new mechanic in place for opcode prefixes, but it could use
some work still. I also only have it working to override ModRM mem
addressing, but of course it does it in a lot of other places like the
string operations.

Making it about 5.5 million instructions into Gunpey now, but of course
that doesn't mean much. Could be going off the rails at any point due to
CPU bugs or unimplemented ports. Riviera's still crashing.
2016-02-03 21:07:50 +11:00
Tim Allen
ad51f1478e Update to v097r07 release.
byuu says:

26 hours in, 173 instructions implemented. Although the four segment
prefix opcodes don't actually do anything yet. There's less than 256
actual instructions on the 80186, not sure of the exact count.

Gunpey gets around ~8,200 instructions in before hitting an unsupported
opcode (loop). Riviera goes off the rails on a retf and ends up
executing an endless stream of bad opcodes in RAM =( Both games hammer
the living shit out of the in/out ports pretty much immediately.
2016-02-02 21:51:17 +11:00
Tim Allen
d0ddd87e9c Update to v097r06 release.
byuu says:

Man, the 80186 is taking a lot longer to implement than I thought it
would. So far I'm 18 hours into this emulator. Whereas I had Super Mario
Bros fully playable (no sound) in 12 hours for the NES >_>

I refactored all the byte/word variant functions to single functions
that take a size parameter. Cuts the amount of code in half.

Also implemented repz/repnz + movsb/movsw, so Riviera now gets 299
instructions in before dying. Nobody really bothers to explain how the
CPU actually implements these instructions, but I think I have it right:
ignore non-string opcodes that follow rep, invoke the string operations
inside the rep opcodes to prevent interrupts from triggering between the
two (which will be even more fun for segment selector overrides ...)

The next opcode needed is 0xC7, which ... throws ModRM on its head. In
this mode, ModRM is only used to determine the target operand (and it
doesn't use the middle bits for that at all), and the source is an
immediate that follows it. Gonna have to waste a few more hours thinking
about how best to handle that.

Also, disabled HiDPI for higan as well on OS X.
2016-01-31 18:59:44 +11:00
Tim Allen
605a8aa3e9 Update to v097r05 release.
byuu says:

More V30MZ implemented, a lot more to go.

icarus now supports importing WS and WSC games. It expects them to have
the correct file extension, same for GB and GBC.

> Ugh, apparently HiDPI icarus doesn't let you press the check boxes.

I set the flag value in the plist to false for now. Forgot to do it for
higan, but hopefully I won't forget before release.
2016-01-30 17:40:35 +11:00
Tim Allen
a8323d0d2b Update to v097r04 release.
byuu says:

Lots of improvements. We're now able to start executing some V30MZ
instructions. 32 of 256 opcodes implemented so far.

I hope this goes without saying, but there's absolutely no point in
loading WS/WSC games right now. You won't see anything until I have the
full CPU and partial PPU implemented.

ROM bank 2 works properly now, the I/O map is 16-bit (address) x 16-bit
(data) as it should be*, and I have a basic disassembler in place
(adding to it as I emulate new opcodes.)

(* I don't know what happens if you access an 8-bit port in 16-bit mode
or vice versa, so for now I'm just treating the handlers as always being
16-bit, and discarding the upper 8-bits when not needed.)
2016-01-28 22:39:49 +11:00
Tim Allen
d7998b23ef Update to v097r03 release.
byuu says:

So, this WIP starts work on something new for higan. Obviously, I can't
keep it a secret until it's ready, because I want to continue daily WIP
releases, and of course, solicit feedback as I go along.
2016-01-27 22:31:39 +11:00
Tim Allen
344e63d928 Update to v097r02 release.
byuu says:

Note: balanced/performance profiles still broken, sorry.

Changelog:
- added nall/GNUmakefile unique() function; used on linking phase of
  higan
- added nall/unique_pointer
- target-tomoko and {System}::Video updated to use
  unique_pointer<ClassName> instead of ClassName* [1]
- locate() updated to search multiple paths [2]
- GB: pass gekkio's if_ie_registers and boot_hwio-G test ROMs
- FC, GB, GBA: merge video/ into the PPU cores
- ruby: fixed ~AudioXAudio2() typo

[1] I expected this to cause new crashes on exit due to changing the
order of destruction of objects (and deleting things that weren't
deleted before), but ... so far, so good. I guess we'll see what crops
up, especially on OS X (which is already crashing for unknown reasons on
exit.)

[2] right now, the search paths are: programpath(), {configpath(),
"higan/"}, {localpath(), "higan/"}; but we can add as many more as we
want, and we can also add platform-specific versions.
2016-01-25 22:27:18 +11:00
Tim Allen
f1ebef2ea8 Update to v097r01 release.
byuu says:

A minor WIP to get us started.

Changelog:
- System::Video merged to PPU::Video
- System::Audio merged to DSP::Audio
- System::Configuration merged to Interface::Settings
- created emulator/emulator.cpp and accompanying object file for shared
  code between all cores

Currently, emulator.cpp just holds a videoColor() function that takes
R16G16B16, performs gamma/saturation/luma adjust, and outputs
(currently) A8R8G8B8. It's basically an internal function call for cores
to use when generating palette entries. This code used to exist inside
ui-tomoko/program/interface.cpp, but we have to move it internal for
software display emulation. But in the future, we could add other useful
cross-core functionality here.
2016-01-23 18:29:34 +11:00
Tim Allen
1fdd0582fc Update to v097 release.
byuu says:

This release features improvements to all emulation cores, but most
substantially for the Game Boy core. All of blargg's test ROMs that pass
in gambatte now either pass in higan, or are off by 1-2 clocks (the
actual behaviors are fully emulated.) I consider the Game Boy core to
now be fairly accurate, but there's still more improvements to be had.

Also, what's sure to be a major feature for some: higan now has full
support for loading and playing ordinary ROM files, whether they have
copier headers, weird extensions, or are inside compressed archives. You
can load these games from the command-line, from the main Library menu
(via Load ROM Image), or via drag-and-drop on the main higan window. Of
course, fans of game folders and the library need not worry: that's
still there as well.

Also new, you can drop the (uncompressed) Game Boy Advance BIOS onto the
higan main window to install it into the correct location with the
correct file name.

Lastly, this release technically restores Mac OS X support. However,
it's still not very stable, so I have decided against releasing binaries
at this time. I'd rather not rush this and leave a bad first impression
for OS X users.

Changelog (since v096):
- higan: project source code hierarchy restructured; icarus directly
  integrated
- higan: added software emulation of color-bleed, LCD-refresh,
  scanlines, interlacing
- icarus: you can now load and import ROM files/archives from the main
  higan menu
- NES: fixed manifest parsing for board mirroring and VRC pinouts
- SNES: fixed manifest for Star Ocean
- SNES: fixed manifest for Rockman X2,X3
- GB: enabling LCD restarts frame
- GB: emulated extra OAM STAT IRQ quirk required for GBVideoPlayer
  (Shonumi)
- GB: VBK, BGPI, OBPI are readable
- GB: OAM DMA happens inside PPU core instead of CPU core
- GB: fixed APU length and sweep operations
- GB: emulated wave RAM quirks when accessing while channel is enabled
- GB: improved timings of several CPU opcodes (gekkio)
- GB: improved timings of OAM DMA refresh (gekkio)
- GB: CPU uses open collector logic; return 0xFF for unmapped memory
  (gekkio)
- GBA: fixed sequencer enable flags; fixes audio in Zelda - Minish Cap
  (Jonas Quinn)
- GBA: fixed disassembler masking error (Lioncash)
- hiro: Cocoa support added; higan can now be compiled on Mac OS X 10.7+
- nall: improved program path detection on Windows
- higan/Windows: moved configuration data from %appdata% to
  %localappdata%
- higan/Linux,BSD: moved configuration data from ~/.config/higan to
  ~/.local/higan
2016-01-17 19:59:25 +11:00
Tim Allen
12df278c5b Update to v096r08 release.
byuu says:

Changelog:
- FC: scanline emulation support added
- SFC: balanced profile compiles again
- SFC: performance profile compiles again
- GB,GBC: more fixes to pass blargg's 07, 08, 11 APU tests
- tomoko: added input loss { pause, allow-input } options
- tomoko: refactored settings video menu options to { Video Scale, Video
  Emulation, Video Shader }
- icarus: connected { About, Preferences, Quit } application menu options
2016-01-15 21:28:51 +11:00
Tim Allen
cec33c1d0f Update to v096r07 release.
byuu says:

Changelog:
- configuration files are now stored in localpath() instead of configpath()
- Video gamma/saturation/luminance sliders are gone now, sorry
- added Video Filter->Blur Emulation [1]
- added Video Filter->Scanline Emulation [2]
- improvements to GBA audio emulation (fixes Minish Cap) [Jonas Quinn]

[1] For the Famicom, this does nothing. For the Super Famicom, this
performs horizontal blending for proper pseudo-hires translucency. For
the Game Boy, Game Boy Color, and Game Boy Advance, this performs
interframe blending (each frame is the average of the current and
previous frame), which is important for things like the GBVideoPlayer.

[2] Right now, this only applies to the Super Famicom, but it'll come to
the Famicom in the future. For the Super Famicom, this option doesn't
just add scanlines, it simulates the phosphor decay that's visible in
interlace mode. If you observe an interlaced game like RPM Racing on
a real SNES, you'll notice that even on perfectly still screens, the
image appears to shake. This option emulates that effect.

Note 1: the buffering right now is a little sub-optimal, so there will
be a slight speed hit with this new support. Since the core is now
generating native ARGB8888 colors, it might as well call out to the
interface to lock/unlock/refresh the video, that way it can render
directly to the screen. Although ... that might not be such a hot idea,
since the GBx interframe blending reads from the target buffer, and that
tends to be a catastrophic option for performance.

Note 2: the balanced and performance profiles for the SNES are
completely busted again. This WIP took 6 1/2 hours, and I'm exhausted.
Very much not looking forward to working on those, since those two have
all kinds of fucked up speedup tricks for non-interlaced and/or
non-hires video modes.

Note 3: if you're on Windows and you saved your system folders somewhere
else, now'd be a good time to move them to %localappdata%/higan
2016-01-15 21:07:57 +11:00
Tim Allen
3414c8c8df Update to v096r06 release.
byuu says:

This WIP finally achieves the vision I've had for icarus.

I also fixed a mapping issue with Cx4 that, oddly enough, only caused
the "2" from the Mega Man X2 title screen to disappear.

[Editor's note - "the vision for icarus" was described in a separate,
public forum post: http://board.byuu.org/phpbb3/viewtopic.php?p=20584
Quoting for posterity:

    icarus is now a full-fledged part of higan, and will be bundled with
    each higan WIP as well. This will ensure that in the future, the
    exact version of icarus you need to run higan will be included right
    along with it. As of this WIP, physical manifest files are now truly
    and entirely optional.

    From now on, you can associate your ROM image files with higan's
    main binary, or drop them directly on top of it, to load and play
    your games.

    Furthermore, there are two new menu options that appear under the
    library menu when icarus is present:

    - "Load ROM File ..." => gives you a single-file selection dialog to
      import (and if possible) run the game
    - "Import ROM Files ..." => gives you a multi-file import dialog
      with checkboxes to pull in multiple games at once

    Finally, as before, icarus can generate manifest.bml files for
    folders that lack them.

    For people who like the game folder and library system, nothing's
    changed. Keep using higan as you have been.

    For people who hate it, you can now use higan like your classic
    emulators. Treat the "Library->{System Name}" entries as your
    "favorites" list: the games you actually play. Treat the
    "Library->Load ROM" as your standard open file dialog in other
    emulators. And finally, treat "Advanced->Game Library" as your save
    data path for cheat codes, save states, save RAM, etc.

]
2016-01-15 21:07:37 +11:00
Tim Allen
82ec876302 Update to v096r05 release.
byuu says:

Changelog:
- GB: re-enabling the LCD resets the display to LY=0,LX=0 [1]
- GB: emulated new findings (as of today!) for a DMG quirk that triggers
  an extra OAM STAT IRQ when Vblank STAT IRQs are off
- GB: made VBK, BGPI, OBPI readable
- GB: fixed APU length operations
- GB: fixed APU sweep operations
- NES: fixed cartridge/ -> board/ manifest lookups for mirroring/pinous
- hiro/Cocoa: added endrift's plist keys

Fixed:
- Astro Rabby is fully playable, even the title screen works correctly
- Bomb Jack is fully playable
- Kirby's Dream Land 2 intro scrolling first scanline of Rick is now fixed
- GBVideoPlayer functions correctly [2]
- Shin Megami Tensei: Devichil series regression fixed

[1] doesn't pass oam_bug-2/1-lcd_sync; because it seems to want
LY=0,LX>0, and I can't step the PPU in a register write as it's not
a state machine; the effect is emulated, it just starts the frame a tiny
bit sooner. blargg's testing is brutal, you can't be even one cycle off
or the test will fail.

[2] note that you will need the GBC Display Emulation shader from
hunterk's repository, or it will look like absolute shit. The
inter-frame blending is absolutely critical here.
2016-01-12 22:08:34 +11:00
Tim Allen
72b6a8b32e Update to v096r04 release.
byuu says:

Changelog:
- fixed S-DD1 RAM writes (Star Ocean audio fixed)
- applied all of the DMG test ROM fixes discussed earlier; passes many
  more test ROMs now
- at least until the GBVideoPlayer is working: for debugging purposes,
  CPU/PPU single-step now instead of sync just-in-time (~30% slower)
- fixed OS X crash on NSTextView (hopefully, would be very odd if not)

Unfortunately passing these test ROMs caused my favorite GB/GBC game to
break all of its graphics =(
Shin Megami Tensei - Devichil - Kuro no Sho (Japan) is all garbled now.
I'm really quite bummed by this ... but I guess I'll go through and
revert r04's fixes one at a time until I find what's causing it.

On the plus side, Astro Rabby is playable now. Still acts weird when
pressing B/A on the first screen, but the start button will start the
game.

EDIT: got it. Shin Megami Tensei - Devichil requires FF4F (VBK) to be
readable. Before, it was always returning 0x00. With my return 0xFF
patch, that broke. But it should be returning the VBK value, which also
fixes it. Also need to handle FF68/FF6A reads. Was really hoping that'd
help GBVideoPlayer too, but nope. It doesn't read any of those three
registers.
2016-01-11 21:31:30 +11:00
Tim Allen
653bb378ee Update to v096r03 release.
byuu says:

Changelog:
- fixed icarus to save settings properly
- fixed higan's full screen toggle on OS X
- increased "Add Codes" button width to avoid text clipping
- implemented cocoa/canvas.cpp
- added 1s delay after mapping inputs before re-enabling the window
  (wasn't actually necessary, but already added it)
- fixed setEnabled(false) on Cocoa's ListView and TextEdit widgets
- updated nall::programpath() to use GetModuleFileName on Windows
- GB: system uses open collector logic, so unmapped reads return 0xFF,
  not 0x00 (passes blargg's cpu_instrs again) [gekkio]
2016-01-08 20:23:46 +11:00
Tim Allen
0b923489dd Update to 20160106 OS X Preview for Developers release.
byuu says:

New update. Most of the work today went into eliminating hiro::Image
from all objects in all ports, replacing with nall::image. That took an
eternity.

Changelog:
- fixed crashing bug when loading games [thanks endrift!!]
- toggling "show status bar" option adjusts window geometry (not
  supposed to recenter the window, though)
- button sizes improved; icon-only button icons no longer being cut off
2016-01-07 19:17:15 +11:00
Tim Allen
4d193d7d94 Update to v096r02 (OS X Preview for Developers) release.
byuu says:

Warning: this is not for the faint of heart. This is a very early,
unpolished, buggy release. But help testing/fixing bugs would be greatly
appreciated for anyone willing.

Requirements:
- Mac OS X 10.7+
- Xcode 7.2+

Installation Commands:

    cd higan
    gmake -j 4
    gmake install
    cd ../icarus
    gmake -j 4
    gmake install

(gmake install is absolutely required, sorry. You'll be missing key
files in key places if you don't run it, and nothing will work.)

(gmake uninstall also exists, or you can just delete the .app bundles
from your Applications folder, and the Dev folder on your desktop.)

If you want to use the GBA emulation, then you need to drop the GBA BIOS
into ~/Emulation/System/Game\ Boy\ Advance.sys\bios.rom

Usage:
You'll now find higan.app and icarus.app in your Applications folders.
First, run icarus.app, navigate to where you keep your game ROMs. Now
click the settings button at the bottom right, and check "Create
Manifests", and click OK. (You'll need to do this every time you run
icarus because there's some sort of bug on OSX saving the settings.) Now
click "Import", and let it bring in your games into ~/Emulation.

Note: "Create Manifests" is required. I don't yet have a pipe
implementation on OS X for higan to invoke icarus yet. If you don't
check this box, it won't create manifest.bml files, and your games won't
run at all.

Now you can run higan.app. The first thing you'll want to do is go to
higan->Preferences... and assign inputs for your gamepads. At the very
least, do it for the default controller for all the systems you want to
emulate.

Now this is very important ... close the application at this point so
that it writes your config file to disk. There's a serious crashing bug,
and if you trigger it, you'll lose your input bindings.

Now the really annoying part ... go to Library->{System} and pick the
game you want to play. Right now, there's a ~50% chance the application
will bomb. It seems the hiro::pListView object is getting destroyed, yet
somehow the internal Cocoa callbacks are being triggered anyway. I don't
know how this is possible, and my attempts to debug with lldb have been
a failure :(

If you're unlucky, the application will crash. Restart and try again. If
it crashes every single time, then you can try launching your game from
the command-line instead. Example:

    open /Applications/higan.app \
	--args ~/Emulation/Super\ Famicom/Zelda3.sfc/

Help wanted:
I could really, really, really use some help with that crashing on game
loading. There's a lot of rough edges, but they're all cosmetic. This
one thing is pretty much the only major show-stopping issue at the
moment, preventing a wider general audience pre-compiled binary preview.
2016-01-07 19:17:15 +11:00
Tim Allen
47d4bd4d81 Update to v096r01 release.
byuu says:

Changelog:

- restructured the project and removed a whole bunch of old/dead
  directives from higan/GNUmakefile
- huge amounts of work on hiro/cocoa (compiles but ~70% of the
  functionality is commented out)
- fixed a masking error in my ARM CPU disassembler [Lioncash]
- SFC: decided to change board cic=(411,413) back to board
  region=(ntsc,pal) ... the former was too obtuse

If you rename Boolean (it's a problem with an include from ruby, not
from hiro) and disable all the ruby drivers, you can compile an
OS X binary, but obviously it's not going to do anything.

It's a boring WIP, I just wanted to push out the project structure
change now at the start of this WIP cycle.
2015-12-30 17:54:59 +11:00
Tim Allen
27660505c8 Update to v096 release.
byuu says:

Changelog (since v095):

- higan: absolutely massive amounts of coding style updates; probably
  150 hours of work here
- higan: manifest format updated for much greater consistency and
  simplicity
- higan: wrote popen() replacement to suppress console flashing when
  loading games via icarus
- icarus: now includes external database with mapping information for
  all verified games
- icarus: added support for importing Campus Challenge '92 and Powerfest
  '94
- icarus: merged settings.bml with higan; changing library path in one
  affects the other now
- SFC: added MSU1 audio resume support
- SFC: added new expansion port device (eBoot); simulation of SNES-Boot
  hardware
- SFC: expansion port device can now be selected from system menu
- SFC: updated handling of open bus (thanks to Exophase for the design
  idea)
- SFC: "BS-X Satellaview" library folder renamed to "BS Memory"
- GBA: fixed 8-bit SRAM reading/writing
- GBA: PRAM is 16-bits wide
- GBA: VRAM OBJ 8-bit writes are ignored
- GBA: BGnCNT unused bits are writable
- GBA: BG(0,1)CNT can't set d13
- GBA: BLDALPHA is readable (fixes many games including Donkey Kong
  Country)
- GBA: DMA masks &~1/Half, &~3/Word
- GBA: fixed many other I/O register reads; gets perfect score on
  endrift's I/O tests
- GBA: fixed caching of r(d) to pass armwrestler tests (Jonas Quinn)
- GBA: blocked DMA to/from BIOS region (Cydrak)
- GBA: fixed sign-extend and rotate on ldrs instructions (Cydrak)
- tomoko: added "Ignore Manifests" option to advanced settings panel
- tomoko: re-added support for ruby/quark video shaders
- tomoko: improved aspect correction behavior
- tomoko: added new tool, "Manifest Viewer" (mostly useful for
  developers)
- ruby: fixed mouse capture clipping on Windows (Cydrak)
- ruby: won't crash when using OpenGL 3.2 Linux driver with only OpenGL
  2.0 available
- ruby: added Linux fallback OpenGL 2.0 driver (not compiled in by
  default)
- ruby: added preliminary WASAPI driver (not compiled in by default, due
  to bugginess)
- hiro: fixed the appearance of Button and ListView::CheckButton on
  Windows classic
- hiro: added missing return values from several functions (fixes
  crashes with Clang)
2015-12-21 23:21:31 +11:00
Tim Allen
702b657e75 Update to v095r18 release.
byuu says:

Changelog:
- replaced popen() with execvp() / CreateProcess()
- suppressed (hid) controllers with no mappable inputs from the input
  settings panel

This gets rid of the window flashing when loading games with
higan+icarus. And hiding of empty devices should be a huge usability
improvement, especially since "None" was appearing at the top of the
list before for the SNES.
2015-12-21 20:16:47 +11:00
Tim Allen
0253db8685 Update to higan and icarus v095r17 release.
byuu says:

higan supports Event mapping again.

Further, icarus can now detect Event ROMs and MSU1 games.

Event ROMs must be named "program.rom", "slot-(1,2,3).rom" MSU1 games
must contain "msu1.rom"; and tracks must be named "track-#.pcm"

When importing the CC'92, PF'94 ROMs, the program.rom and
slot-(1,2,3).rom files must be concatenated. The DSP firmware can
optionally be separate, but I'd recommend you go ahead and merge it all
to one file. Especially since that common "higan DSP pack" floating
around on the web left out the DSP1 ROMs (only has DSP1B) for god knows
what reason.

There is no support for loading "game.sfc+game.msu+game-*.pcm", because
I'm not going to support trying to pull in all of those files through
importing. Games will have to be distributed as game folders to use
MSU1. The MSU1 icarus support is simply so your game folders won't
require an unstable manifest.bml file to be played. So once they're in
there, they are good for life.

Note: the Event sizes in icarus' SFC heuristics are wrong for appended
firmware. Change from 0xXX8000 to 0xXX2000 and it works fine. Will be
fixed in r18.

Added Sintendo's flickering fixes. The window one's a big help for
regular controls, but the ListView double buffering does nothing for me
on Windows 7 :( Fairly sure I know why, but too lazy to try and fix that
now.

Also fixes the mMenu thing.
2015-12-20 13:53:40 +11:00
Tim Allen
2a4eb1cfc8 Update to higan and icarus v095r16 release.
byuu says (in the WIP forum):

    Changelog:
    - satellaviewcartridge/SatellaviewCartridge is now bsmemory/BSMemory
    - Emulation/BS-X Satellaview/ is now Emulation/BS Memory/
    - masking is in for MCC's mcu (awful hack in the code, but that's
      temporary)
    - BS Memory types are now "flash" or "mrom"
    - fixed loading Same Game - Tengai Hen
    - icarus fixed up a lot; can load database entries for any supported
      media type now (only the SFC DB exists currently)

    mMenu::remove() fix will be in the next WIP.

byuu says (in the public beta thread):

    Changelog:
    - GBA emulation accuracy improved quite a bit
    - video shaders are supported once again
    - icarus shares settings.bml with higan; changing library path in
      one now affects the other
    - icarus manifest generation now uses my SNES game dumping database
      for perfect mapping of US games
    - major overhaul to manifest file format. As long as you have
      v095-style folders without manifest.bml, you will be fine
      - if not, go to higan->settings->configuration->advanced and check
	"Ignore Manifests" before loading your first game
    - new "Manifest Viewer" tool (not really meant for regular users;
      more of a developer tool)
    - experimental (but disabled in the binary) WASAPI driver. Help
      stabilizing it would be *greatly* appreciated!
    - lots of other stuff
2015-12-19 21:42:18 +11:00
Tim Allen
bd628de3cf Update to higan and icarus v095r15 release.
r13 and r14 weren't posted as individual releases, but their changelogs
were posted.

byuu says about r13:

    I'm not going to be posting WIPs for r13 and above for a while.

    The reason is that I'm working on the major manifest overhaul I've
    discussed previously on the icarus subforum.

    I'm recreating my boards database from scratch using the map files
    and the new map analyzer. The only games that will load are ones
    I've created board definitions for, and updated
    sfc/cartridge/markup.cpp to parse. Once I've finished all the
    boards, then I'll update the heuristics.

    Then finally, I'll sync the syntax changes over to the fc, gb, gba
    cores.

    Once that's done, I'll start posting WIPs again, along with a new
    build of icarus.

    But I'll still post changelogs as I work through things.

    Changelog (r13):
    - preservation: created new database-builder tool (merges
      region-specific databases with boards)
    - icarus: support new, external database format
      (~/.config/icarus/Database/(Super Famicom.bml, ...)
    - added 1A3B-(10,11,12); 1A3B-20

byuu says about r14:

    r14 work:

    I successfully created mappings for every board used in the US set.

    I also updated icarus' heuristics to use the new mappings, and
    created ones there for the boards that are only in the JP set.

    Then I patched icarus to support pulling games out of the database
    when it's used on a game folder to generate a manifest file.

    Then I updated a lot of code in higan/sfc to support the new mapping
    syntax. sfc/cartridge/markup.cpp is about half the size it used to
    be with the new mappings, and I was able to kill off both map/id and
    map/select entirely.

    Then I updated all four emulated systems (and both subsystems) to
    use "board" as the root node, and harmonized their syntax (made them
    all more consistent with each other.)

    Then I added a manifest viewer to the tools window+menu. It's kind
    of an advanced user feature, but oh well. No reason to coddle people
    when the feature is very useful for developers. The viewer will show
    all manifests in order when you load multi-cart games as well.

    Still not going to call any syntax 100% done right now, but
    thankfully with the new manifest-free folders, nobody will have to
    do anything to use the new format. Just download the new version and
    go.

    The Super Famicom Event stuff is currently broken (CC92/PF94
    boards). That's gonna be fun to support.

byuu says about r15:

    EDIT: small bug in icarus with heuristics. Edit
    core/super-famicom.cpp line 27:

	if(/*auto*/ markup = cartridge.markup) {

    Gotta remove that "auto" so that it returns valid markup.

    Resolved the final concerns I had with the new manifest format.

    Right now there are two things that are definitely broken: MCC (BS-X
    Town cart) and Event (CC '92 and PF'94).
    And there are a few things that are untested: SPC7110, EpsonRTC,
    SharpRTC, SDD1+RAM, SufamiTurbo, BS-X slotted carts.
2015-12-19 20:02:06 +11:00
Tim Allen
2c53d5fbc0 Update to v095r12 release.
byuu says:

Got it. They broke in r05.

Changelog:
- fixed typo in sfc/cpu/timing.cpp that was breaking coprocessor games
  with clocks
- updated sfc/coprocessor/hitachidsp to not access Bus directly
2015-12-15 20:30:26 +11:00
Tim Allen
f2a416aea9 Update to v095r11 release.
byuu says:

Changelog:
- SFC: "uint8 read(uint addr)" -> "uint8 read(uint addr, uint8 data)"
- hiro: mHorizontalLayout::setGeometry() return value
- hiro/GTK: ListView,TreeView::setFocused() does not grab focus of first
  item

Notes:
- nall/windows/utf8.hpp needs using uint = unsigned; at the top to
  compile
- sfc/balanced, sfc/performance won't compile yet

Seems Cx4 games broke a while back. Not from this WIP, either. I'll go
back and find out what's wrong now.
2015-12-14 20:41:06 +11:00
Tim Allen
78d49d3873 Update to v095r10 release.
byuu says:

Changelog:

- int_t<bits> replaced with Integer<bits>
- uint_t<bits> replaced with Natural<bits>
- fixed "Synchronize Audio" menu option that broke recently
- all of sfc/performance ported to "auto function() -> return;" syntax

With this WIP, all of higan is finally ported over to the new function
declaration syntax. Thank the gods.

There's still going to be periodic disruption for diffs from porting
over signed->int, unsigned->uint, and whatever we come up with for the
new Natural<> and Integer<> classes. But the worst of it's behind us
now.
2015-12-07 08:11:41 +11:00
Tim Allen
65a3306ad5 Update to v095r09 release.
byuu says:

Changelog:

- all of fc/ ported to "auto function() -> return;" syntax
  - (includes all of cartridge/board and cartridge/chip as well; even
    though they're all deprecated)
- sfc balanced profile ported to "auto function() -> return;" syntax
- sfc balanced and performance profiles compile again
- Linux always gets -ldl
- removed arch=x86 logic from nall/GNUmakefile, as TDM/GCC64 can't
  produce bug-free 32-bit binaries anyway

The only code that continues to use the old function syntax is the SFC
performance core, obscure parts of nall that higan doesn't use, and the
pieces of code that weren't written by me (blargg's SFC-DSP, Ryphecha's
sinc resampler, and OV2's xaudio2 header file.)

I was too burned out to finish it tonight. The above was about four
hours straight of non-stop typing. Really can't wait to be done with
this once and for all.
2015-12-05 16:44:49 +11:00
Tim Allen
a219f9c121 Update to v095r08 release.
byuu says:

Changelog:
- added preliminary WASAPI driver (it's really terrible, though. Patches
  most welcome.)
- all of processor/ updated to auto fn() -> ret syntax
- all of gb/ updated to auto fn() -> ret syntax

If you want to test the WASAPI driver, then edit ui-tomoko/GNUmakefile,
and replace audio.xaudio2 with audio.wasapi Note that the two drivers
are incompatible and cannot co-exist (yet. We can probably make it work
in the future.)

All that's left for the auto fn() -> ret syntax is the NES core and the
balanced/performance SNES components. This is kind of a big deal because
this syntax change causes diffs between WIPs to go crazy. So the sooner
we get this done and out of the way, the better. It's also nice from
a consistency standpoint, of course.
2015-11-21 18:36:48 +11:00
Tim Allen
6adfe71836 Update to icarus 20151117.
byuu says:

This release adds a settings dialog that lets you control the library
path, optionally generate manifest.bml files, and optionally bypass the
internal games database (so far this is only the US SNES set.)

Also, the settings.bml file can exist in the same folder as the binary
now (portable mode). Plus it can share the same config file as
higan/tomoko itself does. This will allow you to change the library
location in either program and have it affect the other program as well.
It's a bit hackish, but it works >_>

Note: don't use this with higan v095.06 or earlier, or bad things will
happen.
2015-11-19 20:27:56 +11:00
Tim Allen
41c478ac4a Update to v095r07 release.
byuu says:

Changelog:
- entire GBA core ported to auto function() -> return; syntax
- fixed GBA BLDY bug that was causing flickering in a few games
- replaced nall/config usage with nall/string/markup/node
  - this merges all configuration files to a unified settings.bml file
- added "Ignore Manifests" option to the advanced setting tab
  - this lets you keep a manifest.bml for an older version of higan; if
    you want to do regression testing

Be sure to remap your controller/hotkey inputs, and for SNES, choose
"Gamepad" from "Controller Port 1" in the system menu. Otherwise you
won't get any input. No need to blow away your old config files, unless
you want to.
2015-11-16 19:38:05 +11:00
Tim Allen
40f4b91000 Update to v095r06 release.
byuu says:

Changelog:
- fixed I/O register reads; perfect score on endrift's I/O tests now
- fixed mouse capture clipping on Windows [Cydrak]
- several hours of code maintenance work done on the SFC core

All higan/sfc files should now use the auto fn() -> ret; syntax. Haven't
converted all unsigned->uint yet. Also, probably won't do sfc/alt as
that's mostly just speed hack stuff.

Errata:
- forgot auto& instead of just auto on SuperFamicom::Video::draw_cursor,
  which makes Super Scope / Justifier crash. Will be fixed in the next
  WIP.
2015-11-14 11:52:51 +11:00
Tim Allen
6d9f43a37b Update to v095r05 release.
byuu says:

Changelog:
- GBA: lots of emulation improvements
- PPU PRAM is 16-bits wide
- DMA masks &~1/Half, &~3/Word
- VRAM OBJ 8-bit writes are ignored
- OAM 8-bit writes are ignored
- BGnCNT unused bits are writable*
- BG(0,1)CNT can't set the d13
- BLDALPHA is readable (fixes Donkey Kong Country, etc)
- SNES: lots of code cleanups
- sfc/chip => sfc/coprocessor
- UI: save most recent controller selection

GBA test scores: 1552/1552, 37/38, 1020/1260

(* forgot to add the value to the read function, so endrift's I/O tests
for them will fail. Fixed locally.)

Note: SNES is the only system with multiple controller/expansion port
options, and as such is the only one with a "None" option. Because it's
shared by the controller and expansion port, it ends up sorted first in
the list. This means that on your first run, you'll need to go to Super
Famicom->Controller Port 1 and select "Gamepad", otherwise input won't
work.

Also note that changing the expansion port device requires loading a new
cart. Unlike controllers, you aren't meant to hotplug expansion port
devices.
2015-11-12 21:15:03 +11:00
Tim Allen
d1ffd59c29 Update to v095r04 release.
Changelog:
- S-SMP core code style updated
- S-SMP loads reset vector from IPLROM ($fffe-ffff)
- sfc/base => sfc/expansion
- system/input => system/device
- added expansion/eBoot (simulation of defparam's SNES-Boot device)
- expansion port device can now be selected from Super Famicom menu
  option
- improved GBA MROM/SRAM reading

endrift's memory test is up to 1388/1552.

Note: I added the expansion port devices to the same group as controller
ports. I also had to move "None" to the top of the list. Before v096,
I am going to have to add caching of port selections to the
configuration file, check the proper default item in the system menu,
and remove the items with no mappings from the input configuration
window. Lots of work >_>
2015-11-10 22:11:29 +11:00
Tim Allen
0fe55e3f5b Update to v095r03 release and icarus 20151107.
byuu says:

Note: you will need the new icarus (and please use the "no manifest"
system) to run GBA games with this WIP.

Changelog:
- fixed caching of r(d) to pass armwrestler tests [Jonas Quinn]
- DMA to/from GBA BIOS should fail [Cydrak]
- fixed sign-extend and rotate on ldrs instructions [Cydrak]
- fixed 8-bit SRAM reading/writing [byuu]
- refactored GBA/cartridge
  - cartridge/rom,ram.type is now cartridge/mrom,sram,eeprom,flash
  - things won't crash horribly if you specify a RAM size larger than
    the largest legal size in the manifest
  - specialized MROM / SRAM classes replace all the shared read/write
    functions that didn't work right anyway
- there's a new ruby/video.glx2 driver, which is not enabled by default
  - use this if you are running Linux/BSD, but don't have OpenGL 3.2 yet
  - I'm not going to support OpenGL2 on Windows/OS X, because these OSes
    don't ship ancient video card drivers
- probably more. What am I, clairvoyant? :P

For endrift's tests, this gets us to 1348/1552 memory and 1016/1260
timing. Overall, this puts us back in second place. Only no$ is ahead
on memory, but bgba is even more ahead on timing.
2015-11-10 22:11:29 +11:00
Tim Allen
b42ab2fcb3 Update to v095r02 release.
byuu says:

Aspect correction is fixed now. Works way better than in v095 official.

It's still force-enabled in fullscreen mode. The idea of disabling it is
that it looks bad at 2x scale. But when you're fullscreen with a minimum
of 4x scale, there's no reason not to enable it.

It won't turn on at all for GB/C/A anymore. And I dropped the cute
attempt at making the aspect prettier on 2560x1600 monitors, so it'll be
the stock 8:7 across the board now for S/NES.

Also, the aspect correction will affect the window even when a game's
not loaded now, so the size won't bounce around as you change games in
windowed mode between GB/C/A and S/NES.

...

I also enhanced the ruby/glx driver. It won't crash if OpenGL 3.2 isn't
available anymore (fails safely ... had to capture the Xlib error
handler to suppress that), and it defaults to the MESA glXSwapInterval
before the SGI version. Because apparently the MESA version defines the
SGI version, but makes it a no-op. What. The. Fuck. right? But whatever,
reordering the enumerations fixes the ability to toggle Vsync on AMD
GPUs now.

...

Video shaders are back again. If you are using the OpenGL driver, you'll
see a "Video Shaders" menu beneath the "Video Filters" menu (couldn't
merge it with the filters due to hiro now constructing menu ordering
inside the header files. This works fine though.)

You want either "higan.exe" + "Video Shaders/" or "~/.local/bin/tomoko"
+ "~/.local/tomoko/Video Shaders/"
2015-11-10 22:07:34 +11:00
Tim Allen
8476a12deb Update to v095r01 release (open beta).
byuu says:

Changelog:
- added MSU1 resume support
- updated sfc/dsp, sfc/controller to match my coding style
- fixed hiro/Windows Button and ListView::CheckButton in Windows Classic
  mode
2015-10-10 13:16:12 +11:00
Tim Allen
b0e862613b Update to v095 release.
byuu says:

After 20 months of development, higan v095 is released at long last!

The most notable feature is vastly improved Game Boy Advance emulation.
With many thanks to endrift, Cydrak, Jonas Quinn and jchadwick, this
release contains substantially improved CPU timings and many bugfixes.
Being one of only two GBA emulators to offer ROM prefetch emulation,
higan is very near mGBA in terms of accuracy, and far ahead of all
others. As a result of these fixes, compatibility is also much higher
than in v094.

There are also several improvements to SNES emulation. Most
significantly is support for mid-scanline changes to the background mode
in the accuracy profile.

Due to substantial changes to the user interface library used by higan,
this release features yet again a brand-new UI. With the exception of
video shaders and NSS DIP switch selection, it is at feature-parity with
the previous UI. It also offers some new features that v094 lacked.

The cheat code database has also been updated to the latest version by
mightymo.
2015-10-08 22:04:42 +11:00
Tim Allen
b113ecb5a3 Deleting ananke and shaders.
ananke has been superseded by icarus.

The new tomoko UI does not support shaders, and if it ever does it will
probably use another format, so not much point keeping the old files
around.
2015-10-08 22:02:22 +11:00
Tim Allen
bc5ad4a1cd Update to icarus_20151002.
byuu says:

- fixes checkboxes (-again- again [*again*])
- won't check folders with select all / unselect all
- won't crash anymore if the SNES ROM image is too small (Saturday Night
  Slam Masters was crashing it before due to DB size error)
- corrected heuristics for Sufami Turbo base cart (mirrors the
  absurdities of the real cart precisely, since it's one of a kind)
- corrected a few DB issues (BS-X name + PSRAM (again [*again*]), SNSM,
  LAH) (_again_)
  - these are temporary. Monkey patched in the generated .hpp source
    rather than the actual DB
  - not going to fix the SFT sizes because I want to verify what
    happened there first
2015-10-03 16:25:39 +10:00
Tim Allen
1a90e206e0 Update to v094r44b release (open beta).
byuu says:

With any luck, this will be the final WIP before v095. If all looks
good, this will be identical to v095. But if we hit some major issues,
I'll try and fix those first.

The most notable part of this release is probably Jonas Quinn's fix for
the unmapped regions of the GBA memory map. This allows games like Mario
& Luigi and Zelda: Minish Cap to (hopefully) be fully playable now.

icarus now supports my game database, so all games I've dumped will be
emulated with bit-perfect memory maps and native-language game titles.
2015-10-01 20:04:30 +10:00
Tim Allen
483fc81356 Update to v094r44 release.
byuu says:

Changelog:
- return open bus instead of mirroring addresses on the bus (fixes
  Mario&Luigi, Minish Cap, etc) [Jonas Quinn]
- add boolean flag to load requests for slotted game carts (fixes slot
  load prompts)
- rename BS-X Town cart from psram to ram
- icarus: add support for game database

Note: I didn't rename "bsx" to "mcc" in the database for icarus before
uploading that. But I just fixed it locally, so it'll be in the next
WIP. For now, make it create the manifest for you and then rename it
yourself. I did fix the PSRAM size to 256kbit.
2015-10-01 20:00:28 +10:00
Tim Allen
0c87bdabed Update to v094r43 release.
byuu says:

Updated to compile with all of the new hiro changes. My next step is to
write up hiro API documentation, and move the API from alpha (constantly
changing) to beta (rarely changing), in preparation for the first stable
release (backward-compatible changes only.)

Added "--fullscreen" command-line option. I like this over
a configuration file option. Lets you use the emulator in both modes
without having to modify the config file each time.

Also enhanced the command-line game loading. You can now use any of
these methods:

    higan /path/to/game-folder.sfc
    higan /path/to/game-folder.sfc/
    higan /path/to/game-folder.sfc/program.rom

The idea is to support launchers that insist on loading files only.

Technically, the file can be any name (manifest.bml also works); the
only criteria is that the file actually exists and is a file, and not
a directory. This is a requirement to support the first version (a
directory lacking the trailing / identifier), because I don't want my
nall::string class to query the file system to determine if the string
is an actual existing file or directory for its pathname() / dirname()
functions.

Anyway, every game folder I've made so far has program.rom, and that's
very unlikely to change, so this should be fine.

Now, of course, if you drop a regular "game.sfc" file on the emulator,
it won't even try to load it, unless it's in a folder that ends in .fc,
.sfc, etc. In which case, it'll bail out immediately by being unable to
produce a manifest for what is obviously not really a game folder.
2015-08-30 12:08:26 +10:00
Tim Allen
c45633550e Update to v094r42 release.
byuu says:

I imagine you guys will like this WIP very much.

Changelog:
- ListView check boxes on Windows
- ListView removal of columns on reset (changing input dropdowns)
- DirectSound audio duplication on latency change
- DirectSound crash on 20ms latency
- Fullscreen window sizing in multi-monitor setups
- Allow joypad bindings of hotkeys
- Allow triggers to be mapped (Xbox 360 / XInput / Windows only)
- Support joypad rumble for Game Boy Player
- Video scale settings modified from {1x,2x,3x} to {2x,3x,4x}
- System menu now renames to active emulation core
- Added fast forward hotkey

Not changing for v095:
- not adding input focus settings yet
- not adding shaders yet

Not changing at all:
- not implementing maximize
2015-08-24 19:42:11 +10:00
Tim Allen
7081f46e45 Added icarus 20150821. 2015-08-21 21:29:53 +10:00
Tim Allen
213879771e Update to v094r41 release (open beta).
byuu says:

Changelog (since the last open beta):
- icarus is now included. icarus is used to import game files/archives
  into game paks (folders)
- SNES: mid-scanline BGMODE changes now emulated correctly (used only by
  atx2.smc Anthrox Demo)
- GBA: fixed a CPU bug that was causing dozens of games to have
  distorted audio
- GBA: fixed default FlashROM ID; should allow much higher compatibility
- GBA: now using Cydrak's new, much improved, GBA color emulation filter
  (still a work-in-progress)
- re-added command-line loading support for game paks (not for game
  files/archives, sorry!)
- Qt port now compiles and runs again (may be a little buggy;
  Windows/GTK+ ports preferred)
- SNES performance profile now compiles and runs again
- much more
2015-08-21 20:57:03 +10:00
Tim Allen
4344b916b6 Update to v094r40 release.
byuu says:

Changelog:
- updated to newest hiro API
- SFC performance profile builds once again
- hiro: Qt port completed

Errata 1: the hiro/Qt target won't run tomoko just yet. Starts by
crashing inside InputSettings because hiro/Qt isn't forcefully selecting
the first item added to a ComboButton just yet. Even with a monkey patch
to get around that, the UI is incredibly unstable. Lots of geometry
calculation bugs, and a crash when you try and access certain folders in
the browser dialog. Lots of work left to be done there, sadly.

Errata 2: the hiro/Windows port has black backgrounds on all ListView
items. It's because I need to test for unassigned colors and grab the
default Windows brush colors in those cases.

Note: alternating row colors on multi-column ListView widgets is gone
now. Not a bug. May add it back later, but I'm not sure. It doesn't
interact nicely with per-cell background colors.

Things left to do:

First, I have to fix the Windows and Qt target bugs.

Next, I need to go through and revise the hiro API even more (nothing
too major.)

Next, I need to update icarus to use the new hiro API, and add support
for the SFC games database.

Next, I have to rewrite my TSV->BML cheat code tool.

Next, I need to post a final WIP of higan+icarus publicly and wait a few
days.

Next, I need to fix any bugs reported from the final WIP that I can.

Finally, I should be able to release v095.
2015-08-18 20:18:00 +10:00
Tim Allen
0271d6a12b Update to v094r39 release.
byuu says:

Changelog:
- SNES mid-scanline BGMODE fixes finally merged (can run
  atx2.zip{mode7.smc}+mtest(2).sfc properly now)
- Makefile now discards all built-in rules and variables
- switch on bool warning disabled for GCC now as well (was already
  disabled for Clang)
- when loading a game, if any required files are missing, display
  a warning message box (manifest.bml, program.rom, bios.rom, etc)
- when loading a game (or a game slot), if manifest.bml is missing, it
  will invoke icarus to try and generate it
  - if that fails (icarus is missing or the folder is bad), you will get
    a warning telling you that the manifest can't be loaded

The warning prompt on missing files work for both games and the .sys
folders and their files. For some reason, failing to load the DMG/CGB
BIOS is causing a crash before I can display the modal dialog. I have no
idea why, and the stack frame backtrace is junk.

I also can't seem to abort the failed loading process. If I call
Program::unloadMedia(), I get a nasty segfault. Again with a really
nasty stack trace. So for now, it'll just end up sitting there emulating
an empty ROM (solid black screen.) In time, I'd like to fix that too.

Lastly, I need a better method than popen for Windows. popen is kind of
ugly and flashes a console window for a brief second even if the
application launched is linked with -mwindows. Not sure if there even is
one (I need to read the stdout result, so CreateProcess may not work
unless I do something nasty like "> %tmp%/temp") I'm also using the
regular popen instead of _wpopen, so for this WIP, it won't work if your
game folder has non-English letters in the path.
2015-08-04 19:02:04 +10:00
Tim Allen
1b0b54a690 Update to v094r38 release.
byuu says:

I'll post more detailed changes later, but basically:
- fixed Baldur's Gate bug
- guess if no flash ROM ID present (fixes Magical Vacation, many many
  others)
- nall cleanups
- sfc/cartridge major cleanups
- bsxcartridge/"bsx" renamed to mcc/"mcc" after the logic chip it uses
  (consistency with SGB/ICD2)
- ... and more!
2015-08-04 19:01:59 +10:00
Tim Allen
092cac9073 Update to v094r37 release.
byuu says:

Changelog:
- synchronizes lots of nall changes
- changes displayed program title from tomoko to higan(*)
- browser dialog sort is case-insensitive
- .sys folders look at user-selected library path; no longer hard-coded

Tried to get rid of the file modes from the Windows browser dialog, but
it was being a bitch so I left it on for now.

- The storage locations and binary still use tomoko. I'm not really sure
  what to do here. The idea is there may be more than one "higan" UI in
  the future, but I don't want people to go around calling the entire
  program by the UI name. For official Windows releases, I can rename
  the binaries to "higan-{profile}.exe", and by putting the config files
  with the binary, they won't ever see the tomoko folder. Linux is of
  course trickier.

Note: Windows users will need to edit hiro/components.hpp and comment
out these lines:

 #define Hiro_Console
 #define Hiro_IconView
 #define Hiro_SourceView
 #define Hiro_TreeView

I forgot to do that, and too lazy to upload another WIP.
2015-07-14 19:32:43 +10:00
Tim Allen
ecb35cac33 Update to v094r36 release (open beta).
byuu says:

Changelog:
- GBA emulation accuracy has been substantially improved [Cydrak]
- GBA ldm bug fixed [jchadwick]
- SNES SuperFX timing has been improved [AWJ, ARM9, qwertymodo]
- SNES accuracy profile is now ~8% faster than before
- you no longer need to copy the .sys profile folders to
  ~/Emulation/System
    - you still need to put bios.rom (GBA BIOS) into Game Boy
      Advance.sys to use GBA emulation!!
- you no longer need to pre-configure inputs before first use
- loading games / changing window size won't recenter window
- checkboxes in cheat editor update correctly
- can't type into state manager description textbox on an empty slot
- typing in state manager description box works correctly; and updates
  list view correctly
- won't show files that match game extensions anymore (only game folders
  show up)
- libco Win64 port fixes with FPU^H^H^H XMM registers
- libco ARM port now available; so you too can play at 15fps on an RPi2!
  [jessedog3, Cydrak]
- controller selection will check the default item in the menu now on
  game load
- as usual, a whole lot of other stuff I'm forgetting

Known issues:
- type-ahead find does not work in list views (eg game selection
  dialog); I don't know how to fix this
- there's no game file importer yet
- there's no shader support yet
- there's no profiler available for the timing panel, you need to adjust
  values manually for now
2015-07-02 20:24:56 +10:00
Tim Allen
28a14198cb Update to v094r35 release.
byuu says:

GBA timings are *almost* perfect now. Off by 1-3 cycles on each test,
sans a few DMA ones that seem to not run at all according to the numbers
(crazy.)
2015-07-01 20:58:42 +10:00
Tim Allen
7ff7f64482 Update to v094r34 release.
byuu says:

Fixes SuperFX fmult, lmult timings; rambr, bramr and clsr assignment
masking. Implements true GBA ROM prefetch (buggy, lower test score, but
runs Mario & Luigi without crashing on battles anymore.)
2015-06-28 18:44:56 +10:00
Tim Allen
4c9266d18f Update to v094r33 release.
byuu says:

Small WIP, just fixes the timings for GSU multiply.

However, the actual product may still be wrong when CLSR and MS0 are
both set. Since I wasn't 'corrupting' the value in said case before,
then this behavior can only be better than before.

Turned the (cache,memory)_access_timing into functions that compute the
values; and pulled "clockspeed" into GSU.

Also, I'm thinking it might be kind of pointless to have clockspeed at
all. Supposedly even the Mario Chip can run at 21.48MHz anyway.
Enforcing 10.74MHz mode seems kind of silly. If we change it to just be
a "default value for CLSR", then we can just inline the memory access
tests without the need for the access_timing functions (literally just
clsr?2:1 then)

Slight compilation bug: go to processor/gsu/registers.hpp:33 and add

    reg16_t() = default;

I missed it due to a partial recompile. Too lazy to upload another WIP
just for that.

Probably not worth doing much SuperFX testing just yet, as it looks like
they're doing some other tests at the moment on NESdev.
2015-06-27 12:38:47 +10:00
Tim Allen
169e400437 Update to v094r32 release.
byuu says:

Lots more timing improvements to GBA emulation. We're now ahead of
everything but mGBA.

Mario & Luigi is still hanging in battles, so I guess my prefetch
simulation isn't as good as Cydrak's previous attempt, no surprise.
2015-06-27 12:38:08 +10:00
Tim Allen
ea02f1e36a Update to v094r31 release.
byuu says:

This WIP scores 448/920 tests passed.

Gave a shot at ROM prefetch that failed miserably (ranged from 409 to
494 tests passed. Nowhere near where it would be if it were implemented
correctly.)

Three remaining issues:
- ROM prefetch
- DMA timing
- timers (I suspect it's a 3-clock delay in starting, not a 3-clock into
  the future affair)

Probably only going to be able to get the timers working without heroic
amounts of effort.

MUL timing is fixed to use idle cycles.
STMIA is fixed to set sequential at the right moments.
DMA priority support is added, so DMA 0 can interrupt DMA 1 mid-transfer.

In other news ...

I'm calling gtk_widget_destroy on the GtkWindow now, so hopefully those
Window_configure issues go away.

I realize I was leaking Display* handles in the X-video driver while
I was looking at it, so I fixed those.

I added DT_NOPREFIX so the Windows ListView will show & characters
correctly now.
2015-06-25 19:52:32 +10:00
Tim Allen
310ff4fa3b Update to v094r30 release.
byuu says:

This WIP does substantially better on endrift's GBA timing tests. Still
not perfect, though. But hopefully enough to get me out of dead last
place. I also finally fixed the THUMB-mode ldmia bug that jchadwick
reported.

So, GBA emulation should be improved quite a bit, hopefully.
2015-06-24 23:21:24 +10:00
Tim Allen
83f684c66c Update to v094r29 release.
byuu says:

Note: for Windows users, please go to nall/intrinsics.hpp line 60 and
correct the typo from "DISPLAY_WINDOW" to "DISPLAY_WINDOWS" before
compiling, otherwise things won't work at all.

This will be a really major WIP for the core SNES emulation, so please
test as thoroughly as possible.

I rewrote the 65816 CPU core's dispatcher from a jump table to a switch
table. This was so that I could pass class variables as parameters to
opcodes without crazy theatrics.

With that, I killed the regs.r[N] stuff, the flag_t operator|=, &=, ^=
stuff, and all of the template versions of opcodes.

I also removed some stupid pointless flag tests in xcn and pflag that
would always be true.

I sure hope that AWJ is happy with this; because this change was so that
my flag assignments and branch tests won't need to build regs.P into
a full 8-bit variable anymore.

It does of course incur a slight performance hit when you pass in
variables by-value to functions, but it should help with binary size
(and thus cache) by reducing a lot of extra functions. (I know I could
have used template parameters for some things even with a switch table,
but chose not to for the aforementioned reasons.)

Overall, it's about a ~1% speedup from the previous build. The CPU core
instructions were never a bottleneck, but I did want to fix the P flag
building stuff because that really was a dumb mistake v_v'
2015-06-22 23:31:49 +10:00
Tim Allen
e0815b55b9 Update to v094r28 release.
byuu says:

This WIP substantially restructures the ruby API for the first time
since that project started.

It is my hope that with this restructuring, destruction of the ruby
objects should now be deterministic, which should fix the crashing on
closing the emulator on Linux. We'll see I guess ... either way, it
removed two layers of wrappers from ruby, so it's a pretty nice code
cleanup.

It won't compile on Windows due to a few issues I didn't see until
uploading the WIP, too lazy to upload another. But I fixed all the
compilation issues locally, so it'll work on Windows again with the next
WIP (unless I break something else.)

(Kind of annoying that Linux defines glActiveTexture but Windows
doesn't.)
2015-06-20 15:44:05 +10:00
Tim Allen
20cc6148cb Update to v094r27 release.
byuu says:

Added AWJ's fixes for alt/cpu (Tetris Attack framelines issue) and
alt/dsp (Thread::clock reset)

Added fix so that the taskbar entry appears when the application first
starts on Windows.

Fixed checkbox toggling inside of list views on Windows.

Updated nall/image to properly protect variables that should not be
written externally.

New Object syntax for hiro is in.

Fixed the backwards-typing on Windows with the state manager.
NOTE: the list view isn't redrawing when you change the description
text. It does so on the cheat editor because of the resizeColumns call;
but that shouldn't be necessary. I'll try and fix this for the next WIP.
2015-06-18 20:48:53 +10:00
Tim Allen
a21ff570ee Update to v094r26 release (open beta).
byuu says:

Obviously, this is a fairly major WIP. It's the first public release in
17 months. The entire UI has been rewritten (for the 74th time), and is
now internally called tomoko. The official releases will be named higan
(both the binaries and title bar.)

Missing features from v094:

- ananke is missing (this means you will need v094 to create game
  folders to be loaded)
- key assignments are limited to one physical button = one mapping (no
  multi-mapping)
- shader support is missing
- audio/video profiling is missing
- DIP switch window is missing (used by NSS Actraiser with a special
  manifest; that's about it)
- alternate paths for game system folders and configuration BML files

There's some new stuff, but not much. This isn't going to be an exciting
WIP in terms of features. It's more about being a brand new release with
the brand new hiro port and its shared memory model. The goal is to get
these WIPs stable, get v095 out, and then finally start improving the
actual emulation again after that.
2015-06-16 20:30:04 +10:00
Tim Allen
bb3c69a30d Update to v094r25 release.
byuu says:

Windows port should run mostly well now, although exiting fullscreen
breaks the application in a really bizarre way. (clicking on the window
makes it sink to background rather than come to the foreground o_O)

I also need to add the doModalChange => audio.clear() thing for the
accursed menu stuttering with DirectSound.

I also finished porting all of the ruby drivers over to the newer API
changes from nall.

Since I can't compile the Linux or OS X drivers, I have no idea if there
are any typos that will result in compilation errors. If so, please let
me know where they're at and I'll try and fix them. If they're simple,
please try and fix them on your end to test further if you can.

I'm hopeful the udev crash will be gone now that nall::string checks for
null char* values passed to its stringify function. Of course, it's
a problem it's getting a null value in the first place, so it may not
work at all.

If you can compile on Linux (or by some miracle, OS X), please test each
video/audio/input driver if you don't mind, to make sure there's no
"compiles okay but still typos exist" bugs.
2015-06-16 20:30:04 +10:00
Tim Allen
f0c17ffc0d Update to v094r24 release.
byuu says:

Finally!! Compilation works once again on Windows.

However, it's pretty buggy. Modality isn't really working right, you can
still poke at other windows, but when you select ListView items, they
redraw as empty boxes (need to process WM_DRAWITEM before checking
modality.)

The program crashes when you close it (probably a ruby driver's term()
function, that's what it usually is.)

The Layout::setEnabled(false) call isn't working right, so you get that
annoying chiming sound and cursor movement when mapping keyboard keys to
game inputs.

The column sizing seems off a bit on first display for the Hotkeys tab.

And probably lots more.
2015-06-16 20:30:04 +10:00
Tim Allen
314aee8c5c Update to v094r23 release.
byuu says:

The library window is gone, and replaced with
hiro::BrowserWindow::openFolder(). This gives navigation capabilities to
game loading, and it also completes our slotted cart selection code. As
an added bonus, it's less code this way, too.

I also set the window size to consistent sizes between all emulated
systems, so that switching between SFC and GB don't cause the window
size to keep changing, and so that the scaling size is consistent (eg at
normal scale, GB @ 3x is closer to SNES @ 2x.) This means black borders
in GB/GBA mode, but it doesn't look that bad, and it's not like many
people ever use these modes anyway.

Finally, added the placeholder tabs for video, audio and timing. I don't
intend to add the timing calculator code to v095 (it might be better as
a separate tool), but I'll add the ability to set video/audio rates, at
least.

Glitch 1: despite selecting the first item in the BrowserDialog list, if
you press enter when the window appears, it doesn't activate the item
until you press an arrow key first.

Glitch 2: in Game Boy mode, if you set the 4x window size, it's not
honoring the full requested height because the viewport is smaller than
the window. 8+ years of trying to get GTK+ and Qt to simply set the god
damned window size I ask for, and I still can't get them to do it
reliably.

Remaining issues:
- finish configuration panels (video, audio, timing)
- fix ruby driver compilation on Windows
- add DIP switch selection window (NSS) [I may end up punting this one
  to v096]
2015-06-16 20:29:47 +10:00
Tim Allen
7bf4cff946 Update to v094r22 release.
byuu says:

I fixed the hiro layout enable bug, so when you go to assign joypad
input, the window disables itself so your input doesn't mess with the
controls.

I added "reset" to the hotkeys, in case you feel like clearing all of
them at once.

I added device selection support and the ability to disable audio
synchronization (run > 60fps) to the ruby/OSS driver. This is exposed in
tomoko's configuration file.

I added checks to stringify so that assigning null char* strings to
nall::string won't cause crashes anymore (technically the crash was in
strlen(), which doesn't check for null strings, but whatever ... I'll do
the check myself.)

I hooked up BrowserDialog::folderSelect() to loading slotted media for
now. Tested it by loading a Game Boy game successfully through the Super
Game Boy. Definitely want to write a custom window for this though, that
looks more like the library dialog.

Remaining issues:
- finish slotted cart loader (SGB, BSX, ST)
- add DIP switch selection window (NSS) [I may end up punting this one
  to v096]
- add more configuration panels (video, audio, timing)
2015-05-30 21:40:07 +10:00
Tim Allen
99b2b4b57c Update to v094r21 release.
byuu says:

This updates ruby to return shared_pointer<HID::Device> objects instead
of HID::Device* objects. It also fixes an ID bug where joypads were
starting at ID# 2+, but mice were also set to ID# 2. I also revised
nall/hid a lot, with getters and setters instead of stabbing at internal
state. I didn't yet patch nall::string to safely consume nullptr const
char* values, though.
2015-05-24 19:44:28 +10:00
Tim Allen
4e0223d590 Update to v094r20 release.
byuu says:

Main reason for this WIP was because of all the added lines to hiro for
selective component disabling. May as well get all the diff-noise apart
from code changes.

It also merges something I've been talking to Cydrak about ... making
nall::string::(integer,decimal) do built-in binary,octal,hex decoding
instead of just failing on those. This will have fun little side effects
all over the place, like being able to view a topic on my forum via
"forum.byuu.org/topic/0b10010110", heh.

There are two small changes to higan itself, though. First up, I fixed
the resampler ratio when loading non-SNES games. Tested and I can play
Game Boy games fine now. Second, I hooked up menu option hiding for
reset and controller selection. Right now, this works like higan v094,
but I'm thinking I might want to show the "Device -> Controller" even if
that's all that's there. It kind of jives nicer with the input settings
window to see the labels there, I think. And if we ever do add more
stuff, it'll be nice that people already always expect that menu there.

Remaining issues:
* add slotted cart loader (SGB, BSX, ST)
* add DIP switch selection window (NSS)
* add timing configuration (video/audio sync)
2015-05-23 15:37:08 +10:00
Tim Allen
458775a481 Update to v094r19 release.
byuu says:

The input port menu was hooked up.

Alternate input support was added, although I wasn't able to test rumble
support because SDL doesn't support that, and I don't have XInput or
udev drivers on FreeBSD. This one's going to be tricky. Maybe I can test
via cross-compiling on Windows/GTK.

Added mouse capture hotkey, and auto capture/release on toggling
fullscreen (as a bonus it hides the mouse cursor.)

Added all possible video and input drivers to ruby for BSD systems.

Remaining issues before we can release v095:
- add slotted cart loader (SGB, BSX, ST)
- add DIP switch selection window (NSS)
- add timing configuration (video/audio sync)
- hide inapplicable options from system menu (eg controller ports and
  reset button from handheld systems)
2015-05-23 15:29:18 +10:00
Tim Allen
fc8eba133d Update to v094r18 release.
byuu says:

Okay yeah, lots of SNES coprocessor games were horribly broken. They
should be fixed now with the below changes:

Old syntax:

    auto programROM = root["rom[0]/name"].text();
    auto dataROM = root["rom[1]/name"].text();
    load_memory(root["ram[0]"]);

New syntax:

    auto rom = root.find("rom");
    auto ram = root.find("ram");
    auto programROM = rom(0)["name"].text();
    auto dataROM = rom(1)["name"].text();
    load_memory(ram(0));

Since I'm now relying on the XShm driver, which is multi-threaded, I'm
now compiling higan with -fopenmp. On FreeBSD, this requires linking
with -Wl,-rpath=/usr/local/lib -Wl,-rpath=/usr/local/lib/gcc49 to get
the right version of GOMP.

This gives a pretty nice speed boost for XShm, I go from around 101fps
to 111fps at 4x scale on the accuracy profile. The combination of
inlining the accuracy-PPU and parallelizing the XShm renderer about
evenly compensates now for the ~20% CPU overclock I gave up a while ago.

The WIP also has some other niceties from the newer version of nall.
Most noticeably, cheat code database searching is now instantaneous. No
more 3-second stall.
2015-05-16 17:37:13 +10:00
Tim Allen
39ca8a2fab Update to v094r17 release.
byuu says:

This updates higan to use the new Markup::Node changes. This is a really
big change, and one slight typo anywhere could break certain classes of
games from playing.

I don't have ananke hooked up again yet, so I don't have the ability to
test this much. If anyone with some v094 game folders wouldn't mind
testing, I'd help out a great deal.

I'm most concerned about testing one of each SNES special chip game.
Most notably, systems like the SA-1, HitachiDSP and NEC-DSP were using
the fancier lookups, eg node["rom[0]/name"], which I had to convert to
a rather ugly node["rom"].at(0)["name"], which I'm fairly confident
won't work. I'm going to blame that on the fumes from the shelves I just
stained >.> Might work with node.find("rom[0]/name")(0) though ...? But
so ugly ... ugh.

That aside, this WIP adds the accuracy-PPU inlining, so the accuracy
profile should run around 7.5% faster than before.
2015-05-16 17:36:22 +10:00
Tim Allen
c335ee9d80 Update to v094r16 release.
byuu says:

Finished the cheat code system, it'll now load and save cheats.bml to
disk.

Also hooked up overscan masking. But for now you can only configure the
amount it clips via the configuration file, since I don't have a video
settings dialog anymore.

And that's the last of the low-hanging fruit. The remaining items are
all going to be a pain in the ass for one reason or another.

Short-term:
- add input port changing support
- add other input types (mouse-based, etc)

Long-term:
- add slotted cart loader (SGB, BSX, ST)
- add DIP switch selection window (NSS)
- add timing configuration (video/audio sync)

Not planned:
- video color adjustments (will allow emulated color vs raw color; but
  no more sliders)
- pixel shaders
- ananke integration (will need to make a command-line version to get my
  games in)
- fancy audio adjustment controls (resampler, latency, volume)
- input focus settings
- localization support (not enough users)
- window geometry memory
- anything else not in higan v094
2015-04-21 21:58:59 +10:00
Tim Allen
2eb50fd70b Update to v094r15 release.
byuu says:

Implemented the cheat database dialog, and most of the cheat editor
dialog. I still have to handle loading and saving the cheats.bml file
for each game. I wanted to finish it today, but I burned out. It's a ton
of really annoying work to support cheat codes. There's also some issue
with the width calculation for the "code(s)" column in hiro/GTK.

Short-term:
- add input port changing support
- add other input types (mouse-based, etc)
- finish cheat codes

Long-term:
- add slotted cart loader (SGB, BSX, ST)
- add DIP switch selection window (NSS)
- add overscan masking
- add timing configuration (video/audio sync)

Not planned:
- video color adjustments (will allow emulated color vs raw color; but
  no more sliders)
- pixel shaders
- ananke integration (will need to make a command-line version to get my
  games in)
- fancy audio adjustment controls (resampler, latency, volume)
- input focus settings
- localization support (not enough users)
- window geometry memory
- anything else not in higan v094
2015-04-21 21:54:07 +10:00
Tim Allen
89d578bc7f Update to v094r14 release.
byuu says:

Man, over five weeks have passed without so much as touching the
codebase ... time is advancing so fast it's positively frightening. Oh
well, little by little, and we'll get there eventually.

Changelog:
- added save state slots (1-5 in the menu)
- added hotkeys settings dialog + mapping system
- added fullscreen toggle (with a cute aspect correction trick)

About three hours of work here.

Short-term:
- add input port changing support
- add other input types (mouse-based, etc)
- add cheat codes
- add timing configuration (video/audio sync)

Long-term:
- add slotted cart loader (SGB, BSX, ST)
- add DIP switch selection window (NSS)
- add cheat code database
- add state manager
- add overscan masking

Not planned:
- video color adjustments (will allow emulated color vs raw color; but
  no more sliders)
- pixel shaders
- ananke integration (will need to make a command-line version to get my
  games in)
- fancy audio adjustment controls (resampler, latency, volume)
- input focus settings
- relocating game library (not hard, just don't feel like it)
- localization support (not enough users)
- window geometry memory
- anything else not in higan v094
2015-04-13 21:16:33 +10:00
Tim Allen
b4ba95242f Update to v094r13 release.
byuu says:

This version polishes up the input dialogue (reset, erase, disable
button when item not focused, split device ID from mapping name), adds
color emulation toggle, and add dummy menu items for remaining features
(to be filled in later.)

Also, it now compiles cleanly on Windows with GTK.

I didn't test with TDM-GCC-32, because for god knows what reason, the
32-bit version ships with headers from Windows 95 OSR2 only. So I built
with TDM-GCC-64 with arch=x86.

And uh, apparently, moving or resizing a window causes a Visual C++
runtime exception in the GTK+ DLLs. This doesn't happen with trance or
renshuu built with TDM-GCC-32. So, yeah, like I said, don't use -m32.
2015-03-07 21:21:47 +11:00
Tim Allen
a1b2fb0124 Update to v094r12 release.
byuu says:

Changelog:
* added driver selection
* added video scale + aspect correction settings
* added A/V sync + audio mute settings
* added configuration file
* fixed compilation bugs under Windows and Linux
* fixed window sizing
* removed HSU1
* the system menu stays as "System", because "Game Boy Advance" was too
  long a string for the smallest scale size
* some more stuff

You guys probably won't be ecstatic about the video sizing options, but
it's basically your choice of 1x, 2x or 4x scale with optional aspect
correction. 3x was intentionally skipped because it looks horrible on
hires SNES games. The window is resized and recentered upon loading
games. The window doesn't resize otherwise. I never really liked the way
v094 always left you with black screen areas and left you with
off-centered window positions.

I might go ahead and add the pseudo-fullscreen toggle that will jump
into 4x mode (respecting your aspect setting.)

Short-term:
* add input port changing support
* add other input types (mouse-based, etc)
* add save states
* add cheat codes
* add timing configuration (video/audio sync)
* add hotkeys (single state)

We can probably do a new release once the short-term items are
completed.

Long-term:
* add slotted cart loader (SGB, BSX, ST)
* add DIP switch selection window (NSS)
* add cheat code database
* add state manager
* add overscan masking

Not planned:
* video color adjustments (will allow emulated color vs raw color; but
  no more sliders)
* pixel shaders
* ananke integration (will need to make a command-line version to get my
  games in)
* fancy audio adjustment controls (resampler, latency, volume)
* input focus settings
* relocating game library (not hard, just don't feel like it)
* localization support (not enough users)
* window geometry memory
* anything else not in higan v094
2015-03-03 21:26:44 +11:00
Tim Allen
4a069761f9 Update to v094r11 release.
byuu says:

I've hooked up the input subsystem, and the input manager to assign
hotkeys.

So far I only have digital buttons working (keyboard only), and I'm not
planning on supporting input groups again (mapping multiple physical
buttons to one emulated button), but it's progress. As with the rest of
tomoko, the code's a lot more compact. The nice thing about redoing code
so many times is that each time you get a little bit better at it.

The input configuration is saved to ~/.config/tomoko/settings.bml (just
realized that I'm an idiot and need to rename it to input.bml)

Also hooked up game saves and cartridge unloading. Active controller
changing isn't hooked up yet, and I'll probably do it differently.

Oh, and I declared the ruby lines for other platforms.

Still need to add Cydrak's Windows compilation fixes. I am nothing if
not lazy :P
2015-03-03 21:26:44 +11:00
Tim Allen
80c1c9c2ef Update to v094r10 release.
byuu says:

This starts the tomoko UI. So far I have basic library loading and
video+audio output. Basically just enough to take the below screenshot.
(aside from Library, the menus are empty stubs.)

The .sys (system) game folders are now going under ~/Emulation/System,
to avoid needing root privileges to stick them into /usr/share. The game
library now shows all bootable media types, and the drop-down subtype is
gone. I'm going to display a separate modal dialog for loading slotted
games this time around. Much cleaner this way, less clutter.

tomoko's starting off a lot cleaner than ethos was, and I'm scaling back
the number of abstracted classes. What was Utility, Interface, etc are
now being merged all into Program. Of course, the real hell is the input
system. That has so many layers of bullshit that there's really no sane
way to write it.
2015-03-03 21:26:44 +11:00
Tim Allen
a512d14628 Update to v094r09 release.
byuu says:

This will easily be the biggest diff in the history of higan. And not in
a good way.

* target-higan and target-loki have been blown away completely
* nall and ruby massively updated
* phoenix replaced with hiro (pretty near a total rewrite)
* target-higan restarted using hiro (just a window for now)
* all emulation cores updated to compile again
* installation changed to not require root privileges (installs locally)

For the foreseeable future (maybe even permanently?), the new higan UI
will only build under Linux/BSD with GTK+ 2.20+. Probably the most
likely route for Windows/OS X will be to try and figure out how to build
hiro/GTK on those platforms, as awful as that would be. The other
alternative would be to produce new UIs for those platforms ... which
would actually be a good opportunity to make something much more user
friendly.

Being that I just started on this a few hours ago, that means that for
at least a few weeks, don't expect to be able to actually play any
games. Right now, you can pretty much just compile the binary and that's
it. It's quite possible that some nall changes didn't produce
compilation errors, but will produce runtime errors. So until the UI can
actually load games, we won't know if anything is broken. But we should
mostly be okay. It was mostly just trim<1> -> trim changes, moving to
Hash::SHA256 (much cleaner), and patching some reckless memory copy
functions enough to compile.

Progress isn't going to be like it was before: I'm now dividing my time
much thinner between studying and other hobbies.

My aim this time is not to produce a binary for everyone to play games
on. Rather, it's to keep the emulator alive. I want to be able to apply
critical patches again. And I would also like the base of the emulator
to live on, for use in other emulator frontends that utilize higan.
2015-02-28 12:52:53 +11:00
Tim Allen
1a7bc6bb87 Update to v094r08 release.
byuu says:

Lots of changes this time around. FreeBSD stability and compilation is
still a work in progress.

FreeBSD 10 + Clang 3.3 = 108fps
FreeBSD 10 + GCC 4.7 = 130fps

Errata 1: I've been fighting that god-damned endian.h header for the
past nine WIPs now. The above WIP isn't building now because FreeBSD
isn't including headers before using certain types, and you end up with
a trillion error messages. So just delete all the endian.h includes from
nall/intrinsics.hpp to build.

Errata 2: I was trying to match g++ and g++47, so I used $(findstring
g++,$(compiler)), which ends up also matching clang++. Oops. Easy fix,
put Clang first and then else if g++ next. Not ideal, but oh well. All
it's doing for now is declaring -fwrapv twice, so you don't have to fix
it just yet. Probably just going to alias g++="g++47" and do exact
matching instead.

Errata 3: both OpenGL::term and VideoGLX::term are causing a core dump
on BSD. No idea why. The resources are initialized and valid, but
releasing them crashes the application.

Changelog:
- nall/Makefile is more flexible with overriding $(compiler), so you can
  build with GCC or Clang on BSD (defaults to GCC now)
- PLATFORM_X was renamed to PLATFORM_XORG, and it's also declared with
  PLATFORM_LINUX or PLATFORM_BSD
  - PLATFORM_XORG probably isn't the best name ... still thinking about
    what best to call LINUX|BSD|SOLARIS or ^(WINDOWS|MACOSX)
- fixed a few legitimate Clang warning messages in nall
- Compiler::VisualCPP is ugly as hell, renamed to Compiler::CL
- nall/platform includes nall/intrinsics first. Trying to move away from
  testing for _WIN32, etc directly in all files. Work in progress.
- nall turns off Clang warnings that I won't "fix", because they aren't
  broken. It's much less noisy to compile with warnings on now.
- phoenix gains the ability to set background and foreground colors on
  various text container widgets (GTK only for now.)
- rewrote a lot of the MSU1 code to try and simplify it. Really hope
  I didn't break anything ... I don't have any MSU1 test ROMs handy
- SNES coprocessor audio is now mixed as sclamp<16>(system_sample
  + coprocessor_sample) instead of sclamp<16>((sys + cop) / 2)
  - allows for greater chance of aliasing (still low, SNES audio is
    quiet), but doesn't cut base system volume in half anymore
- fixed Super Scope and Justifier cursor colors
- use input.xlib instead of input.x ... allows Xlib input driver to be
  visible on Linux and BSD once again
- make install and make uninstall must be run as root again; no longer
  using install but cp instead for BSD compatibility
- killed $(DESTDIR) ... use make prefix=$DESTDIR$prefix instead
- you can now set text/background colors for the loki console via (eg):
 - settings.terminal.background-color 0x000000
 - settings.terminal.foreground-color 0xffffff
2014-02-24 20:39:09 +11:00
Tim Allen
ecc651c88b Update to v094r07 release.
byuu says:

Changelog for loki:
- added command aliases (match with * [sorry, regex lib isn't available
  everywhere yet], replace with {1}+)
- added command hotkeys
- added window geometry saving
- added save state support
- added power/reset commands
- added an input manager, so you can remap keys (limiting it to the
  keyboard for now though)

The combination of aliases and hotkeys really makes things shine. Save
states will temporarily disable your breakpoints (run/step are
technically temporary breakpoints) so as to ensure the state is captured
at a good time. In practice, this should pose about as much of a problem
as higan desyncing and breaking when capturing states ... should be
exceedingly rare to ever even notice this behavior at all, with 99.9% of
state captures happening in half an instruction boundary. But still,
keep it in mind, as you might see the CPU step one instruction ahead.
Tracing and usage map functionality is still enabled during state
synchronization.

So at this point, I have 100% of the essential stuff in. All that's left
now is to add polish / wishlist features like bass and mosaic
integration.
2014-02-09 17:05:58 +11:00
Tim Allen
3016e595f0 Update to v094r06 release.
byuu says:

New terminal is in. Much nicer to use now. Command history makes a major
difference in usability.

The SMP is now fully traceable and debuggable. Basically they act as
separate entities, you can trace both at the same time, but for the most
part running and stepping is performed on the chip you select.

I'm going to put off CPU+SMP interleave support for a while. I don't
actually think it'll be too hard. Will get trickier if/when we support
coprocessor debugging.

Remaining tasks:
- aliases
- hotkeys
- save states
- window geometry

Basically, the debugger's done. Just have to add the UI fluff.

I also removed tracing/memory export from higan. It was always meant to
be temporary until the debugger was remade.
2014-02-09 17:05:58 +11:00
Tim Allen
423a6c6bf8 Update to v094r05 release.
byuu says:

Commands can be prefixed with: (cpu|smp|ppu|dsp|apu|vram|oam|cgram)/ to
set their source. Eg "vram/hex 0800" or "smp/breakpoints.append execute
ffc0"; default is cpu.

These overlap a little bit in odd ways, but that's just the way the SNES
works: it's not a very orthogonal system. CPU is both a processor and
the main bus (ROM, RAM, WRAM, etc), APU is the shared memory by the
SMP+DSP (eg use it to catch writes from either chip); PPU probably won't
ever be used since it's broken down into three separate buses (VRAM,
OAM, CGRAM), but DSP could be useful for tracking bugs like we found in
Koushien 2 with the DSP echo buffer corrupting SMP opcodes. Technically
the PPU memory pools are only ever tripped by the CPU poking at them, as
the PPU doesn't ever write.

I now have run.for, run.to, step.for, step.to. The difference is that
run only prints the next instruction after running, whereas step prints
all of the instructions along the way as well. run.to acts the same as
"step over" here. Although it's not quite as nice, since you have to
specify the address of the next instruction.

Logging the Field/Vcounter/Hcounter on instruction listings now, good
for timing information.

Added in the tracer mask, as well as memory export, as well as
VRAM/OAM/CGRAM/SMP read/write/execute breakpoints, as well as an APU
usage map (it tracks DSP reads/writes separately, although I don't
currently have debugger callbacks on DSP accesses just yet.)

Have not hooked up actual SMP debugging just yet, but I plan to soon.
Still thinking about how I want to allow / block interleaving of
instructions (terminal output and tracing.)

So ... remaining tasks at this point:
- full SMP debugging
- CPU+SMP interleave support
- aliases
- hotkeys
- save states (will be kind of tricky ... will have to suppress
  breakpoints during synchronization, or abort a save in a break event.)
- keep track of window geometry between runs
2014-02-09 17:05:58 +11:00
Tim Allen
10e2a6d497 Update to v094r04 release.
byuu says:

Changelog:
- target-ethos/ is now target-higan/ (will unfortunately screw up diffs
  pretty badly at this point.)
- had a serious bug in nall::optional<T>::operator=, which is now fixed.
- added tracer (no masking just yet, I need to write a nall::bitvector
  class because I don't want to hard-code those anymore.)
- added usage logging (keep track of RWX/EP states for all bus
  addresses.)
- added read/write to poke at memory (hex also works for reading, but
  this one can poke at MMIO regs and is for one address only.)
- added both run.for (# of instructions) and run.to (program counter
  address.)
- added read/write/execute breakpoints with counters for a given
  address, and with an optional compare byte (for read/write modes.)

About the only major things left now for loki is support for trace
masking, memory export, and VRAM/OAM/CGRAM access.
For phoenix/Console, I really need to add a history to up+down arrows,
and I should support left/right insert-at.
2014-02-09 17:05:58 +11:00
Tim Allen
187ba0eec6 Update to v094r02 release.
byuu says:

Changelog:
- ethos: use nall::programpath() instead of realpath(argv[0]) to get
  executable path
- loki: add presentation window
- loki: add terminal window
- loki: add interface to emulation core
- loki: add ruby
- loki: add enough support to run games and save data on exit
    - load game folders via command-line (or drop folder onto binary),
      use "r" to start, "p" to pause ... temporary command names

I'll probably have to say this several times, but for now, loki is only
available on Linux/GTK+, due to the use of the Console widget. Support
for other platforms can come later easily enough.
2014-02-09 17:05:58 +11:00
Tim Allen
c54be74832 Ignore loki binary too. 2014-02-09 17:05:57 +11:00
Tim Allen
04986d2bf7 Update to v094r01 release.
byuu says:

Changelog:
- port: various compilation fixes for OS X [kode54]
- nall: added programpath() function to return path to process binary
  [todo: need to have ethos use this function]
- ruby: XAudio2 will select default game sound device instead of first
  sound device
- ruby: DirectInput device IDs are no longer ambiguous when VID+PID are
  identical
- ruby: OpenGL won't try and terminate if it hasn't been initialized
- gb: D-pad up+down/left+right not masked in SGB mode
- sfc: rewrote ICD2 video rendering to output in real-time, work with
  cycle-based Game Boy renderer
- sfc: rewrote Bus::reduce(), reduces game loading time by about 500ms
- ethos: store save states in {game}/higan/* instead of {game}/bsnes/*
- loki: added target-loki/ (blank stub for now)
- Makefile: purge out/* on make clean
2014-01-28 21:04:58 +11:00
Tim Allen
10464b8c54 Update to v094 release.
byuu says:

This release adds support for game libraries, and substantially improves
Game Boy and Game Boy Color emulation with cycle-based renderers. Many
other changes are also present.

It's very important to note that this release now defaults to optimal
drivers rather than safe drivers. This is particularly important if you
do not have strong OpenGL 3.2 drivers. If performance is bad, go to
Settings -> Configuration -> Advanced, change the video driver, and
restart higan. In the rare case that you have trouble opening higan, you
can edit settings.bml directly and change the setting there. The Windows
safe driver is Direct3D, and the Linux safe driver is XShm.

Also note that although display emulation shaders are now supported,
they have not been included in this release as they are not ready yet.
The support has been built-in anyway, so that they can be tested by
everyone. Once refined, future releases of higan will come with built-in
shaders for each emulated system that simulates the unique display
characteristics of each.

Changelog (since v093):
- sfc: added SA-1 MDR support (fixes SD Gundam G-Next bug)
- sfc: remove random/ and config/, merge to system/ with better
  randomization
- gb: improved color emulation palette contrast
- gbc: do not sort sprites by X-priority
- gbc: allow transparency on BG priority pixels
- gbc: VRAM DMA timing and register fixes
- gbc: block invalid VRAM DMA transfer source and target addresses
- gba: added LCD color emulation (without it, colors are grossly
  over-saturated)
- gba: removed internal frame blending (use shaders to simulate motion
  blur if desired)
- gba: added Game Boy Player support (adds joypad rumble support to
  supported games)
- gba: SOUND_CTL_H is readable
- gb/gbc: PPU renderer is now cycle-based (major accuracy improvement)
- gb/gbc: OAM DMA runs in parallel with the CPU
- gb/gbc: only HRAM can be accessed during OAM DMA
- gb/gbc: fixed serialization of games with SRAM
- gb/gbc: disallow up+down or left+right at the same time
- gb/gbc: added weak hipass filter to remove DC bias
- gb/gbc: STAT OAM+Hblank IRQs only trigger during active display
- gb/gbc: fixed underflow in window clamping
- gb/gbc/gba: audio mixes internally at 2MHz now instead of 4MHz (does
  not affect accuracy)
- gb/gbc/gba: audio volume reduced for consistency with other systems
- fc/sfc/gb/gbc/gba: cheat codes are now stored in universal, decrypted
  format
- ethos: replaced file loader with a proper game library
- ethos: added display emulation shader support
- ethos: added color emulation option to video settings
- ethos: program icon upgraded from 48x48 to 512x512
- ethos: settings and tools windows now use tab frames (less wasted
  screen space)
- ethos: default to optimal (video, audio, input) drivers instead of
  safest drivers
- ethos: input mapping system completely rewritten to support
  hotplugging and unique device mappings
- ruby: added fixes for OpenGL 3.2 on AMD graphics cards
- ruby: quark shaders now support user settings inside of manifest
- ruby: quark shaders can use integral textures (allows display
  emulation shaders to work with raw colors)
- ruby: add joypad rumble support
- ruby: XInput (Xbox 360) controllers now support hotplugging
- ruby: added Linux udev joypad driver with hotplug support
- phoenix: fixed a rare null pointer dereference issue on Windows
- port: target -std=c++11 instead of -std=gnu++11 (do not rely on GNU
  C++ extensions)
- port: added out-of-the-box compilation support for BSD/Clang 3.3+
- port: applied a few Debian upstream patches
- cheats: updated to mightymo's 2014-01-02 release; decrypted all Game
  Genie codes
2014-01-20 19:55:17 +11:00
Tim Allen
fe85679321 Update to v093r13 release.
byuu says:

This WIP removes nall/input.hpp entirely, and implements the new
universal cheat format for FC/SFC/GB/GBC/SGB.

GBA is going to be tricky since there's some consternation around
byte/word/dword overrides.

It's also not immediately obvious to me how to implement the code search
in logarithmic time, due to the optional compare value.

Lastly, the cheat values inside cheats.bml seem to be broken for the
SFC. Likely there's a bug somewhere in the conversion process. Obviously
I'll have to fix that before v094.

I received no feedback on the universal cheat format. If nobody adds
anything before v094, then I don't want to hear any complaining about
the formatting :P
2014-01-13 20:35:46 +11:00
Tim Allen
2b81b630cb Update to v093r12a release.
byuu says:

Not an official WIP (a WIP WIP? A meta-WIP?), just throwing in the new
fullscreen code, and I noticed that OpenGL colors in 30-bit mode are all
fucked up now for some strange reason. So I'm just using this snapshot
to debug the issue.
2014-01-05 20:59:17 +11:00
Tim Allen
3ce1d19f7a Update to v093r12 release.
byuu says:

I've completely redone the ethos InputManager and ruby to work on
HID::Device objects instead of one giant scancode pool.

Currently only the udev driver supports the changes to ruby, so only
Linux users will be able to compile and run this WIP build.

The nice thing about the new system is that it's now possible to
uniquely identify controllers, so if you swap out gamepads, you won't
end up with it working but with all the mappings all screwed up. Since
higan lets you map multiple physical inputs to one emulated input, you
can now configure your keyboard and multiple gamepads to the same
emulated input, and then just use whatever controller you want.

Because USB gamepad makers failed to provide unique serial#s with each
controller, we have to limit the mapping to specific USB ports.
Otherwise, we couldn't distinguish two otherwise identical gamepads. So
basically your computer USB ports act like real game console input port
numbers. Which is kind of neat, I guess.

And the really nice thing about the new system is that we now have the
capability to support hotplugging input devices. I haven't yet added
this to any drivers, but I'm definitely going to add it to udev for v094
official.

Finally, with the device ID (vendor ID + product ID) exposed, we gain
one last really cool feature that we may be able to develop more in the
future. Say we created a joypad.bml file to include with higan. In it,
we'd store the Xbox 360 controller, and pre-defined button mappings for
each emulated system. So if higan detects you have an Xbox 360
controller, you can just plug it in and use it. Even better, we can
clearly specify the difference between triggers and analog axes, and
name each individual input. So you'd see "Xbox 360 Gamepad #1: Left
Trigger" instead of higan v093's "JP0::Axis2.Hi"

Note: for right now, ethos' input manager isn't filtering the device IDs
to look pretty. So you're going to see a 64-bit hex value for a device
ID right now instead of something like Joypad#N for now.
2013-12-23 22:43:51 +11:00
Tim Allen
73be2e729c Update to v093r11 release.
byuu says:

Changelog:
- GBA: SOUND_CTL_H is readable, fixes sound effects in Mario&Luigi
  Superstar Saga [Cydrak] (note: game is still unplayable due to other
  bugs)
- phoenix/Windows: workaround for Win32 API ListView bug, fixes slot
  loading behavior
- ruby: added udev driver for Linux with rumble support, and added
  rumble support to existing RawInput driver for XInput and DirectInput
- ethos: added new "Rumble" mapping to GBA input assignment, use it to
  tell higan which controller to rumble (clear it to disable rumble)
- GBA: Game Boy Player rumble is now fully emulated
- core: added new normalized raw-color palette mode for Display
  Emulation shaders

The way rumble was added to ethos was somewhat hackish. The support
doesn't really exist in nall.

I need to redesign the entire input system, but that's not a change
I want to make so close to a release.
2013-12-21 21:45:58 +11:00
Tim Allen
84fab07756 Update to v093r10 release.
byuu says:

Changelog:
- Game Boy (Color): STAT OAM+HBlank IRQs only trigger during LY=0-143
  with display enabled
  - fixes backgrounds and text in Wacky Races
- Game Boy (Color): fixed underflow in window clamping
  - fixes Wacky Races, Prehistorik Man, Alleyway, etc
- Game Boy (Color): LCD OAM DMA was running too slow
  - fixes Shin Megami Tensei - Devichil - Kuro no Sho
- Game Boy Advance: removed built-in frame blending; display emulation
  shaders will handle this going forward
- Game Boy Advance: added Game Boy Player emulation
  - currently the screen is tinted red during rumble, no actual gamepad
    rumble support yet
  - this is going to be slow, as we have to hash the frame to detect the
    GBP logo, it'll be optional later on
- Emulator::Interface::Palette can now output a raw palette (for Display
  Emulation shaders only)
  - color channels are not yet split up, it's just the raw packed value
2013-12-20 22:40:39 +11:00
Tim Allen
926a39d701 Update to v093r09 release.
byuu says:

Changelog:
- GB/C OAM DMA now runs in parallel with the CPU
- CPU can only access HRAM during OAM DMA
- fixed SGB mode again
- brand new config files will default to the optimal drivers now
  (OpenGL, etc) instead of the safest
- hopefully fixed remaining Windows UI issues
2013-12-14 17:25:12 +11:00
Tim Allen
1361820dd8 Update to v093r08 release.
byuu says:

Changelog:
- Game Boy and Game Boy Color now have a weak hipass filter to remove DC
  bias (or whatever)
- Game Boy and Game Boy Color now have cycle-based PPU renderers instead
  of scanline-based renderers
- improved Game Boy color emulation palette contrast
- fixed GTK+ ListView selection bug
- fixed a typo when saving states (should say "Saved to slot N", not
  "Save to slot N")
2013-12-11 22:19:17 +11:00
Tim Allen
0f78acffd7 Update to v093r07 release.
byuu says:

Changelog:
- importing a game won't show message box on success
- importing a game will select the game that was imported in the list
  - caveat: GTK+ port doesn't seem to be removing focus from item 0 even
    though the selection is on item 2
- Game Boy audio reduced in volume by 50%
- Game Boy Advance audio reduced in volume by 50%
- Game Boy internally mixes audio at 2MHz now
- Game Boy Advance's Game Boy audio hardware internally mixes audio at
  2MHz now
- Game Boy Color doesn't sort sprites by X-coordinate
- Game Boy Color allows transparency on BGpriority pixels
  - caveat: this seems to allow sprites to appear on top of windows
- Game Boy Color VRAM DMA transfers 16 bytes in 8 clocks (or 16 clocks
  in double speed mode)
- Game Boy Color VRAM DMA masks low 4-bits of source and destination
  address
- Game Boy Color VRAM DMA only allows reads from ROM or RAM
- Game Boy Color VRAM DMA only allows writes to VRAM
- fixed a bug in dereferencing a nullptr from pObject::find(), should
  fix crash when pressing enter key on blank windows
- fixed Windows RadioItem selection
- Game Boy Advance color emulation code added
2013-12-10 23:12:54 +11:00
Tim Allen
35f1605829 Update to v093r06 release.
byuu says:

Changelog:
- Windows port should compile out-of-the-box
- InputManager::scancode[] initialized at startup
- Library menu shows item for each bootable media type (notably Game Boy
  Color)
- Display Emulation menu selection fix
- LibraryManager load button works now
- Added hotkey to show library manager (defaults to L)
- Added color emulation to video settings (missing on GBA for now)
- SFC loading SGB without GB cartridge no longer segfaults
- GB/GBC system.load() after cartridge.load()
- GB/GBC BG-over-OAM fix
- GB/GBC disallow up+down and left+right
2013-12-07 20:12:37 +11:00
Tim Allen
ed4e87f65e Update to v093r05 release.
byuu says:

Library concept has been refined as per the general forum discussion.
2013-12-03 21:01:59 +11:00
Tim Allen
b4f18c3b47 Update to v093r04 release.
byuu says:

This version replaces the old folder-browser with a proper game library.
2013-11-28 21:32:53 +11:00
Tim Allen
68eaf53691 Update to v093r03 release.
byuu says:

Updated to support latest phoenix changes.
Converted Settings and Tools to TabFrame views.

Errata:
- phoenix/Windows ComboButton wasn't calling parent
  pWidget::setGeometry() [fixed locally]
- TRACKBAR_CLASS draws COLOR_3DFACE for the background even when its
  parent is a WC_TABCONTROL
2013-11-28 21:29:01 +11:00
Tim Allen
8c0b0fa4ad Update to v093r02 release.
byuu says:

Changelog:
- nall: fixed major memory leak in string class
- ruby: video shaders support #define-based settings now
- phoenix/GTK+: support > 256x256 icons for window / task bar / alt-tab
- sfc: remove random/ and config/, merge into system/
- ethos: delete higan.png (48x48), replace with higan512.png (512x512)
  as new higan.png
- ethos: default gamma to 100% (no color adjustment)
- ethos: use "Video Shaders/Display Emulation/" instead of "Video
  Shaders/Emulation/"
- use g++ instead of g++-4.7 (g++ -v must be >= 4.7)
- use -std=c++11 instead of -std=gnu++11
- applied a few patches from Debian upstream to make their packaging job
  easier

So because colors are normalized in GLSL, I won't be able to offer video
shaders absolute color literals. We will have to perform basic color
conversion inside the core.

As such, the current plan is to create some sort of Emulator::Settings
interface. With that, I'll connect an option for color correction, which
will be on by default. For FC/SFC, that will mean gamma correction
(darker / stronger colors), and for GB/GBC/GBA, it will mean simulating
the weird brightness levels of the displays. I am undecided on whether
to use pea soup green for the GB or not. By not doing so, it'll be
easier for the display emulation shader to do it.
2013-11-09 22:45:54 +11:00
Tim Allen
66f136718e Update to v093r01 release.
byuu says:

Changelog:
- added SA-1 MDR; fixes bug in SD Gundam G-Next where the main
  battleship was unable to fire
- added out-of-the-box support for any BSD running Clang 3.3+ (FreeBSD
  10+, notably)
- added new video shader, "Display Emulation", which changes the shader
  based on the emulated system
- fixed the home button to go to your default library path
- phoenix: Windows port won't send onActivate unless an item is selected
  (prevents crashing on pressing enter in file dialog)
- ruby: removed vec4 position from out Vertex {} (helps AMD cards)
- shaders: updated all shaders to use texture() instead of texture2D()
  (helps AMD cards)

The "Display Emulation" option works like this: when selected, it tries
to load "<path>/Video Shaders/Emulation/<systemName>.shader/"; otherwise
it falls back to the blur shader. <path> is the usual (next to binary,
then in <config>/higan, then in /usr/share/higan, etc); and <systemName>
is "Famicom", "Super Famicom", "Game Boy", "Game Boy Color", "Game Boy
Advance"

To support BSD, I had to modify the $(platform) variable to
differentiate between Linux and BSD.
As such, the new $(platform) values are:
win -> windows
osx -> macosx
x -> linux or bsd

I am also checking uname -s instead of uname -a now. No reason to
potentially match the hostname to the wrong OS type.
2013-10-21 22:45:39 +11:00
Tim Allen
4e2eb23835 Update to v093 release.
byuu says:

Changelog:
- added Cocoa target: higan can now be compiled for OS X Lion
  [Cydrak, byuu]
- SNES/accuracy profile hires color blending improvements - fixes
  Marvelous text [AWJ]
- fixed a slight bug in SNES/SA-1 VBR support caused by a typo
- added support for multi-pass shaders that can load external textures
  (requires OpenGL 3.2+)
- added game library path (used by ananke->Import Game) to
  Settings->Advanced
- system profiles, shaders and cheats database can be stored in "all
  users" shared folders now (eg /usr/share on Linux)
- all configuration files are in BML format now, instead of XML (much
  easier to read and edit this way)
- main window supports drag-and-drop of game folders (but not game files
  / ZIP archives)
- audio buffer clears when entering a modal loop on Windows (prevents
  audio repetition with DirectSound driver)
- a substantial amount of code clean-up (probably the biggest
  refactoring to date)

One highly desired target for this release was to default to the optimal
drivers instead of the safest drivers, but because AMD drivers don't
seem to like my OpenGL 3.2 driver, I've decided to postpone that. AMD
has too big a market share. Hopefully with v093 officially released, we
can get some public input on what AMD doesn't like.
2013-08-18 13:21:14 +10:00
Tim Allen
c74865e171 Update to v092r10 release.
byuu says:

Changelog:
- you can now drop game folders (not game files, sorry) onto higan's
  main window to load them
- audio buffer will clear on Windows when entering modal loop (entering
  menu, moving or resizing window)
  - this prevents DirectSound driver's audio repetition
- ruby defaults to the optimal driver for each platform, rather than the
  safest driver, now
- added Cydrak's gl_Position.zw change to ruby
- added fixes for all the changes to nall, ruby, phoenix over the past
  three months
2013-07-29 19:42:45 +10:00
Tim Allen
29ea5bd599 Update to v092r09 release.
byuu says:

This will be another massive diff from the previous version.

All of higan was updated to use the new foo& bar syntax, and I also
updated switch statements to be consistent as well (but not in the
disassemblers, was starting to get an RSI just from what I already did.)

phoenix/{windows, cocoa, qt} need to be updated to use "string foo"
instead of "const string& foo", and after that, the major diffs should
be finished.

This archive is the first time I'm posting my copy-on-write,
size+capacity nall::string class, so any feedback on that is welcome as
well.
2013-05-05 19:21:30 +10:00
Tim Allen
75dab443b4 Update to v092r08 release.
byuu says:

Changelog:
- fixed cartridge load window focus on Windows
- lots of updates to nall, ruby and phoenix
- ethos and Emulator::Interface updated from "foo &bar" to "foo& bar"
  syntax (work-in-progress)

Before I had mixed the two ways to declare variables/arguments all over
the place, so the goal is to unify them all for consistency. So the
changelog for this release will be massive (750KB >.>) due to the syntax
change. Yeah, that's what I spent the last three days working on ...
2013-05-02 21:25:45 +10:00
Tim Allen
177e222ca7 Update to v092r07 release.
byuu says:

- OpenGL should work on OS X now; it uses VAOs and VBOs, and is fully
  OpenGL 3.2 core compliant
- all configuration files are now stored in BML format, instead of CFG
  format (half the size, much more readable)
- some old nall libraries that were never used have been removed
- make install works with or without root now (copies core files to
  /usr/share/higan [non-configurable])
- make install also works on OS X (copies to /Library/Application
  Support/higan)
2013-04-14 18:52:47 +10:00
Tim Allen
0d75524791 Update to v092r06 release.
byuu says:

Changelog:
- added support for ruby shader folders (place in "Video Shaders/")
- higan now also looks in your shared folder for configuration files and
  system media folders
- added CFBundleExecutable key to OS X Info.plist

Shared folder locations:
- Windows XP: C:\Documents and Settings\All Users\Application Data\higan
- Windows 7: C:\ProgramData\higan
- OS X: /Library/Application Support/higan
- Linux: /etc/higan

Evaluation order:
- look for item in binary folder: if found, use this folder
- look for item in user folder: if found, use this folder
- look for item in shared folder: if found, use this folder
- create item in user folder

For people repackaging higan for other distros: you should chmod 777
/etc/higan. Failure to do so could result in higan breaking. No, I will
not copy the files from the shared path to the user path.
2013-04-09 23:31:46 +10:00
Tim Allen
5b4bbf5045 Update to v092r05 release.
byuu says:

This release should be polished enough for a general release.

This release should be polished enough for a general release.

Anyone with a real, clean Mac up for posting compiled binaries?
Preferably compile with "make profile=balanced" In fact, I'd like it if
someone were willing to host a "higan for Mac" page, with binaries of
each of the latest releases. Only really needed for major official
releases, but it'd be preferable to have the builds updated as soon as
possible after I post new builds.

Changelog:
- no more keyboard chimes when pressing keys
- status bar added, fully functional
- Label::minimumSize() takes frame into account (but note a few places
  hard-code raw Font::size(), so a few text labels are still clipped)
- resizing the main window looks smooth regardless of whether a game is
  running or not
  - currently, resizing the window pauses the emulation. Allowing it to
    run the main loop was lagging out the window resize process too much
    to be worth it

Additional OS X integration enhancements:
- closing the main window unloads the current game, but does not quit
  the application (quit via the main menu or the dock menu)
- clicking the icon in the dock will (re)display the main menu
2013-03-21 23:59:01 +11:00
Tim Allen
fdd3ea490e Update to v092r04 release.
byuu says:

This is the first release with full support for OS X, although it's
certainly still very buggy.

Known issues:
- window status bars are still unsupported (they just don't show up)
- you get the bad keypress chime when you use the keyboard
- window geometry and font metrics aren't perfect (bit of clipping here
  and there)
- list view headers that aren't auto-sized are sometimes too short (file
  browser)
- input assignment is really rough (assigning a key also moves around in
  the list or beeps at you)

Custom OS X integration support so far:
- 512x512 ICNS application icon: will look razor-sharp even on a retina
  display
- basic Info.plist added to application bundle
- program menu about, preferences, quit all connected
- Settings->Configuration removed (use higan->Preferences instead)
- global menubar

To compile and use this, you'll need:
- Xz Utils (to extract .tar.xz)
- Xcode 4.6
- Lion 10.7.4 or newer

    mkdir higan_v092r04
    tar -xJf higan_v092r04.tar.xz -C higan_v092r04
    cd higan_v092r04
    make -j 2

ananke is missing, and I haven't updated purify yet, so you'll have to
move game folders from Windows or Linux over, or make them by hand (a
not so enjoyable experience, to say the least.)
2013-03-19 19:48:50 +11:00
Tim Allen
b7c212de7e Update to v092r03 release.
byuu says:

This release adds the phoenix/Cocoa port, and rewrites a lot of the
higan user interface to work with all of the new changes (like blocking
in the main run loop and in modal windows.)

It doesn't yet modify the compilation flags to actually build on OS
X yet, and even then, we don't really have ruby drivers, so there'd be
no video, audio or input.

Two months between a single WIP point release ... for the first six
years, I never went more than a month without a full official release.
I guess I should be happy that it's become so refined, but I sure do
miss those halcyon days of exciting progress.
2013-03-16 00:11:33 +11:00
Tim Allen
d9400084c2 Update to v092r02 release.
byuu says:

Changelog:
- merged AWJ's hires color blending improvements (most notably: fixes
  Marvelous' text)
- created sfc/base/ to store base unit (expansion port device) emulation
- synchronized the markup of Satellaview and Sufami Turbo cartridge
  slots in the board markup
- fixed "Initializing ..." typo in timing settings

If at all possible, I'd really like to have heavy testing of games that
use hires graphics to check for any regressions.
I trust AWJ's code, and all of the test ROMs I have thrown at it all
appear to work great. But better safe than sorry. Same deal for any core
changes, it's a lot better to catch it now than after v093 is released.
2013-01-23 19:28:35 +11:00
Tim Allen
bbc33fe05f Update to higan v092r01, ananke v02r01 and purify v03r01 releases.
byuu says:

higan changelog:
- compiler is set to g++-4.7, subst(cc,++) rule is gone, C files compile
  with $(compiler) -x c
- make throws an error when you specify an invalid profile or compile on
  an unsupported platform (instead of hanging forever)
- added unverified.png to resources (causes too big of a speed hit to
  actually check for folder/unverified file ... so disabled for now)
- fixed default browser paths for Game Boy, Sufami Turbo and BS-X
  Satellaview (have to delete paths.cfg to see this)
- browser home button seeks to configpath()/higan/library.cfg
- settings->driver is now settings->advanced, and it adds game library
  path setting and profile information
- emulation cores now load manifest files internally, manifest.bml is
  not required for a game folder to be recognized by higan as such
- BS-X Satellaview and Sufami Turbo slot cartridge handling moved out of
  sfc/chip and into sfc/slot
- Video::StartFullScreen only sets fullscreen when a game is specified
  on the command-line

purify and ananke changelog:
- library output path shown in purify window
- added button to change library path
- squelch firmware warning windows to prevent multi-threading crash, but
  only via purify (they show up in higan still)
2013-01-21 23:27:15 +11:00
Tim Allen
65c4011bec Update to purify v03 release.
byuu says:

This release has an updated version of ananke. If you replace the higan
v092 ananke.dll with this new one, it will fix the SGB+TG3000+ToP+DKJM2
loading issues.
2013-01-21 19:57:04 +11:00
Tim Allen
a7c35a65b4 Update to ananke v01r01 release.
This version fixes a problem where ananke would leave out the
'information' section (that is, the game name) when converting a game to
a game folder, resulting in a folder named " (!)".

It also includes the latest version of nall.
2013-01-19 22:23:42 +11:00
Tim Allen
ba660600ad Update to purify v02r01 release.
Because byuu's Win32 compiler does not yet support the C++11 std::thread
API, he wrote his own portable wrapper library, so now the new purify
works on Windows too.
2013-01-19 22:20:25 +11:00
Tim Allen
b6575ca02a Update to purify v02 release.
byuu says:

purify has been rewritten. It now resembles the older snespurify, and
lets you import multiple game files+archives and regenerate manifests
for multiple game folders. It is also recursive.

So you can now import all of your games for all systems at once, or you
can update all of your bsnes v091 game folders to the new higan v092
format at once.

Caveats:

First, I am now using std::thread, so that the GUI doesn't freeze.
Instead, you get a nice progress bar. Unfortunately, I was mislead and
TDM/GCC 4.7 still does not have std::thread support. So ... sorry, but
I can't compile purify for Windows. I am sick and tired of not being
able to write multi-threaded code, so fuck it. If anyone can get it to
build on Windows, whether that be by using Windows threads, hacking in
std::thread support, skipping threading all together, whatever ...
that'll be great. Otherwise, sorry, purify is Linux only until MinGW can
get its god damned shit together and offers threading support.

Second, there's no way to regenerate Famicom (NES) manifests, because we
discard the iNES header. We are going to need a database for that. So,
all I can suggest is that if you use bsnes/higan, keep all your iNES
images around to re-import as new releases come out.

Third, when you purify game folders, it will back up the ROM and RAM
files only. Your save states, cheat codes, debug logs, etc will be wiped
out. There's a whole lot of reasons for this, the most pertinent is that
it's meant to clean up the folder to a pristine state. It also fixes the
game folder name, etc. So ... sorry, but this is how it works. New
releases rarely if ever allow old save states to work anyway.

Lastly, I am not going to have purify contain infinite backward
compatibility for updating manifests. You will want to keep up with
purifying the collection, otherwise you'll have to grab older purify
copies and convert your way along. Although hopefully the format won't
be so volatile and this won't be necessary very often.
2013-01-17 22:21:00 +11:00
Tim Allen
8d88337e28 Update to ananke v01 release.
byuu says:

This updated anake fixes all of the reported game issues thus far.
2013-01-17 22:20:53 +11:00
Tim Allen
6ac67c260b Update to v092 hotfix release.
byuu says:

For higan:
- I fixed the data ROM/RAM initialization for the Cx4, which would
  periodically cause a crash.
- I also moved the Satellaview MaskROM vs FlashROM detection into the
  Satellaview manifests, so Same Game - Character Data works now.
- I also re-added the driver filter to the video shaders, so the D3D
  driver won't show OGL shaders and vice versa.

For ananke:
- You can now generate the other SGB images by putting sgb.rom in the
  same folder as the BIOS images.
- I fixed the markup in the database and via heuristics for 5MB+ games
  (DKJM2, ToP)
- Sufami Turbo and BS-X Satellaview generate BML now instead of XML when
  using heuristics.
2013-01-15 21:51:49 +11:00
Tim Allen
032e924495 Update to v092 release.
In the release thread, byuu says:

    The first official release of higan has been posted. higan is the
    new name for bsnes, and it continues with the latter's version
    numbering.

    Note that as of now, bsnes still exists. It's a module distributed
    inside of higan. bsnes is now specific to my SNES emulator.

    Due to last minute changes to the emulator interface, and missing
    support in ananke, I wasn't able to include Cydrak's Nintendo DS
    emulator dasShiny in this build, but I hope to do so in the next
    release.

    http://code.google.com/p/higan/downloads/list

    For both new and experienced users, please read the higan user guide
    first:

    http://byuu.org/higan/user-guide

In the v091 WIP thread, byuu says:

    r15->r16:
    - BS-X MaskROM handling (partial ... need to split bsx/flash away
      from sfc/chip, restructure code - it requires tagging the base
      cart markup for now, but it needs to parse the slotted cart
      markup)
    - phoenixflags / phoenixlink += -m32
    - nall/sort stability
    - if(input.poll(scancode[activeScancode]) == false) return;
    - MSU1 / USART need to use interface->path(1)
    - MSU1 needs to use Markup::Document, not XML::Document
    - case-insensitive folder listings
    - remove nall/emulation/system.hpp files (move to ananke)
    - remove rom/ram id= checks with indexing
    X have cores ask for manifest.bml (skipped for v092's release, too
      big a change)
    - rename compatibility profile to balanced (so people don't assume
      it has better compatibility than accuracy)
2013-01-14 23:15:21 +11:00
Tim Allen
b389d17c9a Update to v091r15 release.
byuu says:

Changelog:
- all media types always show base name in the title now (eg Super Game
  Boy + Mega Man II)
- Game Boy loading via ananke has been fixed
- phoenix is dynamically linked on Windows now (needed for ananke)
- Linux port shows the higan program icon (once you install the program
  to get the bitmap into /usr/local/share/pixmaps)
- paths.cfg defaults to "userpath()/Emulation/System Name/" when it is
  created from scratch

[Later, after the v092 release, byuu posted this additional changelog:
    - new compilation rules for win32
    - OS::setName
    - default to ~/Emulation/media.name for paths.cfg
]
2013-01-14 23:14:44 +11:00
Tim Allen
d59ae34e12 Update to higan v091r14 and ananke v00r03 releases.
byuu says:

higan changelog:
- generates title displayed in emulator window by asking the core
- core builds title solely from "information/title" ... if it's not
  there, you don't get a title at all
- sub-system load menu is gone ... since there are multiple revisions of
  the SGB, this never really worked well anyway
- to load an SGB, BS-X or ST cartridge, load the base cartridge first
- "File->Load Game" moved to "Load->Import Game" ... may cause a bit of
  confusion to new users, but I don't like having a single-item menu,
  we'll just have to explain it to new users
- browser window redone to look like ananke
  - home button here goes to ~/Emulation rather than just ~ like ananke,
    since this is the home of game folders
  - game folder icon is now the executable icon for the Tango theme
    (orange diamond), meant to represent a complete game rather than
    a game file or archive

ananke changelog:
- outputs GBC games to "Game Boy Color/" instead of "Game Boy/"
- adds the file basename to "information/title"

Known issues:
- using ananke to load a GB game trips the Super Famicom SGB mode and
  fails (need to make the full-path auto-detection ignore non-bootable
  systems)
- need to dump and test some BS-X media before releasing
- ananke lacks BS-X Satellaview cartridge support
- v092 isn't going to let you retarget the ananke/higan game folder path
  of ~/Emulation, you will have to wait for a future version if that
  bothers you so greatly

[Later, after the v092 release, byuu posted this additional changelog:
    - kill laevateinn
    - add title()
    - add bootable, remove load
    - combine file, library
    - combine [][][] paths
    - fix SFC subtype handling XML->BML
    - update file browser to use buttons
    - update file browser keyboard handling
    - update system XML->BML
    - fix sufami turbo hashing
    - remove Cartridge::manifest
]
2013-01-14 23:13:48 +11:00
Tim Allen
85f2e9a6d4 Update to ananke v00r02 release.
byuu says:

This should be basically final now.

Works with all media types (nes, sfc, gb, gbc, gba, bs, st), strips
headers, can use internal or external firmware, imports saves on first
run.

Added a custom file dialog. It seems both GTK+ and Windows XP have
(un)intelligent file sorting, which puts eg "ActRaiser 2 (NA)" before
"ActRaiser (NA)". So, screw 'em.
2012-12-26 17:46:57 +11:00
Tim Allen
019fc1a2c6 Update to v091r13 release, and ananke v00r01.
byuu says (about higan):

- dropped release/ root node for individual games (still there in
  ananke's database.)
- Memory export uses smarter names (vram.rwm -> video.ram, etc.)
- cheat database moved from XML to BML (3.1MB to 1.9MB file size.)
- cheat codes moved from XML to BML
- resource manifest moved from XML to BML

What can I say, I like consistency. But I'll leave the shaders alone
until I get around to shader folders.

byuu says (about ananke):

Works with higan v091r13. Only does SNES stuff so far.
2012-12-26 17:46:57 +11:00
Tim Allen
84e98833ca Update to v091r11 release.
byuu says:

This release refines HSU1 support as a bidirectional protocol, nests SFC
manifests as "release/cartridge" and "release/information" (but release/
is not guaranteed to be finalized just yet), removes the database
integration, and adds support for ananke.

ananke represents inevitability. It's a library that, when installed,
higan can use to load files from the command-line, and also from a new
File -> Load Game menu option.

I need to change the build rules a bit for it to work on Windows (need
to make phoenix a DLL, basically), but it works now on Linux.

Right now, it only takes *.sfc file names, looks them up in the included
database, converts them to game folders, and returns the game folder
path for higan to load.

The idea is to continue expanding it to support everything we can that
I don't want in the higan core:
- load *.sfc, *.smc, *.swc, *.fig files
- remove SNES copier headers
- split apart merged firmware files
- pull in external firmware files (eg dsp1b.rom - these are staying
  merged, just as SPC7110 prg+dat are merged)
- load *.zip and *.7z archives
- prompt for selection on multi-file archives
- generate manifest files based on heuristics
- apply BPS patches

The "Load" menu option has been renamed to "Library", to represent games
in your library. I'm going to add some sort of suffix to indicate
unverified games, and use a different folder icon for those (eg
manifests built on heuristics rather than from the database.)

So basically, to future end users:
File -> Load Game will be how they play games.
Library -> (specific system) can be thought of as an infinitely-sized
    recent games list.

purify will likely become a simple stub that invokes ananke's functions.
No reason to duplicate all that code.
2012-12-26 17:46:57 +11:00
Tim Allen
d4751c5244 Update to v091r10 release.
byuu says:

This release adds HSU1 support, and fixes the reduce() memory mapping
function.
2012-12-26 17:46:57 +11:00
Tim Allen
ab345ff20c Update to v091r09 release.
[r07 and r08 were not posted to the WIP thread. -Ed.]

byuu says:

I'd appreciate it if you guys wouldn't mind testing out the database
functionality.

Save this file as database.bml (remove the date) inside
~/.config/higan/Super Famicom.sfc/ or %APPDATA%/higan/Super Famicom.sfc/

    http://byuu.org/snes/database/database_2012-10-21.bml

Now load any of the 20 games in the database from the file dialog. They
need to be named *.sfc, have no copier header, and have firmware
appended (for Mario Kart only so far.)

If anyone actually does test it, please let me know how it goes for you
and what you think. Note that future versions of higan will have the
database.bml file included with the release.
2012-12-26 17:46:57 +11:00
Tim Allen
c495c132a7 Update to v091r06 release.
byuu says:

This release adds initial database support.

The way it works is you can now load game folders as you always have, or
you can load a game file. If you load a game file, it tries to create
a game folder for you by looking up the file's sha256 in a database. If
it can't find it, sorry, the game won't play. I'm not hooking up the
oldschool "make up a manifest" code here. The easiest way to handle this
is to get me every game so I can dump it and add it to the database :D

The database entries are complete entries that can be copied directly.
So it describes the board, the information, file layout, etc. That'll be
what comes with higan releases in the future.

Internally, I'm separating the information and board descriptions, and
will use a tool to merge the two together.

Here's a current database copy, with one game in it. Still hammering out
some details, but it's mostly how it's going to look.

    cartridge region=NTSC
	board type=1CB5B-20
	    superfx revision=2
		rom name=program.rom size=0x200000
		ram name=save.rwm size=0x8000
		map id=io address=00-3f,80-bf:3000-32ff
		map id=rom address=00-3f:8000-ffff mask=0x8000
		map id=rom address=40-5f:0000-ffff
		map id=ram address=00-3f,80-bf:6000-7fff size=0x2000
		map id=ram address=70-71:0000-ffff
	information
	    name:   Super Mario World 2 - Yoshi's Island (SNS) (1.1)
	    title:  Super Mario World 2: Yoshi's Island
	    sha256: bd763c1a56365c244be92e6cffefd318780a2a19eda7d5baf1c6d5bd6c1b3e06
	    board:  SHVC-1CB5B-20
	    rom:    0x200000
	    ram:    0x8000
	layout
	    file name=program.rom size=0x200000
2012-12-26 17:46:57 +11:00
Tim Allen
ef746bbda4 Update to v091r05 release.
[No prior releases were posted to the WIP thread. -Ed.]

byuu says:

Super Famicom mapping system has been reworked as discussed with the
mask= changes. offset becomes base, mode is gone. Also added support for
comma-separated fields in the address fields, to reduce the number of
map lines needed.

    <?xml version="1.0" encoding="UTF-8"?>
    <cartridge region="NTSC">
      <superfx revision="2">
	<rom name="program.rom" size="0x200000"/>
	<ram name="save.rwm" size="0x8000"/>
	<map id="io" address="00-3f,80-bf:3000-32ff"/>
	<map id="rom" address="00-3f:8000-ffff" mask="0x8000"/>
	<map id="rom" address="40-5f:0000-ffff"/>
	<map id="ram" address="00-3f,80-bf:6000-7fff" size="0x2000"/>
	<map id="ram" address="70-71:0000-ffff"/>
      </superfx>
    </cartridge>

Or in BML:

    cartridge region=NTSC
      superfx revision=2
	rom name=program.rom size=0x200000
	ram name=save.rwm size=0x8000
	map id=io address=00-3f,80-bf:3000-32ff
	map id=rom address=00-3f:8000-ffff mask=0x8000
	map id=rom address=40-5f:0000-ffff
	map id=ram address=00-3f,80-bf:6000-7fff size=0x2000
	map id=ram address=70-71:0000-ffff

As a result of the changes, old mappings will no longer work. The above
XML example will run Super Mario World 2: Yoshi's Island. Otherwise,
you'll have to write your own.

All that's left now is to work some sort of database mapping system in,
so I can start dumping carts en masse.

The NES changes that FitzRoy asked for are mostly in as well.

Also, part of the reason I haven't released a WIP ... but fuck it, I'm
not going to wait forever to post a new WIP.

I've added a skeleton driver to emulate Campus Challenge '92 and
Powerfest '94. There's no actual emulation, except for the stuff I can
glean from looking at the pictures of the board. It has a DSP-1 (so
SR/DR registers), four ROMs that map in and out, RAM, etc.

I've also added preliminary mapping to upload high scores to a website,
but obviously I need the ROMs first.
2012-12-26 17:46:57 +11:00
Tim Allen
94b2538af5 Update to higan v091 release.
byuu says:

Basically just a project rename, with s/bsnes/higan and the new icon
from lowkee added in.

It won't compile on Windows because I forgot to update the resource.rc
file, and a path transform command isn't working on Windows.
It was really just meant as a starting point, so that v091 WIPs can flow
starting from .00 with the new name (it overshadows bsnes v091, so
publicly speaking this "shouldn't exist" and will probably be deleted
from Google Code when v092 is ready.)
2012-12-26 17:46:36 +11:00
Tim Allen
7f404e6edb Update to v091 release.
byuu says:

A few issues crept up in the last release, this should take care of
them.

First, it seems that the 32-bit runtime on 64-bit versions of Windows
have 64-bit time functions; whereas true 32-bit Windows does not. This
was causing a DLL error when attempting to load bsnes v090.

Second, when there were more than 2,000 files in the same folder on
Windows, it was lagging the file browser. With OV2's help, I've fixed
that and it'll now load the list instantly.

Lastly, I've included the missing video shaders this time.
2012-08-11 12:18:19 +10:00
Tim Allen
47dffcae85 Update to v090 release.
byuu says:

Most notably, this release adds Nintendo DS emulation. The Nintendo DS
module was written entirely by Cydrak, so please give him all of the
credit for it. I for one am extremely grateful to be allowed to use his
module in bsnes.

The Nintendo DS emulator's standalone name is dasShiny. You will need
the Nintendo DS firmware, which I cannot provide, in order to use it. It
also cannot (currently?) detect the save type used by NDS games. As
such, manifest.xml files must be created manually for this purpose. The
long-term plan is to create a database of save types for each game.
Also, you will need an analog input device for the touch screen for now
(joypad axes work well.)

There have also been a lot of changes from my end: a unified
manifest.xml format across all systems, major improvements to SPC7110
emulation, enhancements to RTC emulation, MSU1 enhancements, icons in
the file browser list, improvements to SNES coprocessor memory mapping,
cleanups and improvements in the libraries used to build bsnes, etc.

I've also included kaijuu (which allows launching game folders directly
with bsnes) and purify (which allows opening images that are compressed,
have copier headers, and have wrong extensions); both of which are fully
GUI-based.

This release only loads game folders, not files. Use purify to load ROM
files in bsnes.

Note that this will likely be the last release for a long time, and that
I will probably rename the emulator for the next release, due to how
many additional systems it now supports.
2012-08-08 00:08:37 +10:00
Tim Allen
be625cc0fb Update to v089r18 release.
byuu says:

Changelog:
- fixed bsnes to let config files and system folders to be in the same
  folder as the executable
- fixed RawInput driver to compile again without linear_vector
- fixed phoenix/Windows to compile again without linear_vector
- fixed old vs new name warnings on MinGW w64 (technically the warnings
  were erroneous, but I worked around them anyway)
- added memory export hotkey (SNES driver only; mainly for FEoEZ
  translation)
- restored WRAM randomization for v090 stability (we can discuss that
  idea for v091+)
- fixed SuperFX / SA-1 "0x" prefix in the header generation (drop it
  into the latest purify if you want)
- added nall/Makefile uname support for UnxUtils (was breaking
  compilation with full UnxUtils in your path otherwise)
2012-08-07 23:28:00 +10:00
Tim Allen
4cb8b51606 Update to purify v00r07 release.
byuu says:

This update uses the latest manifest.xml mappings. It also adds a new
"Update Manifests" button that can be used to quickly regenerate all
manifests (sans Famicom games ... since I strip the iNES header, that
information is gone. We can't support Famicom manifest.xml updates until
we have a database.) This is different than the before wrapping of the
functionality on the convert games button. You can also trigger this on
the command-line with "purify synchronize"

g6672D, great catch. This was fixed, thank you.

[g6672D's bug was: "SA-1 and SuperFX are missing the "0x" for
program.rom/save.rwm size." - Ed.]
2012-07-23 22:53:28 +10:00
Tim Allen
87cb164f7c Update to v089r17 release.
byuu says:

This implements the spec from the XML part 3 thread:

    http://board.byuu.org/viewtopic.php?f=16&t=2998

It's also propagated the changes to nall and purify, so you can test
this one.

This is basically it, after years of effort I feel I finally have
a fully consistent and logical XML board format.
The only things left to change will be: modifications if emulation turns
out to be incorrect (eg we missed some MMIO mirrors, or mirrored too
much), and new additions.

And of course, I'm giving it a bit of time for good arguments against
the format.

Other than that, this release removes linear_vector and pointer_vector,
as vector is better than linear_vector and I've never used
pointer_vector.
vector also gets move(), which is a way to use move-semantics across
types. It lets you steal the underlying memory pool, effectively
destroying the vector without a copy.
This works really nicely with the move for read() functions to return
vector<uint8> instead of taking (uint8_t*&, unsigned&) parameters.
2012-07-22 22:27:18 +10:00
Tim Allen
c1318961d8 Update to v089r16 release.
byuu says:

Changelog:
- eliminated <mmio>, <mcu> tags [they are merged to their parent nodes
  now]
- added <ram name= size=> tag to EpsonRTC, SharpRTC
- added <firmware> tag to DSP-n, ST-01n, ST-018, Cx4
- interface->path(0) now returns the system folder, which can be used
  for storage now
- as a fun proof-of-concept, I've simulated SNES warm power cycles by
  saving and loading work RAM (same effect if you instantly swapped
  carts)
  - long-term, I'm not sure how I want to do this. The power menu option
    makes no sense with warm RAM
  - I like the idea of decaying RAM based on timestamp from last play;
    and power can just force the timestamp to 0 (which will corrupt all
    RAM)
- Interface::firmware is gone. The cores now load firmware inside their
  boot up routines
- you now get a message on the screen if the emulator can't find
  firmware, should help with "I just get a black screen" messages

I'd like to start preparing for a v090 release. I think we're almost
there now. Have to update nall/cartridge and purify to handle XML
changes first.
2012-07-22 22:27:14 +10:00
Tim Allen
791e64951b Update to v089r15 release.
byuu says:

Changelog:
- SuperFX has its own ROM and RAM
- Cx4 has its own ROM
- SPC7110 has its own ProgramROM, DataROM and RAM
- OBC1 has its own RAM
- BsxCartridge has its own ROM, RAM and PSRAM
- mapping changes to accommodate the above
2012-07-09 21:40:23 +10:00
Tim Allen
27af50099f Update to v089r14 release.
byuu says:

Changelog:
- NSS emulation improvement (DIP is 8-bits, not 16-bits; can be remapped
  via XML now like all the other chips)
- SA-1 memory map improvements (IRAM and BWRAM can be saved; ROM, IRAM
  and BWRAM are separate from Cartridge::ROM, RAM; no MCU)
- S-DD1 memory map improvements (ROM, RAM inside mapping; no MCU)
- SPC7110 memory map improvements (ROM, RAM inside mapping; no MCU; not
  finished yet [have to handle 8mbit expansion somehow now)

I have tried multiple times now to get the SuperFX core to use internal
ROM and RAM (separate from Cartridge::ROM, RAM) to no avail.
Not sure what the hell is going on there. Trace logs of 430MB are not
enticing ...

So right now: SuperFX, SPC7110 and BS-X cheat by mapping stuff to
Cartridge::ROM, RAM still. They need to not do that.
2012-07-08 12:57:34 +10:00
Tim Allen
fbd52c7e5f Update to v089r13 release.
[also, replace the old purify tool with the new tool by the same name.
There were some previous releases outside the WIP thread, but this is
the first one that actually works with a WIP release. -Ed.]

byuu says:

Fixes up loading issues with recent purify changes, and purify also
works on BS/ST file types now and should be a bit more crash-resistant.
2012-06-25 22:49:39 +10:00
Tim Allen
36795e8061 Update to v089r12 release.
byuu says:

Changelog:
- Game Boy XML uses <cartridge><board type="MBC3"/> instead of
  <cartridge mapper="MBC3">
- if you run bsnes with a filename argument, it will invoke "purify
  filename" and exit immediately
  - this chains: purify will turn the file into a game folder, and then
    invoke bsnes with the game folder name
    - net result: you can drag a ZIP file onto bsnes or associate SMC
      headered ROMs with bsnes and they'll just work
- new nall: unified usage of - vs _ vs nothing on filenames; fancier
  lstring; fancier image (constructor for creating from filename or from
  memory); etc
- new phoenix: images in ListView, GTK+ merges the check box into the
  first column like the other targets do, etc
- browser list now uses icons to differentiate system folders from game
  folders (the game folder icon sucks, I'm open to suggestions though,
  as long as it's available on Debian Squeeze in /usr/share/icons, no
  custom stuff please)
2012-06-18 20:13:51 +10:00
Tim Allen
ec8350794a Update to v089r11 release.
byuu says:

Changelog:
- SPC7110 $480b (and its settings in $4805-6 + $4807) is now fully
  emulated
- decompressor restructured and commented accordingly

The final parts remaining for the SPC7110 core, all within the
decompression engine:
- need to detect when 15+ input bytes are read for one output byte and
  simulate a crash somehow (don't have to perfectly simulate corrupted
  data stream)
- need to emulate time required to decompress data (doesn't have to be
  perfect, just something other than instantaneous)
- need to determine what $480c status flags d6-d0 are for, as best we
  can anyway
2012-06-06 19:57:53 +10:00
Tim Allen
4545e7c62d Update to v089r10 release.
byuu says:

Changelog:
- Cydrak merged all three SPC7110 decompression routines into one, cuts
  the size in half
- fixed masking of $4803.d7 and $4813.d7
- data port out of bounds accesses emulated correctly for app SPC7110
  boards
- all(?) data port $4810 reload cases now supported
- basic timing for $4805-6 seeking; reworked delay timing to work better
  as well
- fixed $480c.d7 flag (1 = ready, not busy)
- AbsoluteInput returns -32768 if presentation window lacks focus and
  you don't always allow input
2012-05-31 22:27:46 +10:00
Tim Allen
3302398907 Update to v089r09 release.
byuu says:

Changelog:
- SPC7110 data port emulation greatly improved
- SPC7110 $480b.d1 emulated (but $4805-4806 does not work right for mode
  2 decompression yet)
- MSU1 audio output will be muted when the S-DSP FLG ($6c).d6 (mute)
  flag is set
- MSU1 will read filenames from manifest now (defaults to msu1.rom and
  track-#.pcm if missing ... for now)
- bugfixes with MSU1 load state and track seek (and $4804 was wrapping
  into $4805 to change the track#)
- Link coprocessor removed (it was meant for ST018 HLE, which never
  happened)

Notes for things I forgot but need to address:
- $4813 needs to be uint7 for the set_data_offset() to not allow reading
  A23 as set ever (or we can mask)
- AbsoluteInput when window doesn't have focus should return -32768, not
  0
- need to support input ID lists that aren't linear (0-7), but arbitrary
  (0,1,6,7 or whatever)
2012-05-29 22:20:46 +10:00
Tim Allen
189e707594 Update to v089r08 release.
byuu says:

Changelog:
- Super Game Boy, BS-X Satellaview and Sufami Turbo cartridges all load
  manifests that specify their file names, and they all work
- Sufami Turbo can now properly handle carts without RAM, or empty slots
  entirely
- Emulator::Interface structures no longer specify any file names, ever
- exposed "capability.(cheats,states)" now. So far, this just means the
  GBA doesn't show the cheat editor, since it doesn't support cheat
  codes yet
- as such, state manager and cheat editor windows auto-hide (may be
  a tiny bit inconvenient, but it makes not having to sync them or deal
  with input when no cart is loaded easier)
- added "AbsoluteInput" type, which returns mouse coordinates from
  -32767,-32767 (top left) to +32767,+32767 (bottom right) or
  -32768,-32768 (offscreen)

AbsoluteInput is just something I'm toying with. Idea is to support eg
Super Scope or Justifier, or possibly some future Famicom controllers
that are absolute-indexed. The coordinates are scaled, so the bigger
your window, the more precise they are. But obviously you can't get more
precise than the emulated system, so 1x scale will behave the same
anyway. I haven't hooked it up yet, need to mess with the idea of custom
cursors via phoenix for that first. Also not sure if it will feel
smoother or not ... if you resize the window, your mouse will seem to
move slower. Still, not having to capture the mouse for SS/JS may be
nicer yet. But we'll see ... just experimenting for now.
2012-05-28 09:50:50 +10:00
Tim Allen
9a8a54c75e Update to v089r07 release.
byuu says:

Not even purify makes compatible images for this WIP.
Unless you want to figure it out yourself, I'd suggest waiting for an
updated tool before using subsequent WIPs.

Changelog:
- MSU1 initializes data port + audio track to 0
- MSU1 implements audio track error flag on $2000.d3
- manifest.xml now controls file names for cartridge folders ... mostly

Regressions:
- Super Game Boy support is broken
- Sufami Turbo support is broken

So, basically Emulator::Interface() now has:

    void load(const string &manifest);
    void save();

The first one will analyze the manifest, and call all the ROM + RAM
loadRequest() commands necessary to run the game.
The second one will call saveRequest() commands on all writable and
non-volatile storage (basically if it's a RAM type and has a filename
specified, it gets saved to disk.)
save() shrinks the size of Emulator::Interface() by hiding information
one is unlikely to care about. It also makes it much easier to save.
The core auto-calls this when you unload a game as well. So the only
time you ever have to worry about it is if you want to save RAM files
mid-game (in case you want to do periodic backups in case of a crash.)
2012-05-26 18:18:42 +10:00
Tim Allen
d418eda97c Update to v089r06 release.
[Yes, the release number is re-used. -Ed.]

byuu says:

I had some bugs in r07 that I couldn't track down, DKJM2's clock was
getting all out of sync.
So I just reverted to r05, blew away both RTCs entirely, and wrote them
cleanly from scratch (obviously looking off the old code.) A bit
extreme, but it worked.
I believe I found the error in the process, day and month were resetting
the counter to 0 instead of 1, it wasn't handling leap year, etc.
While I was at it, I fixed the day-of-week calculation. The SharpRTC
epoch is actually 1000-01-01, and not 1900-01-01.
I'm sure you guys will be really happy that if it ever becomes 1000AD
again and you're playing a ROM hack that uses the SharpRTC and relies on
its weekday value that it will show the correct day now ...
Kind of a pain to compute, but nothing compared to the seventh circle of
hell that was my IBM dBase III Julian<>Gregorian conversion functions :/
Also found a few bugs in the Epson code this way. And I moved the round
seconds actions and flag clear to +125us after flag set.

So, if you had the old r06 or r07, please delete those.

Unfortunately, this took all of my energy today, so the file names
inside manifest changes will have to be in the next WIP.

EDIT: ran a diff against old r07 and new r06.
- added if(days == 31) case twice in EpsonRTC::tick_day()
- forgot weekday = 0; in SharpRTC::load()
- need to move the cartridge+cheat objects up in sfc/Makefile again
- System::init() needs assert(interface != nullptr /* not 0 */)
2012-05-25 09:26:06 +10:00
Tim Allen
5dbd5f4d0f Update to v089r07 release.
byuu says:

Changelog:
- EpsonRTC emulation improved further (stop/pause blocks IRQs, verified
  secondhi >= 3 triggers 30-second adjust (even on invalid BCD),
  second-changed flag is mirrored to minute+hour+day+month+weekday,
  improved busy timing, etc.)
- SharpRTC rewritten, works like EpsonRTC now in that it has its own
  timing thread and ticks with the emulation
- won't attempt to read from an unopen file stream now (I think this is
  what was crashing Sufami Turbo without SRAM?)
- added Tools -> Synchronize Time option below load/save state options.
  Only appears when you play a game with an emulated RTC chip

Just realized that I used 125ms for the 30-second adjust instead of
125us, so I'll fix that in the next WIP.
Aside from that, this is as good as the emulation is going to get.
There's still a couple of absolutely psychopathic edge cases that are
just too damn difficult to simulate.
So that leaves us with data port control + decompression status
registers to investigate before SPC7110 will be finished.
2012-05-23 21:27:45 +10:00
Tim Allen
d6001a2df4 Update to v089r06 release.
byuu says:

Changelog:
- renamed SRTC -> SharpRTC
- renamed RTC4513 -> EpsonRTC (consistent with DSP naming schema)
- full emulation of invalid BCD values for EpsonRTC
- fixed EpsonRTC IRQ mask

Remaining SPC7110 tasks:
- RTC: test 30-second adjust with all values from 00-7f
- RTC: hold is supposed to tick the clock one second after being
  released?
- RTC: wait times are too long (need to use >32KHz oscillation to
  simulate it properly)
- Data Port: test $4818 more thoroughly (not too important)
- Decompression: test $480c more thoroughly (very important)
- Decompression: perform some tests on DMA transferring data, especially
  with $4807 set

Write-offs, at least for now:
- Decompression: emulation of the crash/glitch behavior seen on the real
  chip when fed invalid data
- Decompression: I can find no use of $4808
- ALU: Booth cycles for MUL/DIV (this could actually be rather important
  if the game reads simpler values quickly [some shoddy games did this
  with the CPU ALU])
- RTC: delay after hold release for $4841 accesses
- RTC: 125uS delay after 30-second adjust that will screw with registers
  in odd ways if your read or write too soon
- RTC: psychotic behavior of reading too early returning port address
  - 1
2012-05-22 22:10:00 +10:00
Tim Allen
bd61432322 Update to v089r05 release.
byuu says:

I split the RTC-4513 code from the SPC7110 code (and obviously in the
XML mapping as well), since they are separate chips on the FEoEZ PCB.
In this way, you can use just the RTC-4513 in homebrew now if you want.
It's a bit nicer than the Sharp RTC from Dai Kaijuu Monogatari II.
This was needed anyway, it has an internal oscillator that's not
divisible by the SNES clock used by the SPC7110; and both the RTC and
decompression code need to be running their own threads anyway.

In the process, I rewrote the way variables are stored to use named
integers rather than a block of memory. Makes the code a lot easier on
the eyes, and more importantly, will make emulating bad BCD values
a whole lot easier.
2012-05-21 20:56:48 +10:00
Tim Allen
0611fefefa Update to v089r04 release.
byuu says:

Changelog: (SPC7110)
- emulated $480b.d0 + $4807 deinterleave mode
- cleaned up decompression core (I'd still like to wipe out those static
  variables, those are bad for save states.)
- improved emulation of data port ($481a only increments, never reads)
- improved emulation of RTC (block appropriate bits in the hour register
  based on 12/24h mode select; $4840 modes != 1 all disable the chip;
  added MDR; etc.)
2012-05-19 16:28:54 +10:00
Tim Allen
6ac7b733bd Update to v089r03 release.
byuu says:

Substantial improvements to SPC7110 emulation. Added all of the findings
from http://byuu.org/temp/spc7110-mmio.txt that I understood.

I also completely rewrote the RTC. We only had about ~40% of the chip
emulated before. Turns out there's cool stuff like spare RAM, calendar
disable, 12-hour mode, IRQs, IRQ masking, duty cycles, etc. So I went
ahead and emulated all of it. The upper bits on hour+ don't work as
nocash described though, not sure what doc he was reading. The Epson
RTC-4513 manual I have doesn't explain any of the registers.
The new RTC core also ticks seconds based on the emulated clock, and not
on the system clock. This is going to suck for people wanting to keep
the in-game clock synced with their computer, who also abuse fast
forward and save states. Fast forward makes the clock run faster, and
save states will jump the clock to the time it was at when you took the
save state. (It will keep track of the number of seconds between
unloading the game and loading it again, so time passes normally there.)
This is required, however, and how I'm going to rearrange all of the
RTCs for all systems. Any other method can be detected by the game, and
is thus not faithful emulation. To help with this, I'll probably make an
RTC time tool so that you can adjust the time when the emulator isn't
running, but I don't intend to bundle that into bsnes.
New state format bit-packs the RTCRAM values, and it also uses a 64-bit
timestamp. So it's 16 bytes now instead of 20 bytes. S-RTC will drop
from 16 to 12 when it's done.
The RTC busy flag timing needs to be refined with more hardware tests,
there's a slim chance of the game hanging on save at the moment.

The SPC7110 ALU delays are emulated now, too. They may not be perfectly
accurate, but they get the basic gist down.
The only hack that needs to be removed now is the decompression busy
flag. That's ... not going to be fun.

I also redid the mouse emulation. I was polling the mouse position
multiple times per latch. So it should be a bit more precise now,
I hope.
I read it regardless of latch state, dunno if that's good or not.
2012-05-16 10:27:34 +10:00
Tim Allen
a1e4c67a05 Update to v089r02 release.
(r01 was not posted to the WIP thread)

byuu says:

r01 changelog:
- major improvements to SPC7110 MCU (memory controller unit)
- revised SPC7110 memory map to reflect aforementioned improvements
- added "Toggle Tracer" hotkey to target-ethos (only works for SFC so
  far, I plan to use this as a lightweight laevateinn for FEoEZ)

r02 changelog:
- quick fix: SRAM is mapped to 00-3f|80-bf:6000-7fff
- quick fix: $4830.d7 is SRAM chip enable, not SRAM write enable. Reads
  return 0x00 when this bit is clear
2012-05-14 23:32:55 +10:00
Tim Allen
73ebe093b8 Update to v089 release.
byuu says:

Changelog to v089:
- fix SA-1 Mini Yonku Shining Scorpion
- load from command-line
- remove SNES::Cartridge::NVRAM
- fix SGB save RAM
- update cheats.xml
- already mapped inputs cancel input assign

BS-X wasn't broken after all. I forgot that I ran purify on my BS-X
images, and that the BS Zelda ZIP I have has the disable ROM bit set.
Whoops.
2012-05-12 12:34:35 +10:00
Tim Allen
c3f9d421da Update to v088r16 release.
byuu says:

Changelog:
- fixed BGnxOFS to not cache when MOSAIC is not in effect [fixes Air
  Strike Patrol "Good Luck" text]
- added GameBoy::Interface::Hook for SGB bindings [SGB works again]
- do not create bsnes/ folder unless it is absolutely needed (eg you
  create a save state or state manager archive)
- SuperFX works [needed to call system.init() in Interface::Interface()]

Last chance for any bug reports, at this point I pretty much consider
ethos to be finished. It's shipping without BS-X BIOS game loading
support. Sorry, I can't figure that one out.
2012-05-10 09:35:29 +10:00
Tim Allen
689fc49047 Update to v088r15 release.
byuu says:

Changelog:
- default placement of presentation window optimized for 1024x768
  displays or larger (sorry if yours is smaller, move the window
  yourself.)
- Direct3D waits until a previous Vblank ends before waiting for the
  next Vblank to begin (fixes video timing analysis, and ---really---
  fast computers.)
- Window::setVisible(false) clears modality, but also fixed in Browser
  code as well (fixes loading images on Windows hanging)
- Browser won't consume full CPU resources (but timing analysis will,
  I don't want stalls to affect the results.)
- closing settings window while analyzing stops analysis
- you can load the SGB BIOS without a game (why the hell you would want
  to ...)
- escape closes the Browser window (it won't close other dialogs, it has
  to be hooked up per-window)
- just for fun, joypad hat up/down moves in Browser file list, any
  joypad button loads selected game [not very useful, lacks repeat, and
  there aren't GUI load file open buttons]
- Super Scope and Justifier crosshairs render correctly (probably
  doesn't belong in the core, but it's not something I suspect people
  want to do themselves ...)
- you can load GB, SGB, GB, SGB ... without problems (not happy with how
  I did this, but I don't want to add an Interface::setInterface()
  function yet)
- PAL timing works as I want now (if you want 50fps on a 60hz monitor,
  you must not use sync video) [needed to update the DSP frequency when
  toggling video/audio sync]
- not going to save input port selection for now (lot of work), but it
  will properly keep your port setting across cartridge loads at least
  [just goes to controller on emulator restart]
- SFC overscan on and off both work as expected now (off centers image,
  on shows entire image)
- laevateinn compiles properly now
- ethos goes to ~/.config/bsnes now that target-ui is dead [honestly,
  I recommend deleting the old folder and starting over]
- Emulator::Interface callbacks converted to virtual binding structure
  that GUI inherits from (simplifies binding callbacks)
    - this breaks Super Game Boy for a bit, I need to rethink
      system-specific bindings without direct inheritance

Timing analysis works spectacularly well on Windows, too. You won't get
your 100% perfect rate (unless maybe you leave the analysis running
overnight?), but it'll get really freaking close this way.
2012-05-08 09:29:03 +10:00
Tim Allen
cb97d98ad2 Update to v088r14 release.
byuu says:

Changelog:
- added NSS DIP switch settings window (when loading NSS carts with
  appropriate manifest.xml file)
- added video shader selection (they go in ~/.config/bsnes/Video
  Shaders/ now)
- added driver selection
- added timing settings (not only allows video/audio settings, also has
  code to dynamically compute the values for you ... and it actually
  works pretty good!)
- moved "None" controller device to bottom of list (it is the least
  likely to be used, after all)
- added Interface::path() to support MSU1, USART, Link
- input and hotkey mappings remember list position after assignment
- and more!

target-ethos now has all of the functionality of target-ui, and more.
Final code size for the port is 101.2KB (ethos) vs 167.6KB (ui).
A ~67% reduction in code size, yet it does even more! And you can add or
remove an entire system with only three lines of code (Makefile include,
header include, interface append.)

The only problem left is that the BS-X BIOS won't load the BS Zelda no
Densetsu file.
I can't figure out why it's not working, would appreciate any
assistance, but otherwise I'm probably just going to leave it broken for
v089, sorry.

So the show stoppers for a new release at this point are:
- fix laevateinn to compile with the new interface changes (shouldn't be
  too hard, it'll still use the old, direct interface.)
- clean up Emulator::Interface as much as possible (trim down
  Information, mediaRequest should use an alternate struct designed to
  load firmware / slots separately)
- enhance purify to strip SNES ROM headers, and it really needs a GUI
  interface
- it would be highly desirable to make a launcher that can create
  a cartridge folder from an existing ROM set (* ethos will need to
  accept command-line arguments for this.)
- probably need to remember which controller was selected in each port
  for each system across runs
- need to fix the cursor for Super Scope / Justifier games (move from
  19-bit to 32-bit colors broke it)
- have to refactor that cache.(hv)offset thing to fix ASP
2012-05-07 09:27:42 +10:00
Tim Allen
3cb04b101b Update to v088r13 release.
byuu says:

Changelog:
- fixed Super Game Boy input
- Sufami Turbo prompts to load second slot now (you can cancel to leave
  it empty)
- NEC/Hitachi/ARM DSP firmware is loaded; NEC RAM is saved
- folders are grouped properly: Sufami Turbo save RAM saves to its slot
  folder, etc.
- title shows properly (SGB shows GB game name only, BS-X slotted shows
  game name and optional slot name, etc.)
    - above extends to saving cheats and such in their correct folders
      as well
- added cheat editor and cheat database
    - and hooked up the requisite SGB mode loads and can use GB cheats,
      because that's kinda cool
- added state manager
- input settings, cheat editor and state manager all have erase (one)
  and reset (all) buttons now
- lots of cleanup and restructuring on Emulator::Interface; *almost*
  finished with it now

Remaining:
- BS-X BIOS won't show the data pack
- need XML mapping information window
- need NSS DIP switch settings window
- need video shaders
- need driver selection
- need to hide controllers that have no inputs from the input mapping
  list (tempted to just remove "None" as a controller option ...)

ethos is currently 88KB of code, ui is 167KB. We're missing about 5-10KB
of code in ethos to complete it, so the rewrite nearly cut the GUI code
size in half, while support all of the same functionality and allowing
the easy addition and removal of entire systems.
2012-05-06 16:34:46 +10:00
Tim Allen
5d273c5265 Update to v088r12 release.
byuu says:

Changelog:
- all hotkeys from target-ui now exist in target-ethos
- controller port menus now show up when you load a system (hidden if
  there are no options to choose from)
- tools menu auto-hides with no game open ... not much point to it then
- since we aren't using RawInput's multi-KB/MS support anyway, input and
  hotkey mappings remove KB0:: and turn MS0:: into Mouse::, makes it
  a lot easier to read
- added mute audio, sync video, sync audio, mask overscan
- added video settings: saturation, gamma, luminance, overscan
  horizontal, overscan vertical
- added audio settings: frequency, latency, resampler, volume
- added input settings: when focus is lost [ ] pause emulator [ ] allow
  input
- pausing and autopausing works
- status messages hooked up (show a message in status bar for a few
  seconds, then revert to normal status text)
- sub systems (SGB, BSX, ST) sorted below primary systems list
- added geometry settings cache
- Emulator::Interface cleanups and simplifications
- save states go into (cart foldername.extension/bsnes/state-#.bsa) now.
  Idea is to put emulator-specific data in their own subfolders

Caveats / Missing:
- SGB input does not work
- Sufami Turbo second slot doesn't work yet
- BS-X BIOS won't show the data pack
- need XML mapping information window
- need cheat editor and cheat database
- need state manager
- need video shaders
- need driver selection
- need NSS DIP switch settings
- need to hide controllers that have no inputs from the input mapping
  list

So for video settings, I used to have contrast/brightness/gamma.
Contrast was just a multiplier on intensity of each channel, and
brightness was an addition or subtraction against each channel. They
kind of overlapped and weren't that effective. The new setup has
saturation, gamma and luminance.

Saturation of 100% is normal. If you lower it, color information goes
away. 0% = grayscale. If you raise it, color intensity increases (and
clamps.) This is wonderful for GBA games, since they are oversaturated
to fucking death. Of course we'll want to normalize that inside the
core, so the same sat. value works on all systems, but for now it's
nice. If you raise saturation above 100%, it basically acts like
contrast used to. It's just that lowering it fades to grayscale rather
than black.

Adding doesn't really work well for brightness, it throws off the
relative distance between channels and looks like shit.  So now we have
luminance, which takes over the old contrast <100% role, and just fades
the pixels toward black. Obviously, luminance > 100% would be the same
as saturation > 100%, so that isn't allowed, it caps at 100% now.
Gamma's the same old function. Gamma curve on the lower-half of the
color range.
Effects are applied in the order they appear in the GUI: color ->
saturate -> gammify -> luminate -> output.
2012-05-04 22:47:41 +10:00
Tim Allen
8703d57030 Update to v088r11 release.
byuu says:

Changelog:
- phoenix has added Window::setModal(bool modal = true);
- file dialog is now modal. This allows emulation cores to request data
  and get it immediately before continuing the loading process
- save data is hooked up for most systems, still need to handle
  subsystem slot saves (Sufami Turbo, basically.)
- toggle fullscreen key binding added (Alt+Enter for now. I think F11 is
  probably better though, Enter is often mapped to game start button.)
- video scaling is in (center, scale, stretch), works the same in
  windowed and fullscreen mode (stretch hides resize window option), all
  in the settings menu now
- enough structure to map all saved paths for the browser and to load
  BS-X slotted carts, BS-X carts, single Sufami Turbo carts

Caveats / Missing:
- Super Game Boy input doesn't work yet (due to change in callback
  binding)
- doesn't load secondary Sufami Turbo slot yet
- BS-X BIOS isn't show the data pack games to load for some reason (ugh,
  I hate the shit out of debugging BS-X stuff ...)
- need mute audio, sync audio+video toggle, save/load state menu and
  quick keys, XML mapping information window
- need cheat editor and cheat database
- need state manager
- need to sort subsystems below main systems in load menu (basically
  just see if media.slot.size() > 0)
- need video shaders (will probably leave off filters for the time being
  ... due to that 24/30-bit thing)
- need video adjustments (contrast etc, overscan masks)
- need audio adjustments (frequency, latency, resampler, volume,
  per-system frequency)
- need driver selection and input focus policy (driver crash detection
  would be nice too)
- need NSS DIP switch settings (that one will be really fun)
- need to save and load window geometry settings
- need to hook up controller selection (won't be fun), create a map to
  hide controllers with no inputs to reassign
2012-05-03 22:36:47 +10:00
Tim Allen
9ad8b7eaac Update to v088r10 release.
byuu says:

ethos is going to be absolutely amazing. You guys are in for a treat :D
I'm impressing the hell out of myself with how well-structured this code
is, it's allowing me to do amazing new things.

Just a small sampling of what's in store (and already implemented):

The file browser will display folders as "[ folder name ]", and
cartridge folders as "Game Name" (no extension, no /) [icons would be
nicer, but well ... phoenix.]
Folders are sorted above cartridge folders.
Cartridge folders for other systems do not show up in the list.
Not only are unique paths stored for each image type, your position in
the list is saved across runs.
Some voodoo was added to GTK+ so that all targets even scroll directly
to that item when you open the list. Load->System->Enter restarts your
last game.
That sounds really simple and obvious, but it makes an -incredible-
difference. Didn't realize it until I tried an implementation of it,
wow.

The input mapping list now lets you bind as many hotkeys as you want to
any given input.
So SFC::Port1::Joypad::B = Keyboard::Z or Joypad::Button1 ... no need to
remap everything to switch between keyboard and joypad. Either one
activates the key.
There is a separate Hotkeys tab now. This should hopefully end the
confusion about how to remap hotkeys that users experience.
Hotkeys are different, too. Instead of OR logic, they use AND logic.
So Fullscreen = Keyboard::Alt and Keyboard::Enter. Both must be pressed
to enter the key. This lets you easily implement "super" modifier keys.

The actual codebase has new features the old UI never had, and has about
~50% of the old functionality (so far, of course), yet is only ~25% as
much code.
The entire GUI no longer needs to pull in all the headers for each
emulated system. It just needs a small interface header file.
Then bind the entire system with exactly **two** lines of code.
Everything is dynamically generated for you after that.
2012-05-01 22:27:50 +10:00
Tim Allen
76553756a2 Update to v088r09 release.
byuu says:

Lots of work on ethos, nothing more.
Settings window is in, InputManager pulls all the inputs from all cores
and binds them to ruby inputs, main window adds menu and dynamically
maps in all systems and cartridge slots and options and such, file
browser's back in, RAM is loaded and saved, etc. It's barely usable, but
you have to set up your inputs from the config file by hand for now.
2012-05-01 22:27:50 +10:00
Tim Allen
4fd20f0ae0 Update to v088r08 release.
byuu says:

From this WIP, I'm starting on the impossible task of
a declarative-based GUI, which I'm calling Ethos.
base/ becomes emulator/, and we add emulator/interface.hpp, which is
a base API that all emulation cores must implement in full.
(Right now, it's kind of a hybrid to work with the old GUI and the new
GUI at the same time, of course.)
Unlike the old interfaces, the new base class also provides all general
usability hooks: loading and saving files and states, cheat codes, etc.
The new interface also contains information and vector structs to
describe all possible loading methods, controller bindings, etc; and
gives names for them all.
The actual GUI in fact should not include eg <gba/gba.hpp> anymore.
Should speed up GUI compilation.

So the idea going forward is that ethos will build a list of emulators
right when the application starts up.
Once you've appended an emulator to that list, you're done. No more GUI
changes are needed to support that system.
The GUI will have code to parse the emulator interfaces list, and build
all the requisite GUI options dynamically, declarative style.

Ultimately, once the project is finished, the new GUI should look ~99%
identical to the current GUI. But it'll probably be a whole lot smaller.
2012-05-01 22:27:48 +10:00
Tim Allen
bb4db22a7d Update to v088r07 release.
byuu says:

(r05 and r06 were save points between large core modifications)

I would really appreciate extensive regression testing (especially
around SuperFX, Cx4, ST018, DSP-n, ST-01n, NES, GB) at this point.
The most critical core changes should be completed now. And it was an
unbelievable amount of restructuring.

Changelog:
- SuperFX core moved to Processor::GSU
- SNES::CPU core moved to Processor::R65816
- SNES::SMP core moved to Processor::SPC700
- NES::CPU core renamed to Processor::R6502
- use filestream to load RAM files from interface
- save states store SHA256 instead of CRC32 (CRC32 usage removed
  entirely from bsnes)
- nes/ -> fc/ and NES -> FC
- snes/ -> sfc/ and SNES -> SFC
- SuperFamicom::MappedRAM::copy uses stream instead of data+size
- Linux port uses gcc-4.7 (still using only gcc-4.6 subset, so you can
  make a gcc-4.6 symlink for now if you like.)
- all profiles and all targets compile and work properly

All eight instruction set cores have been moved to processor/ now.
Consistency's a wonderful thing.
The last remnants of NES/SNES are now limited to target-ui code; and the
nall/(system) folder names.
I'm building with gcc-4.7 on my Linux box now because the resultant
binaries are up to 20% faster (seriously) than gcc-4.6.
2012-05-01 22:02:14 +10:00
Tim Allen
67c13f749f Update to v088r04 release.
byuu says:

This will hopefully be a short-lived WIP, I just want to save
a breakpoint before I attempt something else.
NES, GB, GBC and GBA all load via const stream& now.
NES CPU core moved to Processor::RP2A03.
2012-04-28 16:35:51 +10:00
Tim Allen
616372e96b Update to v088r03 release.
byuu says:

static vector<uint8_t> file::read(const string &filename); replaces:
static bool file::read(const string &filename, uint8_t *&data, unsigned
&size); This allows automatic deletion of the underlying data.

Added vectorstream, which is obviously a vector<uint8_t> wrapper for
a data stream.  Plan is for all data accesses inside my emulation cores
to take stream objects, especially MSU1.  This lets you feed the core
anything: memorystream, filestream, zipstream, gzipstream, httpstream,
etc.  There will still be exceptions for link and serial, those need
actual library files on disk. But those aren't official hardware devices
anyway.

So to help with speed a bit, I'm rethinking the video rendering path.

Previous system:
- core outputs system-native samples (SNES = 19-bit LRGB, NES = 9-bit
  emphasis+palette, DMG = 2-bit grayscale, etc.)
- interfaceSystem transforms samples to 30-bit via lookup table inside
  the emulation core
- interfaceSystem masks off overscan areas, if enabled
- interfaceUI runs filter to produce new target buffer, if enabled
- interfaceUI transforms 30-bit video to native display depth (24-bit or
  30-bit), and applies color-adjustments (gamma, etc) at the same time

New system:
- all cores now generate an internal palette, and call
  Interface::videoColor(uint32_t source, uint16_t red, uint16_t green,
  uint16_t blue) to get native display color post-adjusted (gamma, etc
  applied already.)
- all cores output to uint32_t* buffer now (output video.palette[color]
  instead of just color)
- interfaceUI runs filter to produce new target buffer, if enabled
- interfaceUI memcpy()'s buffer to the video card

videoColor() is pretty neat. source is the raw pixel (as per the
old-format, 19-bit SNES, 9-bit NES, etc), and you can create a color
from that if you really want to. Or return that value to get a buffer
just like v088 and below.  red, green, blue are 16-bits per channel,
because why the hell not, right? Just lop off all the bits you don't
want. If you have more bits on your display than that, fuck you :P

The last step is extremely difficult to avoid. Video cards can and do
have pitches that differ from the width of the texture.  Trying to make
the core account for this would be really awful. And even if we did
that, the emulation routine would need to write directly to a video card
RAM buffer.  Some APIs require you to lock the video buffer while
writing, so this would leave the video buffer locked for a long time.
Probably not catastrophic, but still awful.  And lastly, if the
emulation core tried writing directly to the display texture, software
filters would no longer be possible (unless you -really- jump through
hooks and divert to a memory buffer when a filter is enabled, but ...
fuck.)

Anyway, the point of all that work was to eliminate an extra video copy,
and the need for a really painful 30-bit to 24-bit conversion (three
shifts, three masks, three array indexes.) So this basically reverts us,
performance-wise, to where we were pre-30 bit support.

[...]

The downside to this is that we're going to need a filter for each
output depth. Since the array type is uint32_t*, and I don't intend to
support higher or lower depths, we really only need 24+30-bit versions
of each filter.  Kinda shitty, but oh well.
2012-04-27 22:12:53 +10:00
Tim Allen
bba597fc6f Update to v088r02 release.
byuu says:

Basically, the current implementation of nall/array is deprecated now.
The old method was for non-reference types, it acted like a vector for
POD types (raw memory allocation instead of placement new construction.)
And for reference types, it acted like an unordered set. Yeah, not good.

As of right now, nall/array is no longer used. The vector type usage was
replaced with actual vectors.
I've created nall/set, which now contains the specialization for
reference types.
nall/set basically acts much like std::unordered_set. No auto-sort, only
one of each type is allowed, automatic growth.
This will be the same both for reference and non-reference types ...
however, the non-reference type wasn't implemented just yet.
Future plans for nall/array are for it to be a statically allocated
block of memory, ala array<type, size>, which is meant for RAII memory
usage.
Have to work on the specifics, eg the size as a template parameter may
be problematic. I'd like to return allocated chunks of memory (eg
file::read) in this container so that I don't have to manually free the
data anymore.

I also removed nall/moduloarray, and moved that into the SNES DSP class,
since that's the only thing that uses it.
2012-04-26 20:56:15 +10:00
Tim Allen
abe639ea91 Update to v088r01 release.
byuu says:

- GB::CPU::Core -> Processor::LR35902
- Processor::LR35902 -> jump table to switch table
- GB::LCD -> GB::PPU
- static frequency for DMG (no multiplication on clock ticks)
- GB::PPU::interface->videoRefresh() moved outside scheduler (use host
  thread)
- namespace NES  -> Famicom
- namespace SNES -> SuperFamicom
- namespace GB   -> GameBoy
- namespace GBA  -> GameBoyAdvance
- removed boot.rom writeout in GB::System
2012-04-26 20:51:13 +10:00
Tim Allen
77bb5b7891 Update to v088 release.
byuu says:

Changes to v088:
- OBJ mosaic Y fix
- Laevateinn compilation
- Remove filebrowser extra code
- Fix -march=native on Windows
- Fix purify mkdir
- GBA sound volume
- Add .gbb
- free firmware memory after file load
- Add GBA game to profile list (Yoshi's Island should work)
2012-04-24 23:17:52 +10:00
Tim Allen
4b2944c39b Update to v087r30 release.
byuu says:

Changelog:
- DMA channel masks added (some are 27-bit source/target and some are
  14-bit length -- hooray, varuint_t class.)
- No more state.pending flags. Instead, we set dma.pending flag when we
  want a transfer (fixes GBA Video - Pokemon audio) [Cydrak]
- fixed OBJ Vmosaic [Cydrak, krom]
- OBJ cannot read <=0x13fff in BG modes 3-5 (fixes the garbled tile at
  the top-left of some games)
- DMA timing should be much closer to hardware now, but probably not
  perfect
- PPU frame blending uses blargg's bit-perfect, rounded method (slower,
  but what can you do?)
- GBA carts really unload now
- added nall/gba/cartridge.hpp: used when there is no manifest. Scans
  ROMs for library tags, and selects the first valid one found
- added EEPROM auto-detection when EEPROM size=0. Forces disk/save state
  size to 8192 (otherwise states could crash between pre and post
  detect.)
    - detects first read after a set read address command when the size
      is zero, and sets all subsequent bit-lengths to that value, prints
      detected size to terminal
- added nall/nes/cartridge.hpp: moves iNES detection out of emulation
  core.

Important to note: long-term goal is to remove all
nall/(system)/cartridge.hpp detections from the core and replace with
databases. All in good time.
Anyway, the GBA workarounds should work for ~98.5% of the library, if my
pre-scanning was correct (~40 games with odd tags. I reject ones without
numeric versions now, too.)

I think we're basically at a point where we can release a new version
now. Compatibility should be relatively high (at least for a first
release), and fixes are only going to affect one or two games at a time.
I'd like to start doing some major cleaning house internally (rename
NES->Famicom, SNES->SuperFamicom and such.) Would be much wiser to do
that on a .01 WIP to minimize regressions.

The main problems with a release now:
- speed is pretty bad, haven't really optimized much yet (not sure how
  much we can improve it yet, this usually isn't easy)
- sound isn't -great-, but the GBA audio sucks anyway :P
- couple of known bugs (Sonic X video, etc.)
2012-04-22 20:49:19 +10:00
Tim Allen
4c29e6fbab Update to v087r29 release.
byuu says:

Changelog:
- revised NES XML tag nesting
- program.rom is going to refer to PRG+CHR combined. Split is going to
  have to use different file names
- slot loader is gone (good riddance!)
- "Cartridge -> Load Game Boy Advance Cartridge ..." has become "Load ->
  Game Boy Advance ..."
- Load Satellaview Slotted Cartridge is gone. If you load an SNES
  cartridge and it sees <bsx><slot>, it asks if you want to load a BS-X
  data pack
- If you load a Sufami Turbo cartridge with <cartridge linkable="true">,
  it asks if you want to link in another Sufami Turbo cartridge
- if you try and load the same exact Sufami Turbo cartridge in both
  slots, it yells at you for being an idiot :P
2012-04-20 22:48:09 +10:00
Tim Allen
a454e9d927 Update to v087r28 release.
byuu says:

Be sure to run make install, and move required images to their appropriate system profile folders.
I still have no warnings in place if those images aren't present.

Changelog:
- OBJ mosaic should hopefully be emulated correctly now (thanks to krom
  and Cydrak for testing the hardware behavior)
- emulated dummy serial registers, fixes Sonic Advance (you may still
  need to specify 512KB FlashROM with an appropriate ID, I used
  Panaonic's)
- GBA core exits scheduler (PPU thread) and calls
  interface->videoRefresh() from main thread (not required, just nice)
- SRAM, FRAM, EEPROM and FlashROM initialized to 0xFF if it does not
  exist (probably not needed, but FlashROM likes to reset to 0xFF
  anyway)
- GBA manifest.xml for file-mode will now use "gamename.xml" instead of
  "gamename.gba.xml"
- started renaming "NES" to "Famicom" and "SNES" to "Super Famicom" in
  the GUI (may or may not change source code in the long-term)
- removed target-libsnes/
- added profile/

Profiles are the major new feature. So far we have:

    Famicom.sys/{nothing (yet?)}
    Super Famicom.sys/{ipl.rom}
    Game Boy.sys/{boot.rom}
    Game Boy Color.sys/{boot.rom}
    Game Boy Advance.sys/{bios.rom[not included]}
    Super Game Boy.sfc/{boot.rom,program.rom[not included]}
    BS-X Satellaview.sfc/{program.rom,bsx.ram,bsx.pram}
    Sufami Turbo.sfc/{program.rom}

The SGB, BSX and ST cartridges ask you to load GB, BS or ST cartridges
directly now. No slot loader for them.  So the obvious downsides: you
can't quickly pick between different SGB BIOSes, but why would you want
to? Just use SGB2/JP.  It's still possible, so I'll sacrifice a little
complexity for a rare case to make it a lot easier for the more common
case.  ST cartridges currently won't let you load the secondary slot.
BS-X Town cart is the only useful game to load with nothing in the slot,
but only barely, since games are all seeded on flash and not on PSRAM
images. We can revisit a way to boot the BIOS directly if and when we
get the satellite uplink emulated and data can be downloaded onto the
PSRAM :P BS-X slotted cartridges still require the secondary slot.

My plan for BS-X slotted cartridges is to require a manifest.xml to
specify that it has the BS-X slot present.  Otherwise, we have to load
the ROM into the SNES cartridge class, and parse its header before we
can find out if it has one. Screw that.  If it's in the XML, I can tell
before loading the ROM if I need to present you with an optional slot
loading dialog.  I will probably do something similar for Sufami Turbo.
Not all games even work with a secondary slot, so why ask you to load
a second slot for them? Let the XML request a second slot. A complete
Sufami Turbo ROM set will be trivial anyway.  Not sure how I want to do
the sub dialog yet. We want basic file loading, but we don't want it to
look like the dialog 'didn't do anything' if it pops back open
immediately again. Maybe change the background color of the dialog to
a darker gray? Tacky, but it'd give you the visual cue without the need
for some subtle text changes.
2012-04-18 23:58:04 +10:00
Tim Allen
d2241f1931 Update to v087r27 release.
byuu says:

Changelog:
- serialize processor.pc.data, not processor.pc
- call CPU processor.setMode() in ARM serialize
- serialize BIOS.mdr
- support SRAM > 32KB
- EEPROM, FlashROM serialize
- EEPROM lose nall/bitarray.hpp
- noise line feed after envelope
- space out PSR read
- ST018 needs byte reads fixed (don't align) [fixes HNMS2]
- flush sram/eeprom/flashrom to 0 on cartridge load
- APU/PPU dont sync back to CPU if syncing for state
- fixed APU sync problems in GB/GBC core that could possibly wreck save
  states as well

Quite a lot of little problems there. I believe GBA save states are
fixed now.
2012-04-17 22:16:54 +10:00
Tim Allen
0cd0dcd811 Update to v087r26 release.
byuu says:

Changelog:
- fixed FIFO[1] reset behavior (fixes audio in Sword of Mana)
- added FlashROM emulation (both sizes)
- GBA parses RAM settings from manifest.xml now
- save RAM is written to disk now
- added save state support (it's currently broken, though)
- fixed ROM/RAM access timings
- open bus should mostly work (we don't do the PC+12 stuff yet)
- emulated the undocumented memory control register (mirror IWRAM,
  disable I+EWRAM, EWRAM wait state count)
- emulated keypad interrupts
- emulated STOP (freezes video, audio, DMA and timers; only breaks on
  keypad IRQs)
- probably a lot more, it was a long night ...

Show stoppers, missing things, broken things, etc:
- ST018 is still completely broken
- GBC audio sequencer apparently needs work
- GBA audio FIFO buffer seems too quiet
- PHI / ROM prefetch needs to be emulated (no idea on how to do this,
  especially PHI)
- SOUNDBIAS 64/128/256khz modes should output at that resolution
  (really, we need to simulate PWM properly, no idea on how to do this)
- object mosaic top-left coordinates are wrong (minor, fixing will
  actually make the effect look worse)
- need to emulate PPU greenswap and color palette distortion (no idea on
  how do this)
- need GBA save type database (I would also LIKE to blacklist
  / patch-out trainers, but that's a discussion for another day.)
- some ARM ops advance the prefetch buffer, so you can read PC+12 in
  some cases
2012-04-16 22:19:39 +10:00
Tim Allen
1c18812f47 Update to v087r25 release.
byuu says:

(r24 was a point release during merging of changes.)

This release is almost entirely Cydrak's direct work:
- Added ARM::sequential() and some WAITCNT timings
- Added Bus::io(uint32 pc), intended for prefetch timing
- Added ARM::load() with data rotation (fixed Mother 3 graphics)
- Added ARM::store() for data mirroring
- LDM, STM, and instruction fetch still use read/write()
- ARM::vector() no longer unmasks FIQs
- Set THUMB shifter flags via bit(), consistent with ARM
- Replace shifter loops with conditional tests

My changes:
- fixed sprite clipping on left-edge of screen
- added first system folder, GBA.system
- sudo make install is now make install
- make install will create GBA.system for you in your home folder

Windows users, take data/GBA.system and put it in the same folder as
bsnes.exe, and give it a BIOS named bios.rom
Or place it in your home folder (%APPDATA%/bsnes)
Also note that this is highly experimental, I'll probably be changing
things a lot before release.

EDIT: I botched the cartridge timing change. Will fix in r26. It'll
still run a bit too fast for now, unfortunately.
2012-04-15 16:49:56 +10:00
Tim Allen
28885db586 Update to v087r23 release.
byuu says:

Changelog:
- fixed cascading timers and readouts (speed hit from 320fps to 240fps;
  would be 155fps with r20 timers) (fixes Spyro)
- OBJ mode 3 acts like OBJ mode 2 now (may not be correct, but nobody
  has info on it)
- added background + object vertical+horizontal mosaic in all modes
  (linear+affine+bitmap)
- object mosaic uses sprite (0,0) for start coordinates, not screen
  (0,0) (again, nobody seems to have info on it)
- BIOS cannot be read by r(15)>=0x02000000; returns last BIOS read
  instead (I can't believe games rely on this to work ... fixes SMA
  Mario Bros.)

Mosaic is what concerns me the most, I've no idea if I'm doing it
correctly. But anything is probably better than nothing, so there's
that. I don't really notice the effect in Metroid Fusion. So either it's
broken, or it's really subtle.
2012-04-14 17:26:45 +10:00
Tim Allen
d423ae0a29 Update to v087r22 release.
byuu says:

Changelog:
- fixed below pixel green channel on color blending
- added semi-transparent objects [Exophase's method]
- added full support for windows (both inputs, OBJ windows, and output, with optional color effect disable)
- EEPROM uses nall::bitarray now to be friendlier to saving memory to disk
- removed incomplete mosaic support for now (too broken, untested)
- improved sprite priority. Hopefully it's right now.

Just about everything should look great now. It took 25 days, but we
finally have the BIOS rendering correctly.

In order to do OBJ windows, I had to drop my above/below buffers
entirely. I went with the nuclear option.  There's separate layers for
all BGs and objects. I build the OBJ window table during object
rendering.  So as a result, after rendering I go back and apply windows
(and the object window that now exists.) After that, I have to do
a painful Z-buffer select of the top two most important pixels.  Since
I now know the layers, the blending enable tests are a lot nicer, at
least.  But this obviously has quite a speed hit: 390fps to 325fps for
Mr. Driller 2 title screen.

TONC says that "bad" window coordinates do really insane things. GBAtek
says it's a simple y2 < y1 || y2 > 160 ? 160 : y2; x2 < x1 || x2 > 240
? 240 : x2; I like the GBAtek version more, so I went with that. I sure
hope it's right ... but my guess is the hardware does this with
a counter that wraps around or something.  Also, say you have two OBJ
mode 2 sprites that overlap each other, but with different priorities.
The lower (more important) priority sprite has a clear pixel, but the
higher priority sprite has a set pixel. Do we set the "inside OBJ
window" flag to true here? Eg does the value OR, or does it hold the
most important sprite's pixel value? Cydrak suspects it's OR-based,
I concur from what I can see.

Mosaic, I am at a loss. I really need a lot more information in order to
implement it.  For backgrounds, does it apply to the Vcounter of the
entire screen? Or does it apply post-scroll? Or does it even apply after
every adjust in affine/bitmap modes?  I'm betting the hcounter
background mosaic starts at the leftmost edge of the screen, and repeats
previous pixels to apply the effect. Like SNES, very simple.  For
sprites, the SNES didn't have this. Does the mosaic grid start at (0,0)
of the screen, or at (0,0) of each sprite? The latter will look a lot
nicer, but be a lot more complex. Is mosaic on affine objects any
different than mosaic of linear(tiled) objects?

With that out of the way, we still have to fix the CPU memory access
timing, add the rest of the CPU penalty cycles, the memory rotation
/ alignment / extend behavior needs to be fixed, the shifter desperately
needs to be moved from loops to single shift operations, and I need to
add flash memory support.
2012-04-13 21:50:53 +10:00
Tim Allen
303a0a67d0 Update to v087r21 release.
byuu says:

Timer speedup added. Boosts Mr. Driller 2 title from 170fps to 400fps.
Other games still benefit, but not as amazingly. I don't dip below
160fps ever here.
Reverted the memory speed to 2 for everything for now, to fix
Castlevania slowdown. We obviously need to add the N/S stuff before we
do that.
Added linear BG and linear OBJ mosaic-Y. Did not add mosaic-X, or any
mosaic to the affine/bitmap modes, because I'm not sure when to apply
the compensation.
Rewrote layer stuff. It now has two layers (above and below), and it
performs the four blending modes as needed.
Didn't add semi-transparent sprites because the docs are too confusing.
Added a blur filter directly into the PPU for now. This obviously
violates my interface, but F-Zero needed it for HUD display. We can
remove it when we have an official release with a blur filter available.
The filter still doesn't warp colors like a real GBA, because I don't
know the formula.
2012-04-10 21:41:35 +10:00
Tim Allen
01b4cb9919 Update to v087r20 release.
byuu says:

Changelog:
- HALT waits 16 cycles before testing IRQs instead of 1 (probably less
  precise, but provides a massive speedup) [we will need to work on this
  later]
- MMIO regs for CPU/PPU simplified by combining array accesses
- custom VRAM/PRAM/OAM read/write functions that emulate 8->16-bit
  writes
- 16-bit PRAM data (decent speedup)
- emulated memory access speed (but don't handle non-sequential
  penalties or PPU access penalties yet) [amazingly, doesn't help speed
  at all]
- misc. code cleanups

For this WIP, FPS for Mr. Driller 2 went from 88fps to 172fps.
Compatibility should be unchanged. Timers are still an interesting
avenue to increase performance, but will be very tough to handle the
16MHz timers with eg a period of 65535 (overflow every single tick.) And
that's basically the last major speed boost we'll be able to get.
Blending and windowing is going to hurt performance, but it remains to
be seen how much.
2012-04-09 16:41:27 +10:00
Tim Allen
17b5bae86a Update to v087r19 release.
byuu says:

Changelog:
- added FIFO buffer emulation (with DMA and all that jazz) [Cydrak]
- fixed timers and vcounter assign [Cydrak]
- emulated EEPROM (you have to change size manually for 14-bit mode, we
  need a database badly now) [SMA runs now]
- removed OAM array, now decoding directly to struct Object {} [128] and
  ObjectParam {} [32] (faster this way)
- check forceblank (still doesn't remove all garble between transitions,
  though??)
- lots of other stuff

Delete your settings.cfg, or manually change frequencyGBA to 32768, or
bad things will happen (this may change back to 256KHz-4MHz later.)

15 of 16 games are fully playable now, and look and sound great.
The major missing detail right now is PPU blending support, and we
really need to optimize the hell out of the code.
2012-04-09 16:19:32 +10:00
Tim Allen
6189c93f3d Update to v087r18 release.
byuu says:

Merged Cydrak's r17c changes:
- BG affine mode added
- BG bitmap mode added
- OBJ affine mode added
- fixed IRQ bug in THUMB mode (fixed almost every game)
- timers added (broke almost every game, whee.)

Cydrak is absolutely amazingly awesome and patient. This really wouldn't
be happening without him.

Also fixed some things from my end, including greatly improved sprite
priorities, and a much better priority sorter. Mr. Driller looks a lot
better now.
2012-04-07 18:17:49 +10:00
Tim Allen
1de484262c Update to v087r17 release.
byuu says:

Emulated GBC sound plus the new extensions to it.
I am kind of surprised by how little developers utilized the GBC audio
portion.
Mr. Driller now has sound effects, and Pinobee no Daibouken has BGM.
I still have yet to emulate the GBA extra sound channels and PWM. Need
to emulate timers and DMA 2 refresh mode before I can do that.

Also, I moved both GBC and GBA audio to use length = data; if(++length
== 0); rather than length = 64 - data; if(--length == 0); so that
I could return literal values for register reads.
I thought there was a good reason we used the latter version, but
I can't hear any audible difference even in GBC games, so oh well.
Lastly, I think the pattern[++offset] in the wave channel was a bug in
the DMG/GBC only. I really, really hope it doesn't apply to the GBA,
because that will make bank selection a serious pain in the ass.
2012-04-06 14:29:50 +10:00
Tim Allen
b8d0ec29b2 Update to v087r16 release.
byuu says:

Fixed the r15 mask per Cydrak.
Added DMA support (immediate + Vblank + Hblank + HDMA) with IRQ support.
Basically only missing FIFO reload mode for the APU on channel 2.
Added background linear renderer (tilemap mode.)
Added really inefficient pixel priority selector, so that all BGs+OBJ
could be visible onscreen at the same time.

As a result of the above:
* Mr. Driller is our first fully playable game
* Bakunetsu Dodge Ball Fighters is also fully playable
* Pinobee no Daibouken is also fully playable

Most games (15 of 16 tested) are now showing *something*, many things
look really really good in fact.

Absolutely essential missing components:
- APU
- CPU timers and their interrupts
- DMA FIFO mode
- OBJ affine mode
- BG affine mode
- BG bitmap mode
- PPU windows (BG and OBJ)
- PPU mosaic
- PPU blending modes
- SRAM / EEPROM (going to rely on a database, not heuristics. Homebrew
  will require a manifest file.)
2012-04-04 09:50:40 +10:00
Tim Allen
79a47d133a Update to v087r15 release.
byuu says:

Added linear (eg non-affine) sprite rendering, 4bpp and 8bpp with hflip
and vflip. Nothing else.
You can now see the Nintendo logo and Gameboy text at the end of the BIOS.
It's a start =)
2012-04-03 10:47:28 +10:00
Tim Allen
c8bb4949b1 Update to v087r14 release.
byuu says:

Fixed aforementioned issues.

[From a previous post:
- mul was using r(d) instead of r(n) for accumulate.
- mull didn't remove c/v clear.
- APU register mask was broken, so SOUNDBIAS was reading out wrong.
- APU was only mapping 0x088 and not 0x089 as well.
- Halfword reads in CPU+PPU+APU were all reading from the low address
  each time.]

All CPU+PPU registers are now hooked up (not that they do anything.)
SOUNDBIAS for APU was hooked up, got tired of working on it for the rest :P
I recall from the GB APU that you can't just assign values for the APU
MMIO regs. They do odd reload things as well.
Also, was using MMIO read code like this:

    return (
       (flaga << 0)
    || (flagb << 1)
    || (flagc << 2)
    );

Logical or doesn't work so well with building flags :P
Bad habit from how I split multiple conditionals across several lines.

So ... r14 is basically what r13 should have been yesterday, delaying my
schedule by yet another day :(
2012-04-01 11:41:15 +10:00
Tim Allen
f587cb0dcc Update to v087r13 release.
byuu says:

Contains all of Cydrak's fixes, sans PPU.
On the PPU front, I've hooked up 100% of read and write registers.
All three DISPSTAT IRQs (Vblank, Hblank, Vcoincidence) are connected now
as well.
Super Mario Advance now runs without *appearing* to crash, although it's
hard to tell since I have no video or sound :P
ARM Wrestler is known to run, as is the BIOS.
2012-03-31 19:17:36 +11:00
Tim Allen
ea086fe33f Update to v087r12 release.
byuu says:

Enough to get through the BIOS and into cartridge ROM.
I am a bit annoyed that I was basically told that the GBA PPU wasn't
that bad. Sprites are a clusterfuck, easily worse than Mode7, docs don't
even begin to explain them in enough detail.
This is going to be fun.
2012-03-31 19:14:31 +11:00
Tim Allen
c66cc73374 Update to v087r11 release.
byuu says:

Added all of the above fixes and changes. [A lot of individual fixes for
the ARM core from Cydrak - Ed.] Also new is pipeline_decode() to fetch
data, and IME/IE/IF support, and an ARM::processor.irqline flag that
triggers IRQs at 0x18. Only Vblank is hooked up, which is what SWI 4 was
waiting on previously.
I'm sure my interrupt support is horribly broken and wrong. I was never
able to really figure out IE/IF on the Game Boy, so there's no question
this is even worse.
It's now going crazy and writing 0 to IE forever now after the Vblank
IRQ triggers.
2012-03-29 22:58:10 +11:00
Tim Allen
5a1dcf5079 Update to v087r10 release.
byuu says:

Changelog:
- fixed THUMB hi immediate reads (immediate * 4)
- cartridge is properly mirrored to 32MB (eg 12mbit repeats as
  lo8+hi4+hi4+lo8+hi4+hi4) [so it's a bit slower than a standard memcpy
  fill]
- added ARM - load/store halfword register offset
- added ARM - load/store halfword immediate offset
- added ARM - load signed halfword/byte register offset
- added ARM - load signed halfword/byte immediate offset
- added decode() function to make opcode bit testing a lot clearer
  (didn't apply it to the debugger yet)

All ARMv4M and all THUMBv4 instructions should now be implemented.
Although I'm not sure if my implementations of the new instructions are
correct.
2012-03-27 22:02:57 +11:00
Tim Allen
e16dd58184 Update to v087r09 release.
byuu says:

Split apart necdsp: core is now in processor/upd96050 (wish I had
a better name for it, but there's no family name that is maskable.)
I would like to support the uPD7720 in the core as well, just for
completeness' sake, but I'll have to modify the decoder to drop one bit
from each mode.
So ... I'll do that later. Worst part is even if I do, I won't be able
to test it :(

Added all of Cydrak's changes. I also simplified LDMIA/STMIA and
PUSH/POP by merging the outer loops.
Probably infinitesimally slower, but less code is nicer. Maybe GCC
optimization will expand it, who knows.
2012-03-26 21:13:02 +11:00
Tim Allen
395e5a5639 Update to v087r08 release.
byuu says:

Added some more ARM opcodes, hooked up MMIO. Bind it with mmio[(addr
000-3ff)] = this; inside CPU/PPU/APU, goes to read(), write().
Also moved the Hitachi HG51B core to processor/, and split it apart from
the snes/chip/hitachidsp implementation.
This one actually worked really well. Very clean split between MMIO/DMA
and the processor core. I may move a more generic DMA function inside
the core, not sure yet.

I still believe the HG51B169 to be a variant of the HG51BS family, but
given they're meant to be incredibly flexible microcontrollers, it's
possible that each variant gets its own instruction set.
So, who knows. We'll worry about it if we ever find another HG51B DSP,
I guess.

GBA BIOS is constantly reading from 04000300, but it never writes. If
I return prng()&1, I can get it to proceed until it hits a bad opcode
(stc opcode, which the GBA lacks a coprocessor so ... bad codepath.)
Without it, it just reads that register forever and keeps resetting the
system, or something ...

I guess we're going to have to try and get ARMwrestler working, because
the BIOS seems to need too much emulation code to do anything at all.
2012-03-24 18:52:36 +11:00
Tim Allen
ec939065bd Update to v087r07 release.
There was a "v087r07pre" release that I unfortunately missed.

byuu says (about v087r07pre):

Creates the bsnes/processor folder. This has a shared ARM core there
which both the GBA and ST018 inherit.
There are going to be separate decoders, and revision-specific checks,
to support the differences between v3+.
In the future, I also want to move the other processor cores here:
- GBZ80 (GB, GBC)
- 65816 (SNES CPU, SA-1)
- NEC uPD (7725, 96050, maybe 7720 just for fun)
- Hitachi HG51B169
- SuperFX
- SPC700
- 65(C?)02

Basically, the GBA/ST018 forces my hand to start coding a bit more like
a multi-system emulator.

Right now, the ST018 is broken. Hence the pre. Apparently the GBA core
being used now has some bugs. So this'll be a nice way to stress-test
the GBA core a bit before we make it to ARMwrestler.

byuu says (about v087r07):

Both snes/chip/armdsp and gba/cpu use processor/arm now.
Fixed THUMB to execute the BL prefix and suffix separately. I can now
get the GBA BIOS stuck in some kind of infinite loop. Hooray ...
I guess?
2012-03-23 21:43:39 +11:00
Tim Allen
77578cd0a4 Update to v087r06 release.
byuu says:

I believe I've implemented every THUMB instruction now, although I'm
sure there are dozens of bugs in the implementation.

It seems that the last jump taken is ending up being off-by-two. It's
probably due to not masking/adjusting PC correctly at certain points.

I don't know if any other bugs are being hit prior to this or not.
I don't implement any I/O registers yet, and the BIOS seems to be poking
at a few of them along the way, so ... who knows.
I could also be reading the log wrong, but it looks to me like there's
some PSR setting the mode flag register to 0, which is supposed to be an
undefined behavior mode ... perhaps mrs has no effect on the m/t bits,
and it just affects the i/f bits?
2012-03-22 22:47:25 +11:00
Tim Allen
04087a74b0 Update to v087r05 release.
byuu says:

Implemented all of the ARMv3 instructions, and the bx rm instruction as
well. Already hit THUMB mode right at the start of the BIOS, sigh.
Implemented the first THUMB instruction to get that rolling. Also tried
to support the S flag to LDM/STM, but not sure how successful I was.
2012-03-21 22:08:16 +11:00
Tim Allen
6701403745 Update to v087r04 release.
byuu says:

GBA stuff re-added. Only thing missing that was there before is the ARM
branch opcode.
Since we're going to be staring at it for a very long time, I added
a more interesting test video pattern.

Went from 6fps to 912fps. Amazing what being able to divide can do for
a frame rate.
2012-03-19 22:19:53 +11:00
Tim Allen
95c62f92ac Update to v087r03 release.
byuu says:

Fixing the PPU stepping increased FPS to 250. Promising, at least, since
the ARM core is still severely overclocked.
However, I reverted back to r02. This one patches gameboy/ and GameBoy::
to gb/ and GB:: and that's it.
Sorry, I just couldn't shake this bad feeling about the code. There were
some poorly hacked-together constructs. I'd rather just redo two days of
work than feel bad about the codebase for the next several years. Going
to attempt the GBA bridge again. Third time's a charm, I suppose (there
was a pre-r03 WIP I abandoned as well.)
This isn't unprecedented, GB core took a few attempts like this as well.
2012-03-19 21:27:40 +11:00
Tim Allen
0f54be93b7 Update to v087r04 release.
byuu says:

Changelog:
- gameboy/ -> gb/
- GameBoy -> GB
- basic memory map for GBA
- enough code to execute the first BIOS instruction (b 0x68)

I have the code resetting r(15) to 0 on an exception just as a test.
Since that flushes the pipeline, that means we're basically executing "b
0x68" at 8MHz, and nothing else.
... and I am getting __6 motherfucking FPS__ at 4.4GHz on an i7.

Something is seriously, horribly, unfuckingbelievably wrong here, and
I can't figure out what it is.
My *fully complete* ARM core on the ST018 is even less efficient and
runs at 21.47MHz, and yet I get 60fps even after emulating the SNES
CPU+PPU @ 10+MHz each as well.

... I'm stuck. I can't proceed until we figure out what in the holy fuck
is going on here. So ... if anyone can help, please do. If we can't fix
this, the GBA emulation is dead.
I was able to profile on Windows, and I've included that in this WIP
under out/log.txt.
But it looks normal to me. But yeah, there's NO. FUCKING. WAY. This code
should be running this slowly.
2012-03-18 23:35:53 +11:00
Tim Allen
8db134843f Update to v087r03 release.
byuu says:

I wanted to keep this a secret, but unlike other recent additions, this
will easily take several weeks, maybe months, to show anything.
Assuming I can even pull it off. Nothing technically overwhelming here,
I'm more worried about the near-impossibility of debugging the CPU.
2012-03-18 12:04:22 +11:00
Tim Allen
06e83c6154 Update to v087r02 release.
byuu says:

Changelog:
- extended USART with quit(), readable(), writable() [both emulation and
  hardware]
    - quit() returns true on hardware when Ctrl+C (SIGINT) is generated
      (breaks main loop); no effect under emulation yet (hard to
      simulate)
    - readable() returns true when data is ready to be read
      (non-blocking support for read())
    - writable() returns true when data can be written (non-blocking
      support for write()) [always true under emulation, since we have
      no buffer size limit]
2012-03-10 23:47:19 +11:00
Tim Allen
cbfbec4dc3 Update to v087r01 release.
byuu says:

Changelog:
- fixes ARM core unaligned memory reads (fixes HNMS2 AI, hopefully completely,
  we'll see though) [Cydrak]
- ARM 40000010 writes are now connected to d2 rather than the timer
- ARM bus_readbyte() removed (would love to do the same for writebyte if
  we can ... then we can drop back to bus_read + bus_write only)
- USART with IObit set acts as a regular gamepad now (don't have this
  hooked up with real hardware, but oh well, it's technically possible
  so there's that)
- OpenGL/GLX will use 30-bit when you have a 30-bit display; no need for
  config file video.depth anymore
2012-03-10 23:37:36 +11:00
2740 changed files with 223297 additions and 236736 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
higan/profile/WonderSwan.sys/internal.ram
higan/profile/WonderSwan Color.sys/internal.ram

View File

@@ -1,96 +0,0 @@
include nall/Makefile
nes := nes
snes := snes
gameboy := gameboy
profile := accuracy
target := ui
# options += console
# compiler
c := $(compiler) -std=gnu99
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
flags := -I. -O3 -fomit-frame-pointer
link :=
objects := libco
# profile-guided optimization mode
# pgo := instrument
# pgo := optimize
ifeq ($(pgo),instrument)
flags += -fprofile-generate
link += -lgcov
else ifeq ($(pgo),optimize)
flags += -fprofile-use
endif
# platform
ifeq ($(platform),x)
link += -s -ldl -lX11 -lXext
else ifeq ($(platform),osx)
else ifeq ($(platform),win)
link += $(if $(findstring console,$(options)),-mconsole,-mwindows)
link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
else
unknown_platform: help;
endif
ui := target-$(target)
# implicit rules
compile = \
$(strip \
$(if $(filter %.c,$<), \
$(c) $(flags) $1 -c $< -o $@, \
$(if $(filter %.cpp,$<), \
$(cpp) $(flags) $1 -c $< -o $@ \
) \
) \
)
%.o: $<; $(call compile)
all: build;
obj/libco.o: libco/libco.c libco/*
include $(ui)/Makefile
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
# targets
clean:
-@$(call delete,obj/*.o)
-@$(call delete,obj/*.a)
-@$(call delete,obj/*.so)
-@$(call delete,obj/*.dylib)
-@$(call delete,obj/*.dll)
-@$(call delete,*.res)
-@$(call delete,*.pgd)
-@$(call delete,*.pgc)
-@$(call delete,*.ilk)
-@$(call delete,*.pdb)
-@$(call delete,*.manifest)
sync:
if [ -d ./libco ]; then rm -r ./libco; fi
if [ -d ./nall ]; then rm -r ./nall; fi
if [ -d ./ruby ]; then rm -r ./ruby; fi
if [ -d ./phoenix ]; then rm -r ./phoenix; fi
cp -r ../libco ./libco
cp -r ../nall ./nall
cp -r ../ruby ./ruby
cp -r ../phoenix ./phoenix
rm -r libco/doc
rm -r libco/test
rm -r nall/test
rm -r ruby/_test
rm -r phoenix/nall
rm -r phoenix/test
archive-all:
tar -cjf bsnes.tar.bz2 base data gameboy libco nall nes obj out phoenix ruby snes target-debugger target-libsnes target-ui Makefile cc.bat purge.bat
help:;

View File

@@ -1,129 +0,0 @@
#ifndef BASE_HPP
#define BASE_HPP
const char Version[] = "087";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>
#include <nall/any.hpp>
#include <nall/array.hpp>
#include <nall/dl.hpp>
#include <nall/dsp.hpp>
#include <nall/endian.hpp>
#include <nall/file.hpp>
#include <nall/function.hpp>
#include <nall/moduloarray.hpp>
#include <nall/priorityqueue.hpp>
#include <nall/property.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
#include <nall/varint.hpp>
#include <nall/vector.hpp>
using namespace nall;
//debugging function hook:
//no overhead (and no debugger invocation) if not compiled with -DDEBUGGER
//wraps testing of function to allow invocation without a defined callback
template<typename T> struct hook;
template<typename R, typename... P> struct hook<R (P...)> {
function<R (P...)> callback;
R operator()(P... p) const {
#if defined(DEBUGGER)
if(callback) return callback(std::forward<P>(p)...);
#endif
return R();
}
hook() {}
hook(const hook &hook) { callback = hook.callback; }
hook(void *function) { callback = function; }
hook(R (*function)(P...)) { callback = function; }
template<typename C> hook(R (C::*function)(P...), C *object) { callback = { function, object }; }
template<typename C> hook(R (C::*function)(P...) const, C *object) { callback = { function, object }; }
template<typename L> hook(const L& function) { callback = function; }
hook& operator=(const hook& hook) { callback = hook.callback; return *this; }
};
#if defined(DEBUGGER)
#define privileged public
#else
#define privileged private
#endif
typedef int1_t int1;
typedef int2_t int2;
typedef int3_t int3;
typedef int4_t int4;
typedef int5_t int5;
typedef int6_t int6;
typedef int7_t int7;
typedef int8_t int8;
typedef int9_t int9;
typedef int10_t int10;
typedef int11_t int11;
typedef int12_t int12;
typedef int13_t int13;
typedef int14_t int14;
typedef int15_t int15;
typedef int16_t int16;
typedef int17_t int17;
typedef int18_t int18;
typedef int19_t int19;
typedef int20_t int20;
typedef int21_t int21;
typedef int22_t int22;
typedef int23_t int23;
typedef int24_t int24;
typedef int25_t int25;
typedef int26_t int26;
typedef int27_t int27;
typedef int28_t int28;
typedef int29_t int29;
typedef int30_t int30;
typedef int31_t int31;
typedef int32_t int32;
typedef int64_t int64;
typedef uint1_t uint1;
typedef uint2_t uint2;
typedef uint3_t uint3;
typedef uint4_t uint4;
typedef uint5_t uint5;
typedef uint6_t uint6;
typedef uint7_t uint7;
typedef uint8_t uint8;
typedef uint9_t uint9;
typedef uint10_t uint10;
typedef uint11_t uint11;
typedef uint12_t uint12;
typedef uint13_t uint13;
typedef uint14_t uint14;
typedef uint15_t uint15;
typedef uint16_t uint16;
typedef uint17_t uint17;
typedef uint18_t uint18;
typedef uint19_t uint19;
typedef uint20_t uint20;
typedef uint21_t uint21;
typedef uint22_t uint22;
typedef uint23_t uint23;
typedef uint24_t uint24;
typedef uint25_t uint25;
typedef uint26_t uint26;
typedef uint27_t uint27;
typedef uint28_t uint28;
typedef uint29_t uint29;
typedef uint30_t uint30;
typedef uint31_t uint31;
typedef uint32_t uint32;
typedef uint_t<33> uint33;
typedef uint64_t uint64;
typedef varuint_t varuint;
#endif

View File

@@ -1,2 +0,0 @@
@mingw32-make -j 8
@pause

View File

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

View File

@@ -1,8 +0,0 @@
[Desktop Entry]
Name=bsnes
Comment=SNES emulator
Exec=bsnes
Icon=bsnes
Terminal=false
Type=Application
Categories=Game;Emulator;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,108 +0,0 @@
#ifdef APU_CPP
bool APU::Noise::dac_enable() {
return (envelope_volume || envelope_direction);
}
void APU::Noise::run() {
if(period && --period == 0) {
period = divisor << frequency;
if(frequency < 14) {
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
lfsr = (lfsr >> 1) ^ (bit << (narrow_lfsr ? 6 : 14));
}
}
uint4 sample = (lfsr & 1) ? (uint4)0 : volume;
if(enable == false) sample = 0;
output = sample;
}
void APU::Noise::clock_length() {
if(counter && length) {
if(--length == 0) enable = false;
}
}
void APU::Noise::clock_envelope() {
if(enable && envelope_frequency && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
}
void APU::Noise::write(unsigned r, uint8 data) {
if(r == 1) { //$ff20 NR41
length = 64 - (data & 0x3f);
}
if(r == 2) { //$ff21 NR42
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
if(dac_enable() == false) enable = false;
}
if(r == 3) { //$ff22 NR43
frequency = data >> 4;
narrow_lfsr = data & 0x08;
divisor = (data & 0x07) << 4;
if(divisor == 0) divisor = 8;
period = divisor << frequency;
}
if(r == 4) { //$ff34 NR44
bool initialize = data & 0x80;
counter = data & 0x40;
if(initialize) {
enable = dac_enable();
lfsr = ~0U;
envelope_period = envelope_frequency;
volume = envelope_volume;
if(length == 0) length = 64;
}
}
}
void APU::Noise::power() {
enable = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
frequency = 0;
narrow_lfsr = 0;
divisor = 0;
counter = 0;
output = 0;
length = 0;
envelope_period = 0;
volume = 0;
period = 0;
lfsr = 0;
}
void APU::Noise::serialize(serializer &s) {
s.integer(enable);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(narrow_lfsr);
s.integer(divisor);
s.integer(counter);
s.integer(output);
s.integer(length);
s.integer(envelope_period);
s.integer(volume);
s.integer(period);
s.integer(lfsr);
}
#endif

View File

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

View File

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

View File

@@ -1,160 +0,0 @@
#ifdef APU_CPP
bool APU::Square1::dac_enable() {
return (envelope_volume || envelope_direction);
}
void APU::Square1::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
phase++;
switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_
case 1: duty_output = (phase >= 6); break; //______--
case 2: duty_output = (phase >= 4); break; //____----
case 3: duty_output = (phase <= 5); break; //------__
}
}
uint4 sample = (duty_output ? volume : (uint4)0);
if(enable == false) sample = 0;
output = sample;
}
void APU::Square1::sweep(bool update) {
if(sweep_enable == false) return;
sweep_negate = sweep_direction;
unsigned delta = frequency_shadow >> sweep_shift;
signed freq = frequency_shadow + (sweep_negate ? -delta : delta);
if(freq > 2047) {
enable = false;
} else if(sweep_shift && update) {
frequency_shadow = freq;
frequency = freq & 2047;
period = 4 * (2048 - frequency);
}
}
void APU::Square1::clock_length() {
if(counter && length) {
if(--length == 0) enable = false;
}
}
void APU::Square1::clock_sweep() {
if(enable && sweep_frequency && --sweep_period == 0) {
sweep_period = sweep_frequency;
sweep(1);
sweep(0);
}
}
void APU::Square1::clock_envelope() {
if(enable && envelope_frequency && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
}
void APU::Square1::write(unsigned r, uint8 data) {
if(r == 0) { //$ff10 NR10
if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false;
sweep_frequency = (data >> 4) & 7;
sweep_direction = data & 0x08;
sweep_shift = data & 0x07;
}
if(r == 1) { //$ff11 NR11
duty = data >> 6;
length = 64 - (data & 0x3f);
}
if(r == 2) { //$ff12 NR12
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
if(dac_enable() == false) enable = false;
}
if(r == 3) { //$ff13 NR13
frequency = (frequency & 0x0700) | data;
}
if(r == 4) { //$ff14 NR14
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
enable = dac_enable();
envelope_period = envelope_frequency;
volume = envelope_volume;
frequency_shadow = frequency;
sweep_period = sweep_frequency;
sweep_enable = sweep_period || sweep_shift;
sweep_negate = false;
if(sweep_shift) sweep(0);
if(length == 0) length = 64;
}
}
period = 4 * (2048 - frequency);
}
void APU::Square1::power() {
enable = 0;
sweep_frequency = 0;
sweep_direction = 0;
sweep_shift = 0;
sweep_negate = 0;
duty = 0;
length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
frequency = 0;
counter = 0;
output = 0;
duty_output = 0;
phase = 0;
period = 0;
envelope_period = 0;
sweep_period = 0;
frequency_shadow = 0;
sweep_enable = 0;
volume = 0;
}
void APU::Square1::serialize(serializer &s) {
s.integer(enable);
s.integer(sweep_frequency);
s.integer(sweep_direction);
s.integer(sweep_shift);
s.integer(sweep_negate);
s.integer(duty);
s.integer(length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(counter);
s.integer(output);
s.integer(duty_output);
s.integer(phase);
s.integer(period);
s.integer(envelope_period);
s.integer(sweep_period);
s.integer(frequency_shadow);
s.integer(sweep_enable);
s.integer(volume);
}
#endif

View File

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

View File

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

View File

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

View File

@@ -1,99 +0,0 @@
#ifdef APU_CPP
void APU::Wave::run() {
if(period && --period == 0) {
period = 2 * (2048 - frequency);
pattern_sample = pattern[++pattern_offset];
}
uint4 sample = pattern_sample >> volume_shift;
if(enable == false) sample = 0;
output = sample;
}
void APU::Wave::clock_length() {
if(counter && length) {
if(--length == 0) enable = false;
}
}
void APU::Wave::write(unsigned r, uint8 data) {
if(r == 0) { //$ff1a NR30
dac_enable = data & 0x80;
if(dac_enable == false) enable = false;
}
if(r == 1) { //$ff1b NR31
length = 256 - data;
}
if(r == 2) { //$ff1c NR32
switch((data >> 5) & 3) {
case 0: volume_shift = 4; break; // 0%
case 1: volume_shift = 0; break; //100%
case 2: volume_shift = 1; break; // 50%
case 3: volume_shift = 2; break; // 25%
}
}
if(r == 3) { //$ff1d NR33
frequency = (frequency & 0x0700) | data;
}
if(r == 4) { //$ff1e NR34
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
enable = dac_enable;
pattern_offset = 0;
if(length == 0) length = 256;
}
}
period = 2 * (2048 - frequency);
}
void APU::Wave::write_pattern(unsigned p, uint8 data) {
p <<= 1;
pattern[p + 0] = (data >> 4) & 15;
pattern[p + 1] = (data >> 0) & 15;
}
void APU::Wave::power() {
enable = 0;
dac_enable = 0;
volume_shift = 0;
frequency = 0;
counter = 0;
random_lfsr r;
for(auto &n : pattern) n = r() & 15;
output = 0;
length = 0;
period = 0;
pattern_offset = 0;
pattern_sample = 0;
}
void APU::Wave::serialize(serializer &s) {
s.integer(enable);
s.integer(dac_enable);
s.integer(volume_shift);
s.integer(frequency);
s.integer(counter);
s.array(pattern);
s.integer(output);
s.integer(length);
s.integer(period);
s.integer(pattern_offset);
s.integer(pattern_sample);
}
#endif

View File

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

View File

@@ -1,153 +0,0 @@
#include <gameboy/gameboy.hpp>
#include <nall/crc32.hpp>
#define CARTRIDGE_CPP
namespace GameBoy {
#include "mbc0/mbc0.cpp"
#include "mbc1/mbc1.cpp"
#include "mbc2/mbc2.cpp"
#include "mbc3/mbc3.cpp"
#include "mbc5/mbc5.cpp"
#include "mmm01/mmm01.cpp"
#include "huc1/huc1.cpp"
#include "huc3/huc3.cpp"
#include "serialization.cpp"
Cartridge cartridge;
void Cartridge::load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size) {
if(size == 0) size = 32768;
romdata = allocate<uint8>(romsize = size, 0xff);
if(data) memcpy(romdata, data, size);
info.mapper = Mapper::Unknown;
info.ram = false;
info.battery = false;
info.rtc = false;
info.rumble = false;
info.romsize = 0;
info.ramsize = 0;
XML::Document document(markup);
auto &mapperid = document["cartridge"]["mapper"].data;
if(mapperid == "none" ) info.mapper = Mapper::MBC0;
if(mapperid == "MBC1" ) info.mapper = Mapper::MBC1;
if(mapperid == "MBC2" ) info.mapper = Mapper::MBC2;
if(mapperid == "MBC3" ) info.mapper = Mapper::MBC3;
if(mapperid == "MBC5" ) info.mapper = Mapper::MBC5;
if(mapperid == "MMM01") info.mapper = Mapper::MMM01;
if(mapperid == "HuC1" ) info.mapper = Mapper::HuC1;
if(mapperid == "HuC3" ) info.mapper = Mapper::HuC3;
info.rtc = document["cartridge"]["rtc"].data == "true";
info.rumble = document["cartridge"]["rumble"].data == "true";
info.romsize = numeral(document["cartridge"]["rom"]["size"].data);
info.ramsize = numeral(document["cartridge"]["ram"]["size"].data);
info.battery = document["cartridge"]["ram"]["battery"].data == "true";
switch(info.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
case Mapper::MBC1: mapper = &mbc1; break;
case Mapper::MBC2: mapper = &mbc2; break;
case Mapper::MBC3: mapper = &mbc3; break;
case Mapper::MBC5: mapper = &mbc5; break;
case Mapper::MMM01: mapper = &mmm01; break;
case Mapper::HuC1: mapper = &huc1; break;
case Mapper::HuC3: mapper = &huc3; break;
}
ramdata = new uint8_t[ramsize = info.ramsize]();
system.load(revision);
loaded = true;
sha256 = nall::sha256(romdata, romsize);
}
void Cartridge::unload() {
if(loaded == false) return;
if(romdata) { delete[] romdata; romdata = 0; }
if(ramdata) { delete[] ramdata; ramdata = 0; }
loaded = false;
}
uint8 Cartridge::rom_read(unsigned addr) {
if(addr >= romsize) addr %= romsize;
return romdata[addr];
}
void Cartridge::rom_write(unsigned addr, uint8 data) {
if(addr >= romsize) addr %= romsize;
romdata[addr] = data;
}
uint8 Cartridge::ram_read(unsigned addr) {
if(ramsize == 0) return 0x00;
if(addr >= ramsize) addr %= ramsize;
return ramdata[addr];
}
void Cartridge::ram_write(unsigned addr, uint8 data) {
if(ramsize == 0) return;
if(addr >= ramsize) addr %= ramsize;
ramdata[addr] = data;
}
uint8 Cartridge::mmio_read(uint16 addr) {
if(addr == 0xff50) return 0x00;
if(bootrom_enable) {
const uint8 *data = nullptr;
switch(system.revision()) { default:
case System::Revision::GameBoy: data = System::BootROM::dmg; break;
case System::Revision::SuperGameBoy: data = System::BootROM::sgb; break;
case System::Revision::GameBoyColor: data = System::BootROM::cgb; break;
}
if(addr >= 0x0000 && addr <= 0x00ff) return data[addr];
if(addr >= 0x0200 && addr <= 0x08ff && system.cgb()) return data[addr - 256];
}
return mapper->mmio_read(addr);
}
void Cartridge::mmio_write(uint16 addr, uint8 data) {
if(bootrom_enable && addr == 0xff50) {
bootrom_enable = false;
return;
}
mapper->mmio_write(addr, data);
}
void Cartridge::power() {
bootrom_enable = true;
mbc0.power();
mbc1.power();
mbc2.power();
mbc3.power();
mbc5.power();
mmm01.power();
huc1.power();
huc3.power();
for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this;
for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this;
bus.mmio[0xff50] = this;
}
Cartridge::Cartridge() {
loaded = false;
romdata = 0;
ramdata = 0;
}
Cartridge::~Cartridge() {
unload();
}
}

View File

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

View File

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

View File

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

View File

@@ -1,25 +0,0 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC0::mmio_read(uint16 addr) {
if((addr & 0x8000) == 0x0000) { //$0000-7fff
return cartridge.rom_read(addr);
}
if((addr & 0xe000) == 0xa000) { //$a000-bfff
return cartridge.ram_read(addr & 0x1fff);
}
return 0x00;
}
void Cartridge::MBC0::mmio_write(uint16 addr, uint8 data) {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
cartridge.ram_write(addr & 0x1fff, data);
return;
}
}
void Cartridge::MBC0::power() {
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,91 +0,0 @@
#include <gameboy/gameboy.hpp>
namespace GameBoy {
Cheat cheat;
bool Cheat::decode(const string &code_, unsigned &addr, unsigned &data, unsigned &comp) {
static bool initialize = false;
static uint8 mapProActionReplay[256], mapGameGenie[256];
if(initialize == false) {
initialize = true;
for(auto &n : mapProActionReplay) n = ~0;
mapProActionReplay['0'] = 0; mapProActionReplay['1'] = 1; mapProActionReplay['2'] = 2; mapProActionReplay['3'] = 3;
mapProActionReplay['4'] = 4; mapProActionReplay['5'] = 5; mapProActionReplay['6'] = 6; mapProActionReplay['7'] = 7;
mapProActionReplay['8'] = 8; mapProActionReplay['9'] = 9; mapProActionReplay['A'] = 10; mapProActionReplay['B'] = 11;
mapProActionReplay['C'] = 12; mapProActionReplay['D'] = 13; mapProActionReplay['E'] = 14; mapProActionReplay['F'] = 15;
for(auto &n : mapGameGenie) n = ~0;
mapGameGenie['0'] = 0; mapGameGenie['1'] = 1; mapGameGenie['2'] = 2; mapGameGenie['3'] = 3;
mapGameGenie['4'] = 4; mapGameGenie['5'] = 5; mapGameGenie['6'] = 6; mapGameGenie['7'] = 7;
mapGameGenie['8'] = 8; mapGameGenie['9'] = 9; mapGameGenie['A'] = 10; mapGameGenie['B'] = 11;
mapGameGenie['C'] = 12; mapGameGenie['D'] = 13; mapGameGenie['E'] = 14; mapGameGenie['F'] = 15;
}
string code = code_;
code.upper();
unsigned length = code.length(), bits = 0;
if(code.wildcard("????:??")) {
code = { substr(code, 0, 4), substr(code, 5, 2) };
for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false;
bits = hex(code);
addr = (bits >> 8) & 0xffff;
data = (bits >> 0) & 0xff;
comp = ~0;
return true;
}
if(code.wildcard("????:??:??")) {
code = { substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2) };
for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false;
bits = hex(code);
addr = (bits >> 16) & 0xffff;
data = (bits >> 8) & 0xff;
comp = (bits >> 0) & 0xff;
return true;
}
if(code.wildcard("???" "-" "???")) {
code = { substr(code, 0, 3), substr(code, 4, 3) };
for(unsigned n = 0; n < 6; n++) if(mapGameGenie[code[n]] > 15) return false;
for(unsigned n = 0; n < 6; n++) bits |= mapGameGenie[code[n]] << (20 - n * 4);
addr = (bits >> 0) & 0xffff;
data = (bits >> 16) & 0xff;
comp = ~0;
addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000;
return true;
}
if(code.wildcard("???" "-" "???" "-" "???")) {
code = { substr(code, 0, 3), substr(code, 4, 3), substr(code, 8, 1), substr(code, 10, 1) };
for(unsigned n = 0; n < 8; n++) if(mapGameGenie[code[n]] > 15) return false;
for(unsigned n = 0; n < 8; n++) bits |= mapGameGenie[code[n]] << (28 - n * 4);
addr = (bits >> 8) & 0xffff;
data = (bits >> 24) & 0xff;
comp = (bits >> 0) & 0xff;
addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000;
comp = (((comp >> 2) | (comp << 6)) & 0xff) ^ 0xba;
return true;
}
return false;
}
void Cheat::synchronize() {
for(auto &n : override) n = false;
for(unsigned n = 0; n < size(); n++) {
override[operator[](n).addr] = true;
}
}
}

View File

@@ -1,14 +0,0 @@
struct CheatCode {
unsigned addr;
unsigned data;
unsigned comp;
};
struct Cheat : public linear_vector<CheatCode> {
static bool decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp);
void synchronize();
bool override[65536];
};
extern Cheat cheat;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,202 +0,0 @@
#include <gameboy/gameboy.hpp>
#define CPU_CPP
namespace GameBoy {
#include "core/core.cpp"
#include "mmio/mmio.cpp"
#include "timing/timing.cpp"
#include "serialization.cpp"
CPU cpu;
void CPU::Main() {
cpu.main();
}
void CPU::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(trace) print(disassemble(r[PC]), "\n");
interrupt_test();
uint8 opcode = op_read(r[PC]++);
(this->*opcode_table[opcode])();
}
}
void CPU::interrupt_raise(CPU::Interrupt id) {
if(id == Interrupt::Vblank) {
status.interrupt_request_vblank = 1;
if(status.interrupt_enable_vblank) status.halt = false;
}
if(id == Interrupt::Stat) {
status.interrupt_request_stat = 1;
if(status.interrupt_enable_stat) status.halt = false;
}
if(id == Interrupt::Timer) {
status.interrupt_request_timer = 1;
if(status.interrupt_enable_timer) status.halt = false;
}
if(id == Interrupt::Serial) {
status.interrupt_request_serial = 1;
if(status.interrupt_enable_serial) status.halt = false;
}
if(id == Interrupt::Joypad) {
status.interrupt_request_joypad = 1;
if(status.interrupt_enable_joypad) status.halt = status.stop = false;
}
}
void CPU::interrupt_test() {
if(status.ime) {
if(status.interrupt_request_vblank && status.interrupt_enable_vblank) {
status.interrupt_request_vblank = 0;
return interrupt_exec(0x0040);
}
if(status.interrupt_request_stat && status.interrupt_enable_stat) {
status.interrupt_request_stat = 0;
return interrupt_exec(0x0048);
}
if(status.interrupt_request_timer && status.interrupt_enable_timer) {
status.interrupt_request_timer = 0;
return interrupt_exec(0x0050);
}
if(status.interrupt_request_serial && status.interrupt_enable_serial) {
status.interrupt_request_serial = 0;
return interrupt_exec(0x0058);
}
if(status.interrupt_request_joypad && status.interrupt_enable_joypad) {
status.interrupt_request_joypad = 0;
return interrupt_exec(0x0060);
}
}
}
void CPU::interrupt_exec(uint16 pc) {
status.ime = 0;
op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0);
r[PC] = pc;
op_io();
op_io();
op_io();
}
void CPU::power() {
create(Main, 4 * 1024 * 1024);
for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
for(unsigned n = 0xff80; n <= 0xfffe; n++) bus.mmio[n] = this; //HRAM
bus.mmio[0xff00] = this; //JOYP
bus.mmio[0xff01] = this; //SB
bus.mmio[0xff02] = this; //SC
bus.mmio[0xff04] = this; //DIV
bus.mmio[0xff05] = this; //TIMA
bus.mmio[0xff06] = this; //TMA
bus.mmio[0xff07] = this; //TAC
bus.mmio[0xff0f] = this; //IF
bus.mmio[0xff46] = this; //DMA
bus.mmio[0xffff] = this; //IE
if(system.cgb()) {
bus.mmio[0xff4d] = this; //KEY1
bus.mmio[0xff51] = this; //HDMA1
bus.mmio[0xff52] = this; //HDMA2
bus.mmio[0xff53] = this; //HDMA3
bus.mmio[0xff54] = this; //HDMA4
bus.mmio[0xff55] = this; //HDMA5
bus.mmio[0xff56] = this; //RP
bus.mmio[0xff6c] = this; //???
bus.mmio[0xff70] = this; //SVBK
bus.mmio[0xff72] = this; //???
bus.mmio[0xff73] = this; //???
bus.mmio[0xff74] = this; //???
bus.mmio[0xff75] = this; //???
bus.mmio[0xff76] = this; //???
bus.mmio[0xff77] = this; //???
}
for(auto &n : wram) n = 0x00;
for(auto &n : hram) n = 0x00;
r[PC] = 0x0000;
r[SP] = 0x0000;
r[AF] = 0x0000;
r[BC] = 0x0000;
r[DE] = 0x0000;
r[HL] = 0x0000;
status.clock = 0;
status.halt = false;
status.stop = false;
status.ei = false;
status.ime = 0;
status.p15 = 0;
status.p14 = 0;
status.joyp = 0;
status.mlt_req = 0;
status.serial_data = 0;
status.serial_bits = 0;
status.serial_transfer = 0;
status.serial_clock = 0;
status.div = 0;
status.tima = 0;
status.tma = 0;
status.timer_enable = 0;
status.timer_clock = 0;
status.interrupt_request_joypad = 0;
status.interrupt_request_serial = 0;
status.interrupt_request_timer = 0;
status.interrupt_request_stat = 0;
status.interrupt_request_vblank = 0;
status.speed_double = 0;
status.speed_switch = 0;
status.dma_source = 0;
status.dma_target = 0;
status.dma_mode = 0;
status.dma_length = 0;
status.ff6c = 0;
status.ff72 = 0;
status.ff73 = 0;
status.ff74 = 0;
status.ff75 = 0;
status.wram_bank = 1;
status.interrupt_enable_joypad = 0;
status.interrupt_enable_serial = 0;
status.interrupt_enable_timer = 0;
status.interrupt_enable_stat = 0;
status.interrupt_enable_vblank = 0;
}
CPU::CPU() : trace(false) {
initialize_opcode_table();
}
}

View File

@@ -1,105 +0,0 @@
struct CPU : Processor, MMIO {
#include "core/core.hpp"
#include "mmio/mmio.hpp"
#include "timing/timing.hpp"
bool trace;
enum class Interrupt : unsigned {
Vblank,
Stat,
Timer,
Serial,
Joypad,
};
struct Status {
unsigned clock;
bool halt;
bool stop;
bool ei;
bool ime;
//$ff00 JOYP
bool p15;
bool p14;
uint8 joyp;
uint8 mlt_req;
//$ff01 SB
uint8 serial_data;
unsigned serial_bits;
//$ff02 SC
bool serial_transfer;
bool serial_clock;
//$ff04 DIV
uint8 div;
//$ff05 TIMA
uint8 tima;
//$ff06 TMA
uint8 tma;
//$ff07 TAC
bool timer_enable;
unsigned timer_clock;
//$ff0f IF
bool interrupt_request_joypad;
bool interrupt_request_serial;
bool interrupt_request_timer;
bool interrupt_request_stat;
bool interrupt_request_vblank;
//$ff4d KEY1
bool speed_double;
bool speed_switch;
//$ff51,$ff52 HDMA1,HDMA2
uint16 dma_source;
//$ff53,$ff54 HDMA3,HDMA4
uint16 dma_target;
//$ff55 HDMA5
bool dma_mode;
uint16 dma_length;
//$ff6c ???
uint8 ff6c;
//$ff70 SVBK
uint3 wram_bank;
//$ff72-$ff75 ???
uint8 ff72;
uint8 ff73;
uint8 ff74;
uint8 ff75;
//$ffff IE
bool interrupt_enable_joypad;
bool interrupt_enable_serial;
bool interrupt_enable_timer;
bool interrupt_enable_stat;
bool interrupt_enable_vblank;
} status;
uint8 wram[32768]; //GB=8192, GBC=32768
uint8 hram[128];
static void Main();
void main();
void interrupt_raise(Interrupt id);
void interrupt_test();
void interrupt_exec(uint16 pc);
void power();
void serialize(serializer&);
CPU();
};
extern CPU cpu;

View File

@@ -1,271 +0,0 @@
#ifdef CPU_CPP
unsigned CPU::wram_addr(uint16 addr) const {
addr &= 0x1fff;
if(addr < 0x1000) return addr;
auto bank = status.wram_bank + (status.wram_bank == 0);
return (bank * 0x1000) + (addr & 0x0fff);
}
void CPU::mmio_joyp_poll() {
unsigned button = 0, dpad = 0;
button |= interface->inputPoll((unsigned)Input::Start) << 3;
button |= interface->inputPoll((unsigned)Input::Select) << 2;
button |= interface->inputPoll((unsigned)Input::B) << 1;
button |= interface->inputPoll((unsigned)Input::A) << 0;
dpad |= interface->inputPoll((unsigned)Input::Down) << 3;
dpad |= interface->inputPoll((unsigned)Input::Up) << 2;
dpad |= interface->inputPoll((unsigned)Input::Left) << 1;
dpad |= interface->inputPoll((unsigned)Input::Right) << 0;
status.joyp = 0x0f;
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
if(status.p15 == 0) status.joyp &= button ^ 0x0f;
if(status.p14 == 0) status.joyp &= dpad ^ 0x0f;
if(status.joyp != 0x0f) interrupt_raise(Interrupt::Joypad);
}
uint8 CPU::mmio_read(uint16 addr) {
if(addr >= 0xc000 && addr <= 0xfdff) return wram[wram_addr(addr)];
if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f];
if(addr == 0xff00) { //JOYP
return (status.p15 << 5)
| (status.p14 << 4)
| (status.joyp << 0);
}
if(addr == 0xff01) { //SB
return 0xff;
}
if(addr == 0xff02) { //SC
return (status.serial_transfer << 7)
| (status.serial_clock << 0);
}
if(addr == 0xff04) { //DIV
return status.div;
}
if(addr == 0xff05) { //TIMA
return status.tima;
}
if(addr == 0xff06) { //TMA
return status.tma;
}
if(addr == 0xff07) { //TAC
return (status.timer_enable << 2)
| (status.timer_clock << 0);
}
if(addr == 0xff0f) { //IF
return (status.interrupt_request_joypad << 4)
| (status.interrupt_request_serial << 3)
| (status.interrupt_request_timer << 2)
| (status.interrupt_request_stat << 1)
| (status.interrupt_request_vblank << 0);
}
if(addr == 0xff4d) { //KEY1
return (status.speed_double << 7);
}
if(addr == 0xff55) { //HDMA5
return (status.dma_length / 16) - 1;
}
if(addr == 0xff56) { //RP
return 0x02;
}
if(addr == 0xff6c) { //???
return 0xfe | status.ff6c;
}
if(addr == 0xff70) { //SVBK
return status.wram_bank;
}
if(addr == 0xff72) { //???
return status.ff72;
}
if(addr == 0xff73) { //???
return status.ff73;
}
if(addr == 0xff74) { //???
return status.ff74;
}
if(addr == 0xff75) { //???
return 0x8f | status.ff75;
}
if(addr == 0xff76) { //???
return 0x00;
}
if(addr == 0xff77) { //???
return 0x00;
}
if(addr == 0xffff) { //IE
return (status.interrupt_enable_joypad << 4)
| (status.interrupt_enable_serial << 3)
| (status.interrupt_enable_timer << 2)
| (status.interrupt_enable_stat << 1)
| (status.interrupt_enable_vblank << 0);
}
return 0x00;
}
void CPU::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0xc000 && addr <= 0xfdff) { wram[wram_addr(addr)] = data; return; }
if(addr >= 0xff80 && addr <= 0xfffe) { hram[addr & 0x7f] = data; return; }
if(addr == 0xff00) { //JOYP
status.p15 = data & 0x20;
status.p14 = data & 0x10;
interface->joypWrite(status.p15, status.p14);
mmio_joyp_poll();
return;
}
if(addr == 0xff01) { //SB
status.serial_data = data;
return;
}
if(addr == 0xff02) { //SC
status.serial_transfer = data & 0x80;
status.serial_clock = data & 0x01;
if(status.serial_transfer) status.serial_bits = 8;
return;
}
if(addr == 0xff04) { //DIV
status.div = 0;
return;
}
if(addr == 0xff05) { //TIMA
status.tima = data;
return;
}
if(addr == 0xff06) { //TMA
status.tma = data;
return;
}
if(addr == 0xff07) { //TAC
status.timer_enable = data & 0x04;
status.timer_clock = data & 0x03;
return;
}
if(addr == 0xff0f) { //IF
status.interrupt_request_joypad = data & 0x10;
status.interrupt_request_serial = data & 0x08;
status.interrupt_request_timer = data & 0x04;
status.interrupt_request_stat = data & 0x02;
status.interrupt_request_vblank = data & 0x01;
return;
}
if(addr == 0xff46) { //DMA
for(unsigned n = 0x00; n <= 0x9f; n++) {
bus.write(0xfe00 + n, bus.read((data << 8) + n));
add_clocks(4);
}
return;
}
if(addr == 0xff4d) { //KEY1
status.speed_switch = data & 0x01;
return;
}
if(addr == 0xff51) { //HDMA1
status.dma_source = (status.dma_source & 0x00ff) | (data << 8);
return;
}
if(addr == 0xff52) { //HDMA2
status.dma_source = (status.dma_source & 0xff00) | (data << 0);
return;
}
if(addr == 0xff53) { //HDMA3
status.dma_target = (status.dma_target & 0x00ff) | (data << 8);
return;
}
if(addr == 0xff54) { //HDMA4
status.dma_target = (status.dma_target & 0xff00) | (data << 0);
return;
}
if(addr == 0xff55) { //HDMA5
status.dma_mode = data & 0x80;
status.dma_length = ((data & 0x7f) + 1) * 16;
if(status.dma_mode == 0) do {
bus.write(status.dma_target++, bus.read(status.dma_source++));
add_clocks(4 << status.speed_double);
} while(--status.dma_length);
return;
}
if(addr == 0xff56) { //RP
return;
}
if(addr == 0xff6c) { //???
status.ff6c = data & 0x01;
return;
}
if(addr == 0xff72) { //???
status.ff72 = data;
return;
}
if(addr == 0xff73) { //???
status.ff73 = data;
return;
}
if(addr == 0xff74) { //???
status.ff74 = data;
return;
}
if(addr == 0xff75) { //???
status.ff75 = data & 0x70;
return;
}
if(addr == 0xff70) { //SVBK
status.wram_bank = data & 0x07;
return;
}
if(addr == 0xffff) { //IE
status.interrupt_enable_joypad = data & 0x10;
status.interrupt_enable_serial = data & 0x08;
status.interrupt_enable_timer = data & 0x04;
status.interrupt_enable_stat = data & 0x02;
status.interrupt_enable_vblank = data & 0x01;
return;
}
}
#endif

View File

@@ -1,4 +0,0 @@
unsigned wram_addr(uint16 addr) const;
void mmio_joyp_poll();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,60 +0,0 @@
#ifndef GAMEBOY_HPP
#define GAMEBOY_HPP
#include <base/base.hpp>
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static const unsigned SerializerVersion = 3;
}
}
/*
bgameboy - Game Boy, Super Game Boy, and Game Boy Color emulator
author: byuu
license: GPLv3
project started: 2010-12-27
*/
#include <libco/libco.h>
#include <nall/gameboy/cartridge.hpp>
namespace GameBoy {
struct Processor {
cothread_t thread;
unsigned frequency;
int64 clock;
inline void create(void (*entrypoint)(), unsigned frequency) {
if(thread) co_delete(thread);
thread = co_create(65536 * sizeof(void*), entrypoint);
this->frequency = frequency;
clock = 0;
}
inline void serialize(serializer &s) {
s.integer(frequency);
s.integer(clock);
}
inline Processor() : thread(nullptr) {
}
inline ~Processor() {
if(thread) co_delete(thread);
}
};
#include <gameboy/memory/memory.hpp>
#include <gameboy/system/system.hpp>
#include <gameboy/scheduler/scheduler.hpp>
#include <gameboy/cartridge/cartridge.hpp>
#include <gameboy/cpu/cpu.hpp>
#include <gameboy/apu/apu.hpp>
#include <gameboy/lcd/lcd.hpp>
#include <gameboy/cheat/cheat.hpp>
#include <gameboy/video/video.hpp>
};
#endif

View File

@@ -1,27 +0,0 @@
#include <gameboy/gameboy.hpp>
namespace GameBoy {
Interface *interface = nullptr;
void Interface::lcdScanline() {
}
void Interface::joypWrite(bool p15, bool p14) {
}
void Interface::videoRefresh(const uint16_t *data) {
}
void Interface::audioSample(int16_t center, int16_t left, int16_t right) {
}
bool Interface::inputPoll(unsigned id) {
return false;
}
void Interface::message(const string &text) {
print(text, "\n");
}
}

View File

@@ -1,12 +0,0 @@
struct Interface {
virtual void lcdScanline();
virtual void joypWrite(bool p15, bool p14);
virtual void videoRefresh(const uint16_t *data);
virtual void audioSample(int16_t center, int16_t left, int16_t right);
virtual bool inputPoll(unsigned id);
virtual void message(const string &text);
};
extern Interface *interface;

View File

@@ -1,185 +0,0 @@
#ifdef LCD_CPP
void LCD::cgb_render() {
for(unsigned n = 0; n < 160; n++) {
line[n] = 0x7fff;
origin[n] = Origin::None;
}
if(status.display_enable) {
cgb_render_bg();
if(status.window_display_enable) cgb_render_window();
if(status.ob_enable) cgb_render_ob();
}
uint16 *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
interface->lcdScanline();
}
//Attributes:
//0x80: 0 = OAM priority, 1 = BG priority
//0x40: vertical flip
//0x20: horizontal flip
//0x08: VRAM bank#
//0x07: palette#
void LCD::cgb_read_tile(bool select, unsigned x, unsigned y, unsigned &tile, unsigned &attr, unsigned &data) {
unsigned tmaddr = 0x1800 + (select << 10);
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
tile = vram[0x0000 + tmaddr];
attr = vram[0x2000 + tmaddr];
unsigned tdaddr = attr & 0x08 ? 0x2000 : 0x0000;
if(status.bg_tiledata_select == 0) {
tdaddr += 0x1000 + ((int8)tile << 4);
} else {
tdaddr += 0x0000 + (tile << 4);
}
y &= 7;
if(attr & 0x40) y ^= 7;
tdaddr += y << 1;
data = vram[tdaddr++] << 0;
data |= vram[tdaddr++] << 8;
if(attr & 0x20) data = hflip(data);
}
void LCD::cgb_render_bg() {
unsigned iy = (status.ly + status.scy) & 255;
unsigned ix = status.scx, tx = ix & 7;
unsigned tile, attr, data;
cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data);
for(unsigned ox = 0; ox < 160; ox++) {
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
unsigned palette = 0;
palette |= bgpd[palette_index++] << 0;
palette |= bgpd[palette_index++] << 8;
palette &= 0x7fff;
line[ox] = palette;
origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG);
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data);
}
}
void LCD::cgb_render_window() {
if(status.ly - status.wy >= 144u) return;
if(status.wx >= 167u) return;
unsigned iy = status.wyc++;
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
unsigned tile, attr, data;
cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data);
for(unsigned ox = 0; ox < 160; ox++) {
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
unsigned palette = 0;
palette |= bgpd[palette_index++] << 0;
palette |= bgpd[palette_index++] << 8;
palette &= 0x7fff;
if(ox - (status.wx - 7) < 160u) {
line[ox] = palette;
origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG);
}
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data);
}
}
//Attributes:
//0x80: 0 = OBJ above BG, 1 = BG above OBJ
//0x40: vertical flip
//0x20: horizontal flip
//0x08: VRAM bank#
//0x07: palette#
void LCD::cgb_render_ob() {
const unsigned Height = (status.ob_size == 0 ? 8 : 16);
unsigned sprite[10], sprites = 0;
//find first ten sprites on this scanline
for(unsigned s = 0; s < 40; s++) {
unsigned sy = oam[(s << 2) + 0] - 16;
unsigned sx = oam[(s << 2) + 1] - 8;
sy = status.ly - sy;
if(sy >= Height) continue;
sprite[sprites++] = s;
if(sprites == 10) break;
}
//sort by X-coordinate, when equal, lower address comes first
for(unsigned x = 0; x < sprites; x++) {
for(unsigned y = x + 1; y < sprites; y++) {
signed sx = oam[(sprite[x] << 2) + 1] - 8;
signed sy = oam[(sprite[y] << 2) + 1] - 8;
if(sy < sx) {
sprite[x] ^= sprite[y];
sprite[y] ^= sprite[x];
sprite[x] ^= sprite[y];
}
}
}
//render backwards, so that first sprite has highest priority
for(signed s = sprites - 1; s >= 0; s--) {
unsigned n = sprite[s] << 2;
unsigned sy = oam[n + 0] - 16;
unsigned sx = oam[n + 1] - 8;
unsigned tile = oam[n + 2] & ~status.ob_size;
unsigned attr = oam[n + 3];
sy = status.ly - sy;
if(sy >= Height) continue;
if(attr & 0x40) sy ^= (Height - 1);
unsigned tdaddr = (attr & 0x08 ? 0x2000 : 0x0000) + (tile << 4) + (sy << 1), data = 0;
data |= vram[tdaddr++] << 0;
data |= vram[tdaddr++] << 8;
if(attr & 0x20) data = hflip(data);
for(unsigned tx = 0; tx < 8; tx++) {
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(index == 0) continue;
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
unsigned palette = 0;
palette |= obpd[palette_index++] << 0;
palette |= obpd[palette_index++] << 8;
palette &= 0x7fff;
unsigned ox = sx + tx;
if(ox < 160) {
//When LCDC.D0 (BG enable) is off, OB is always rendered above BG+Window
if(status.bg_enable) {
if(origin[ox] == Origin::BGP) continue;
if(attr & 0x80) {
if(origin[ox] == Origin::BG || origin[ox] == Origin::BGP) {
if(line[ox] > 0) continue;
}
}
}
line[ox] = palette;
origin[ox] = Origin::OB;
}
}
}
}
#endif

View File

@@ -1,145 +0,0 @@
#ifdef LCD_CPP
void LCD::dmg_render() {
for(unsigned n = 0; n < 160; n++) {
line[n] = 0x00;
origin[n] = Origin::None;
}
if(status.display_enable) {
if(status.bg_enable) dmg_render_bg();
if(status.window_display_enable) dmg_render_window();
if(status.ob_enable) dmg_render_ob();
}
uint16 *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
interface->lcdScanline();
}
uint16 LCD::dmg_read_tile(bool select, unsigned x, unsigned y) {
unsigned tmaddr = 0x1800 + (select << 10), tdaddr;
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
if(status.bg_tiledata_select == 0) {
tdaddr = 0x1000 + ((int8)vram[tmaddr] << 4);
} else {
tdaddr = 0x0000 + (vram[tmaddr] << 4);
}
tdaddr += (y & 7) << 1;
return (vram[tdaddr + 0] << 0) | (vram[tdaddr + 1] << 8);
}
void LCD::dmg_render_bg() {
unsigned iy = (status.ly + status.scy) & 255;
unsigned ix = status.scx, tx = ix & 7;
unsigned data = dmg_read_tile(status.bg_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
line[ox] = bgp[palette];
origin[ox] = Origin::BG;
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = dmg_read_tile(status.bg_tilemap_select, ix, iy);
}
}
void LCD::dmg_render_window() {
if(status.ly - status.wy >= 144u) return;
if(status.wx >= 167u) return;
unsigned iy = status.wyc++;
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
unsigned data = dmg_read_tile(status.window_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(ox - (status.wx - 7) < 160u) {
line[ox] = bgp[palette];
origin[ox] = Origin::BG;
}
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = dmg_read_tile(status.window_tilemap_select, ix, iy);
}
}
//Attributes:
//0x80: 0 = OBJ above BG, 1 = BG above OBJ
//0x40: vertical flip
//0x20: horizontal flip
//0x10: palette#
void LCD::dmg_render_ob() {
const unsigned Height = (status.ob_size == 0 ? 8 : 16);
unsigned sprite[10], sprites = 0;
//find first ten sprites on this scanline
for(unsigned s = 0; s < 40; s++) {
unsigned sy = oam[(s << 2) + 0] - 16;
unsigned sx = oam[(s << 2) + 1] - 8;
sy = status.ly - sy;
if(sy >= Height) continue;
sprite[sprites++] = s;
if(sprites == 10) break;
}
//sort by X-coordinate, when equal, lower address comes first
for(unsigned x = 0; x < sprites; x++) {
for(unsigned y = x + 1; y < sprites; y++) {
signed sx = oam[(sprite[x] << 2) + 1] - 8;
signed sy = oam[(sprite[y] << 2) + 1] - 8;
if(sy < sx) {
sprite[x] ^= sprite[y];
sprite[y] ^= sprite[x];
sprite[x] ^= sprite[y];
}
}
}
//render backwards, so that first sprite has highest priority
for(signed s = sprites - 1; s >= 0; s--) {
unsigned n = sprite[s] << 2;
unsigned sy = oam[n + 0] - 16;
unsigned sx = oam[n + 1] - 8;
unsigned tile = oam[n + 2] & ~status.ob_size;
unsigned attr = oam[n + 3];
sy = status.ly - sy;
if(sy >= Height) continue;
if(attr & 0x40) sy ^= (Height - 1);
unsigned tdaddr = (tile << 4) + (sy << 1), data = 0;
data |= vram[tdaddr++] << 0;
data |= vram[tdaddr++] << 8;
if(attr & 0x20) data = hflip(data);
for(unsigned tx = 0; tx < 8; tx++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(palette == 0) continue;
palette = obp[(bool)(attr & 0x10)][palette];
unsigned ox = sx + tx;
if(ox < 160) {
if(attr & 0x80) {
if(origin[ox] == Origin::BG) {
if(line[ox] > 0) continue;
}
}
line[ox] = palette;
origin[ox] = Origin::OB;
}
}
}
}
#endif

View File

@@ -1,158 +0,0 @@
#include <gameboy/gameboy.hpp>
//LY = 0-153
//Raster = 0-143
//Vblank = 144-153
//LX = 0-455
#define LCD_CPP
namespace GameBoy {
#include "dmg.cpp"
#include "cgb.cpp"
#include "mmio/mmio.cpp"
#include "serialization.cpp"
LCD lcd;
void LCD::Main() {
lcd.main();
}
void LCD::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
add_clocks(4);
status.lx += 4;
if(status.lx >= 456) scanline();
if(status.display_enable && status.lx == 0) {
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.display_enable && status.lx == 252) {
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
cpu.hblank();
}
}
}
void LCD::add_clocks(unsigned clocks) {
clock += clocks * cpu.frequency;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread);
}
}
void LCD::scanline() {
status.lx -= 456;
if(++status.ly == 154) frame();
if(status.display_enable && status.interrupt_lyc == true) {
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.ly < 144) {
system.cgb() == false ? dmg_render() : cgb_render();
}
if(status.display_enable && status.ly == 144) {
cpu.interrupt_raise(CPU::Interrupt::Vblank);
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
}
void LCD::frame() {
interface->videoRefresh(screen);
cpu.mmio_joyp_poll();
status.ly = 0;
status.wyc = 0;
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
unsigned LCD::hflip(unsigned data) const {
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);
}
void LCD::power() {
create(Main, 4 * 1024 * 1024);
for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
for(unsigned n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM
bus.mmio[0xff40] = this; //LCDC
bus.mmio[0xff41] = this; //STAT
bus.mmio[0xff42] = this; //SCY
bus.mmio[0xff43] = this; //SCX
bus.mmio[0xff44] = this; //LY
bus.mmio[0xff45] = this; //LYC
bus.mmio[0xff47] = this; //BGP
bus.mmio[0xff48] = this; //OBP0
bus.mmio[0xff49] = this; //OBP1
bus.mmio[0xff4a] = this; //WY
bus.mmio[0xff4b] = this; //WX
if(system.cgb()) {
bus.mmio[0xff4f] = this; //VBK
bus.mmio[0xff68] = this; //BGPI
bus.mmio[0xff69] = this; //BGPD
bus.mmio[0xff6a] = this; //OBPI
bus.mmio[0xff6b] = this; //OBPD
}
for(auto &n : screen) n = 0x0000;
for(auto &n : line) n = 0x0000;
for(auto &n : origin) n = Origin::None;
for(auto &n : vram) n = 0x00;
for(auto &n : oam) n = 0x00;
for(auto &n : bgp) n = 0x00;
for(auto &n : obp[0]) n = 0x00;
for(auto &n : obp[1]) n = 0x00;
for(auto &n : bgpd) n = 0x0000;
for(auto &n : obpd) n = 0x0000;
status.lx = 0;
status.wyc = 0;
status.display_enable = 0;
status.window_tilemap_select = 0;
status.window_display_enable = 0;
status.bg_tiledata_select = 0;
status.bg_tilemap_select = 0;
status.ob_size = 0;
status.ob_enable = 0;
status.bg_enable = 0;
status.interrupt_lyc = 0;
status.interrupt_oam = 0;
status.interrupt_vblank = 0;
status.interrupt_hblank = 0;
status.scy = 0;
status.scx = 0;
status.ly = 0;
status.lyc = 0;
status.wy = 0;
status.wx = 0;
status.vram_bank = 0;
status.bgpi_increment = 0;
status.bgpi = 0;
status.obpi_increment = 0;
status.obpi = 0;
}
LCD::LCD() {
}
}

View File

@@ -1,94 +0,0 @@
struct LCD : Processor, MMIO {
#include "mmio/mmio.hpp"
struct Status {
unsigned lx;
unsigned wyc;
//$ff40 LCDC
bool display_enable;
bool window_tilemap_select;
bool window_display_enable;
bool bg_tiledata_select;
bool bg_tilemap_select;
bool ob_size;
bool ob_enable;
bool bg_enable;
//$ff41 STAT
bool interrupt_lyc;
bool interrupt_oam;
bool interrupt_vblank;
bool interrupt_hblank;
//$ff42 SCY
uint8 scy;
//$ff43 SCX
uint8 scx;
//$ff44 LY
uint8 ly;
//$ff45 LYC
uint8 lyc;
//$ff4a WY
uint8 wy;
//$ff4b WX
uint8 wx;
//$ff4f VBK
bool vram_bank;
//$ff68 BGPI
bool bgpi_increment;
uint6 bgpi;
//$ff6a OBPI
bool obpi_increment;
uint8 obpi;
} status;
uint16 screen[160 * 144];
uint16 line[160];
struct Origin { enum : unsigned { None, BG, BGP, OB }; };
uint8 origin[160];
uint8 vram[16384]; //GB = 8192, GBC = 16384
uint8 oam[160];
uint8 bgp[4];
uint8 obp[2][4];
uint8 bgpd[64];
uint8 obpd[64];
static void Main();
void main();
void add_clocks(unsigned clocks);
void scanline();
void frame();
unsigned hflip(unsigned data) const;
//dmg.cpp
void dmg_render();
uint16 dmg_read_tile(bool select, unsigned x, unsigned y);
void dmg_render_bg();
void dmg_render_window();
void dmg_render_ob();
//cgb.cpp
void cgb_render();
void cgb_read_tile(bool select, unsigned x, unsigned y, unsigned &tile, unsigned &attr, unsigned &data);
void cgb_render_bg();
void cgb_render_window();
void cgb_render_ob();
void power();
void serialize(serializer&);
LCD();
};
extern LCD lcd;

View File

@@ -1,203 +0,0 @@
#ifdef LCD_CPP
unsigned LCD::vram_addr(uint16 addr) const {
return (status.vram_bank * 0x2000) + (addr & 0x1fff);
}
uint8 LCD::mmio_read(uint16 addr) {
if(addr >= 0x8000 && addr <= 0x9fff) return vram[vram_addr(addr)];
if(addr >= 0xfe00 && addr <= 0xfe9f) return oam[addr & 0xff];
if(addr == 0xff40) { //LCDC
return (status.display_enable << 7)
| (status.window_tilemap_select << 6)
| (status.window_display_enable << 5)
| (status.bg_tiledata_select << 4)
| (status.bg_tilemap_select << 3)
| (status.ob_size << 2)
| (status.ob_enable << 1)
| (status.bg_enable << 0);
}
if(addr == 0xff41) { //STAT
unsigned mode;
if(status.ly >= 144) mode = 1; //Vblank
else if(status.lx < 80) mode = 2; //OAM
else if(status.lx < 252) mode = 3; //LCD
else mode = 0; //Hblank
return (status.interrupt_lyc << 6)
| (status.interrupt_oam << 5)
| (status.interrupt_vblank << 4)
| (status.interrupt_hblank << 3)
| ((status.ly == status.lyc) << 2)
| (mode << 0);
}
if(addr == 0xff42) { //SCY
return status.scy;
}
if(addr == 0xff43) { //SCX
return status.scx;
}
if(addr == 0xff44) { //LY
return status.ly;
}
if(addr == 0xff45) { //LYC
return status.lyc;
}
if(addr == 0xff47) { //BGP
return (bgp[3] << 6)
| (bgp[2] << 4)
| (bgp[1] << 2)
| (bgp[0] << 0);
}
if(addr == 0xff48) { //OBP0
return (obp[0][3] << 6)
| (obp[0][2] << 4)
| (obp[0][1] << 2)
| (obp[0][0] << 0);
}
if(addr == 0xff49) { //OBP1
return (obp[1][3] << 6)
| (obp[1][2] << 4)
| (obp[1][1] << 2)
| (obp[1][0] << 0);
}
if(addr == 0xff4a) { //WY
return status.wy;
}
if(addr == 0xff4b) { //WX
return status.wx;
}
if(addr == 0xff69) { //BGPD
return bgpd[status.bgpi];
}
if(addr == 0xff6b) { //OBPD
return obpd[status.obpi];
}
return 0x00;
}
void LCD::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0x8000 && addr <= 0x9fff) { vram[vram_addr(addr)] = data; return; }
if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; }
if(addr == 0xff40) { //LCDC
if(status.display_enable == false && (data & 0x80)) {
status.lx = 0; //unverified behavior; fixes Super Mario Land 2 - Tree Zone
}
status.display_enable = data & 0x80;
status.window_tilemap_select = data & 0x40;
status.window_display_enable = data & 0x20;
status.bg_tiledata_select = data & 0x10;
status.bg_tilemap_select = data & 0x08;
status.ob_size = data & 0x04;
status.ob_enable = data & 0x02;
status.bg_enable = data & 0x01;
return;
}
if(addr == 0xff41) { //STAT
status.interrupt_lyc = data & 0x40;
status.interrupt_oam = data & 0x20;
status.interrupt_vblank = data & 0x10;
status.interrupt_hblank = data & 0x08;
return;
}
if(addr == 0xff42) { //SCY
status.scy = data;
return;
}
if(addr == 0xff43) { //SCX
status.scx = data;
return;
}
if(addr == 0xff44) { //LY
status.ly = 0;
return;
}
if(addr == 0xff45) { //LYC
status.lyc = data;
return;
}
if(addr == 0xff47) { //BGP
bgp[3] = (data >> 6) & 3;
bgp[2] = (data >> 4) & 3;
bgp[1] = (data >> 2) & 3;
bgp[0] = (data >> 0) & 3;
return;
}
if(addr == 0xff48) { //OBP0
obp[0][3] = (data >> 6) & 3;
obp[0][2] = (data >> 4) & 3;
obp[0][1] = (data >> 2) & 3;
obp[0][0] = (data >> 0) & 3;
return;
}
if(addr == 0xff49) { //OBP1
obp[1][3] = (data >> 6) & 3;
obp[1][2] = (data >> 4) & 3;
obp[1][1] = (data >> 2) & 3;
obp[1][0] = (data >> 0) & 3;
return;
}
if(addr == 0xff4a) { //WY
status.wy = data;
return;
}
if(addr == 0xff4b) { //WX
status.wx = data;
return;
}
if(addr == 0xff4f) { //VBK
status.vram_bank = data & 1;
return;
}
if(addr == 0xff68) { //BGPI
status.bgpi_increment = data & 0x80;
status.bgpi = data & 0x3f;
return;
}
if(addr == 0xff69) { //BGPD
bgpd[status.bgpi] = data;
if(status.bgpi_increment) status.bgpi++;
return;
}
if(addr == 0xff6a) { //OBPI
status.obpi_increment = data & 0x80;
status.obpi = data & 0x3f;
}
if(addr == 0xff6b) { //OBPD
obpd[status.obpi] = data;
if(status.obpi_increment) status.obpi++;
}
}
#endif

View File

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

View File

@@ -1,53 +0,0 @@
#ifdef LCD_CPP
void LCD::serialize(serializer &s) {
Processor::serialize(s);
s.array(screen);
s.array(line);
s.array(origin);
s.array(vram);
s.array(oam);
s.array(bgp);
s.array(obp[0]);
s.array(obp[1]);
s.array(bgpd);
s.array(obpd);
s.integer(status.lx);
s.integer(status.wyc);
s.integer(status.display_enable);
s.integer(status.window_tilemap_select);
s.integer(status.window_display_enable);
s.integer(status.bg_tiledata_select);
s.integer(status.bg_tilemap_select);
s.integer(status.ob_size);
s.integer(status.ob_enable);
s.integer(status.bg_enable);
s.integer(status.interrupt_lyc);
s.integer(status.interrupt_oam);
s.integer(status.interrupt_vblank);
s.integer(status.interrupt_hblank);
s.integer(status.scy);
s.integer(status.scx);
s.integer(status.ly);
s.integer(status.lyc);
s.integer(status.wy);
s.integer(status.wx);
s.integer(status.vram_bank);
s.integer(status.bgpi_increment);
s.integer(status.bgpi);
s.integer(status.obpi_increment);
s.integer(status.obpi);
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,135 +0,0 @@
#ifdef SYSTEM_CPP
//SHA256 = 4bf5021be357ce523a59ac5f4efff5d6371ae50112a6db0adf4a75916ad760a9
const uint8_t System::BootROM::cgb[2048] = {
0x31,0xfe,0xff,0x3e,0x02,0xc3,0x7c,0x00,0xd3,0x00,0x98,0xa0,0x12,0xd3,0x00,0x80,
0x00,0x40,0x1e,0x53,0xd0,0x00,0x1f,0x42,0x1c,0x00,0x14,0x2a,0x4d,0x19,0x8c,0x7e,
0x00,0x7c,0x31,0x6e,0x4a,0x45,0x52,0x4a,0x00,0x00,0xff,0x53,0x1f,0x7c,0xff,0x03,
0x1f,0x00,0xff,0x1f,0xa7,0x00,0xef,0x1b,0x1f,0x00,0xef,0x1b,0x00,0x7c,0x00,0x00,
0xff,0x03,0xce,0xed,0x66,0x66,0xcc,0x0d,0x00,0x0b,0x03,0x73,0x00,0x83,0x00,0x0c,
0x00,0x0d,0x00,0x08,0x11,0x1f,0x88,0x89,0x00,0x0e,0xdc,0xcc,0x6e,0xe6,0xdd,0xdd,
0xd9,0x99,0xbb,0xbb,0x67,0x63,0x6e,0x0e,0xec,0xcc,0xdd,0xdc,0x99,0x9f,0xbb,0xb9,
0x33,0x3e,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,0x58,0x43,0xe0,0x70,0x3e,0xfc,
0xe0,0x47,0xcd,0x75,0x02,0xcd,0x00,0x02,0x26,0xd0,0xcd,0x03,0x02,0x21,0x00,0xfe,
0x0e,0xa0,0xaf,0x22,0x0d,0x20,0xfc,0x11,0x04,0x01,0x21,0x10,0x80,0x4c,0x1a,0xe2,
0x0c,0xcd,0xc6,0x03,0xcd,0xc7,0x03,0x13,0x7b,0xfe,0x34,0x20,0xf1,0x11,0x72,0x00,
0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,0xcd,0xf0,0x03,0x3e,0x01,0xe0,0x4f,
0x3e,0x91,0xe0,0x40,0x21,0xb2,0x98,0x06,0x4e,0x0e,0x44,0xcd,0x91,0x02,0xaf,0xe0,
0x4f,0x0e,0x80,0x21,0x42,0x00,0x06,0x18,0xf2,0x0c,0xbe,0x20,0xfe,0x23,0x05,0x20,
0xf7,0x21,0x34,0x01,0x06,0x19,0x78,0x86,0x2c,0x05,0x20,0xfb,0x86,0x20,0xfe,0xcd,
0x1c,0x03,0x18,0x02,0x00,0x00,0xcd,0xd0,0x05,0xaf,0xe0,0x70,0x3e,0x11,0xe0,0x50,
0x21,0x00,0x80,0xaf,0x22,0xcb,0x6c,0x28,0xfb,0xc9,0x2a,0x12,0x13,0x0d,0x20,0xfa,
0xc9,0xe5,0x21,0x0f,0xff,0xcb,0x86,0xcb,0x46,0x28,0xfc,0xe1,0xc9,0x11,0x00,0xff,
0x21,0x03,0xd0,0x0e,0x0f,0x3e,0x30,0x12,0x3e,0x20,0x12,0x1a,0x2f,0xa1,0xcb,0x37,
0x47,0x3e,0x10,0x12,0x1a,0x2f,0xa1,0xb0,0x4f,0x7e,0xa9,0xe6,0xf0,0x47,0x2a,0xa9,
0xa1,0xb0,0x32,0x47,0x79,0x77,0x3e,0x30,0x12,0xc9,0x3e,0x80,0xe0,0x68,0xe0,0x6a,
0x0e,0x6b,0x2a,0xe2,0x05,0x20,0xfb,0x4a,0x09,0x43,0x0e,0x69,0x2a,0xe2,0x05,0x20,
0xfb,0xc9,0xc5,0xd5,0xe5,0x21,0x00,0xd8,0x06,0x01,0x16,0x3f,0x1e,0x40,0xcd,0x4a,
0x02,0xe1,0xd1,0xc1,0xc9,0x3e,0x80,0xe0,0x26,0xe0,0x11,0x3e,0xf3,0xe0,0x12,0xe0,
0x25,0x3e,0x77,0xe0,0x24,0x21,0x30,0xff,0xaf,0x0e,0x10,0x22,0x2f,0x0d,0x20,0xfb,
0xc9,0xcd,0x11,0x02,0xcd,0x62,0x02,0x79,0xfe,0x38,0x20,0x14,0xe5,0xaf,0xe0,0x4f,
0x21,0xa7,0x99,0x3e,0x38,0x22,0x3c,0xfe,0x3f,0x20,0xfa,0x3e,0x01,0xe0,0x4f,0xe1,
0xc5,0xe5,0x21,0x43,0x01,0xcb,0x7e,0xcc,0x89,0x05,0xe1,0xc1,0xcd,0x11,0x02,0x79,
0xd6,0x30,0xd2,0x06,0x03,0x79,0xfe,0x01,0xca,0x06,0x03,0x7d,0xfe,0xd1,0x28,0x21,
0xc5,0x06,0x03,0x0e,0x01,0x16,0x03,0x7e,0xe6,0xf8,0xb1,0x22,0x15,0x20,0xf8,0x0c,
0x79,0xfe,0x06,0x20,0xf0,0x11,0x11,0x00,0x19,0x05,0x20,0xe7,0x11,0xa1,0xff,0x19,
0xc1,0x04,0x78,0x1e,0x83,0xfe,0x62,0x28,0x06,0x1e,0xc1,0xfe,0x64,0x20,0x07,0x7b,
0xe0,0x13,0x3e,0x87,0xe0,0x14,0xfa,0x02,0xd0,0xfe,0x00,0x28,0x0a,0x3d,0xea,0x02,
0xd0,0x79,0xfe,0x01,0xca,0x91,0x02,0x0d,0xc2,0x91,0x02,0xc9,0x0e,0x26,0xcd,0x4a,
0x03,0xcd,0x11,0x02,0xcd,0x62,0x02,0x0d,0x20,0xf4,0xcd,0x11,0x02,0x3e,0x01,0xe0,
0x4f,0xcd,0x3e,0x03,0xcd,0x41,0x03,0xaf,0xe0,0x4f,0xcd,0x3e,0x03,0xc9,0x21,0x08,
0x00,0x11,0x51,0xff,0x0e,0x05,0xcd,0x0a,0x02,0xc9,0xc5,0xd5,0xe5,0x21,0x40,0xd8,
0x0e,0x20,0x7e,0xe6,0x1f,0xfe,0x1f,0x28,0x01,0x3c,0x57,0x2a,0x07,0x07,0x07,0xe6,
0x07,0x47,0x3a,0x07,0x07,0x07,0xe6,0x18,0xb0,0xfe,0x1f,0x28,0x01,0x3c,0x0f,0x0f,
0x0f,0x47,0xe6,0xe0,0xb2,0x22,0x78,0xe6,0x03,0x5f,0x7e,0x0f,0x0f,0xe6,0x1f,0xfe,
0x1f,0x28,0x01,0x3c,0x07,0x07,0xb3,0x22,0x0d,0x20,0xc7,0xe1,0xd1,0xc1,0xc9,0x0e,
0x00,0x1a,0xe6,0xf0,0xcb,0x49,0x28,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x1a,
0xe6,0x0f,0xcb,0x49,0x20,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x13,0xcb,0x41,
0x28,0x0d,0xd5,0x11,0xf8,0xff,0xcb,0x49,0x28,0x03,0x11,0x08,0x00,0x19,0xd1,0x0c,
0x79,0xfe,0x18,0x20,0xcc,0xc9,0x47,0xd5,0x16,0x04,0x58,0xcb,0x10,0x17,0xcb,0x13,
0x17,0x15,0x20,0xf6,0xd1,0x22,0x23,0x22,0x23,0xc9,0x3e,0x19,0xea,0x10,0x99,0x21,
0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,0xf9,0x2e,0x0f,0x18,0xf3,0xc9,
0x3e,0x01,0xe0,0x4f,0xcd,0x00,0x02,0x11,0x07,0x06,0x21,0x80,0x80,0x0e,0xc0,0x1a,
0x22,0x23,0x22,0x23,0x13,0x0d,0x20,0xf7,0x11,0x04,0x01,0xcd,0x8f,0x03,0x01,0xa8,
0xff,0x09,0xcd,0x8f,0x03,0x01,0xf8,0xff,0x09,0x11,0x72,0x00,0x0e,0x08,0x23,0x1a,
0x22,0x13,0x0d,0x20,0xf9,0x21,0xc2,0x98,0x06,0x08,0x3e,0x08,0x0e,0x10,0x22,0x0d,
0x20,0xfc,0x11,0x10,0x00,0x19,0x05,0x20,0xf3,0xaf,0xe0,0x4f,0x21,0xc2,0x98,0x3e,
0x08,0x22,0x3c,0xfe,0x18,0x20,0x02,0x2e,0xe2,0xfe,0x28,0x20,0x03,0x21,0x02,0x99,
0xfe,0x38,0x20,0xed,0x21,0xd8,0x08,0x11,0x40,0xd8,0x06,0x08,0x3e,0xff,0x12,0x13,
0x12,0x13,0x0e,0x02,0xcd,0x0a,0x02,0x3e,0x00,0x12,0x13,0x12,0x13,0x13,0x13,0x05,
0x20,0xea,0xcd,0x62,0x02,0x21,0x4b,0x01,0x7e,0xfe,0x33,0x20,0x0b,0x2e,0x44,0x1e,
0x30,0x2a,0xbb,0x20,0x49,0x1c,0x18,0x04,0x2e,0x4b,0x1e,0x01,0x2a,0xbb,0x20,0x3e,
0x2e,0x34,0x01,0x10,0x00,0x2a,0x80,0x47,0x0d,0x20,0xfa,0xea,0x00,0xd0,0x21,0xc7,
0x06,0x0e,0x00,0x2a,0xb8,0x28,0x08,0x0c,0x79,0xfe,0x4f,0x20,0xf6,0x18,0x1f,0x79,
0xd6,0x41,0x38,0x1c,0x21,0x16,0x07,0x16,0x00,0x5f,0x19,0xfa,0x37,0x01,0x57,0x7e,
0xba,0x28,0x0d,0x11,0x0e,0x00,0x19,0x79,0x83,0x4f,0xd6,0x5e,0x38,0xed,0x0e,0x00,
0x21,0x33,0x07,0x06,0x00,0x09,0x7e,0xe6,0x1f,0xea,0x08,0xd0,0x7e,0xe6,0xe0,0x07,
0x07,0x07,0xea,0x0b,0xd0,0xcd,0xe9,0x04,0xc9,0x11,0x91,0x07,0x21,0x00,0xd9,0xfa,
0x0b,0xd0,0x47,0x0e,0x1e,0xcb,0x40,0x20,0x02,0x13,0x13,0x1a,0x22,0x20,0x02,0x1b,
0x1b,0xcb,0x48,0x20,0x02,0x13,0x13,0x1a,0x22,0x13,0x13,0x20,0x02,0x1b,0x1b,0xcb,
0x50,0x28,0x05,0x1b,0x2b,0x1a,0x22,0x13,0x1a,0x22,0x13,0x0d,0x20,0xd7,0x21,0x00,
0xd9,0x11,0x00,0xda,0xcd,0x64,0x05,0xc9,0x21,0x12,0x00,0xfa,0x05,0xd0,0x07,0x07,
0x06,0x00,0x4f,0x09,0x11,0x40,0xd8,0x06,0x08,0xe5,0x0e,0x02,0xcd,0x0a,0x02,0x13,
0x13,0x13,0x13,0x13,0x13,0xe1,0x05,0x20,0xf0,0x11,0x42,0xd8,0x0e,0x02,0xcd,0x0a,
0x02,0x11,0x4a,0xd8,0x0e,0x02,0xcd,0x0a,0x02,0x2b,0x2b,0x11,0x44,0xd8,0x0e,0x02,
0xcd,0x0a,0x02,0xc9,0x0e,0x60,0x2a,0xe5,0xc5,0x21,0xe8,0x07,0x06,0x00,0x4f,0x09,
0x0e,0x08,0xcd,0x0a,0x02,0xc1,0xe1,0x0d,0x20,0xec,0xc9,0xfa,0x08,0xd0,0x11,0x18,
0x00,0x3c,0x3d,0x28,0x03,0x19,0x20,0xfa,0xc9,0xcd,0x1d,0x02,0x78,0xe6,0xff,0x28,
0x0f,0x21,0xe4,0x08,0x06,0x00,0x2a,0xb9,0x28,0x08,0x04,0x78,0xfe,0x0c,0x20,0xf6,
0x18,0x2d,0x78,0xea,0x05,0xd0,0x3e,0x1e,0xea,0x02,0xd0,0x11,0x0b,0x00,0x19,0x56,
0x7a,0xe6,0x1f,0x5f,0x21,0x08,0xd0,0x3a,0x22,0x7b,0x77,0x7a,0xe6,0xe0,0x07,0x07,
0x07,0x5f,0x21,0x0b,0xd0,0x3a,0x22,0x7b,0x77,0xcd,0xe9,0x04,0xcd,0x28,0x05,0xc9,
0xcd,0x11,0x02,0xfa,0x43,0x01,0xcb,0x7f,0x28,0x04,0xe0,0x4c,0x18,0x28,0x3e,0x04,
0xe0,0x4c,0x3e,0x01,0xe0,0x6c,0x21,0x00,0xda,0xcd,0x7b,0x05,0x06,0x10,0x16,0x00,
0x1e,0x08,0xcd,0x4a,0x02,0x21,0x7a,0x00,0xfa,0x00,0xd0,0x47,0x0e,0x02,0x2a,0xb8,
0xcc,0xda,0x03,0x0d,0x20,0xf8,0xc9,0x01,0x0f,0x3f,0x7e,0xff,0xff,0xc0,0x00,0xc0,
0xf0,0xf1,0x03,0x7c,0xfc,0xfe,0xfe,0x03,0x07,0x07,0x0f,0xe0,0xe0,0xf0,0xf0,0x1e,
0x3e,0x7e,0xfe,0x0f,0x0f,0x1f,0x1f,0xff,0xff,0x00,0x00,0x01,0x01,0x01,0x03,0xff,
0xff,0xe1,0xe0,0xc0,0xf0,0xf9,0xfb,0x1f,0x7f,0xf8,0xe0,0xf3,0xfd,0x3e,0x1e,0xe0,
0xf0,0xf9,0x7f,0x3e,0x7c,0xf8,0xe0,0xf8,0xf0,0xf0,0xf8,0x00,0x00,0x7f,0x7f,0x07,
0x0f,0x9f,0xbf,0x9e,0x1f,0xff,0xff,0x0f,0x1e,0x3e,0x3c,0xf1,0xfb,0x7f,0x7f,0xfe,
0xde,0xdf,0x9f,0x1f,0x3f,0x3e,0x3c,0xf8,0xf8,0x00,0x00,0x03,0x03,0x07,0x07,0xff,
0xff,0xc1,0xc0,0xf3,0xe7,0xf7,0xf3,0xc0,0xc0,0xc0,0xc0,0x1f,0x1f,0x1e,0x3e,0x3f,
0x1f,0x3e,0x3e,0x80,0x00,0x00,0x00,0x7c,0x1f,0x07,0x00,0x0f,0xff,0xfe,0x00,0x7c,
0xf8,0xf0,0x00,0x1f,0x0f,0x0f,0x00,0x7c,0xf8,0xf8,0x00,0x3f,0x3e,0x1c,0x00,0x0f,
0x0f,0x0f,0x00,0x7c,0xff,0xff,0x00,0x00,0xf8,0xf8,0x00,0x07,0x0f,0x0f,0x00,0x81,
0xff,0xff,0x00,0xf3,0xe1,0x80,0x00,0xe0,0xff,0x7f,0x00,0xfc,0xf0,0xc0,0x00,0x3e,
0x7c,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x16,0x36,0xd1,0xdb,0xf2,0x3c,0x8c,
0x92,0x3d,0x5c,0x58,0xc9,0x3e,0x70,0x1d,0x59,0x69,0x19,0x35,0xa8,0x14,0xaa,0x75,
0x95,0x99,0x34,0x6f,0x15,0xff,0x97,0x4b,0x90,0x17,0x10,0x39,0xf7,0xf6,0xa2,0x49,
0x4e,0x43,0x68,0xe0,0x8b,0xf0,0xce,0x0c,0x29,0xe8,0xb7,0x86,0x9a,0x52,0x01,0x9d,
0x71,0x9c,0xbd,0x5d,0x6d,0x67,0x3f,0x6b,0xb3,0x46,0x28,0xa5,0xc6,0xd3,0x27,0x61,
0x18,0x66,0x6a,0xbf,0x0d,0xf4,0x42,0x45,0x46,0x41,0x41,0x52,0x42,0x45,0x4b,0x45,
0x4b,0x20,0x52,0x2d,0x55,0x52,0x41,0x52,0x20,0x49,0x4e,0x41,0x49,0x4c,0x49,0x43,
0x45,0x20,0x52,0x7c,0x08,0x12,0xa3,0xa2,0x07,0x87,0x4b,0x20,0x12,0x65,0xa8,0x16,
0xa9,0x86,0xb1,0x68,0xa0,0x87,0x66,0x12,0xa1,0x30,0x3c,0x12,0x85,0x12,0x64,0x1b,
0x07,0x06,0x6f,0x6e,0x6e,0xae,0xaf,0x6f,0xb2,0xaf,0xb2,0xa8,0xab,0x6f,0xaf,0x86,
0xae,0xa2,0xa2,0x12,0xaf,0x13,0x12,0xa1,0x6e,0xaf,0xaf,0xad,0x06,0x4c,0x6e,0xaf,
0xaf,0x12,0x7c,0xac,0xa8,0x6a,0x6e,0x13,0xa0,0x2d,0xa8,0x2b,0xac,0x64,0xac,0x6d,
0x87,0xbc,0x60,0xb4,0x13,0x72,0x7c,0xb5,0xae,0xae,0x7c,0x7c,0x65,0xa2,0x6c,0x64,
0x85,0x80,0xb0,0x40,0x88,0x20,0x68,0xde,0x00,0x70,0xde,0x20,0x78,0x20,0x20,0x38,
0x20,0xb0,0x90,0x20,0xb0,0xa0,0xe0,0xb0,0xc0,0x98,0xb6,0x48,0x80,0xe0,0x50,0x1e,
0x1e,0x58,0x20,0xb8,0xe0,0x88,0xb0,0x10,0x20,0x00,0x10,0x20,0xe0,0x18,0xe0,0x18,
0x00,0x18,0xe0,0x20,0xa8,0xe0,0x20,0x18,0xe0,0x00,0x20,0x18,0xd8,0xc8,0x18,0xe0,
0x00,0xe0,0x40,0x28,0x28,0x28,0x18,0xe0,0x60,0x20,0x18,0xe0,0x00,0x00,0x08,0xe0,
0x18,0x30,0xd0,0xd0,0xd0,0x20,0xe0,0xe8,0xff,0x7f,0xbf,0x32,0xd0,0x00,0x00,0x00,
0x9f,0x63,0x79,0x42,0xb0,0x15,0xcb,0x04,0xff,0x7f,0x31,0x6e,0x4a,0x45,0x00,0x00,
0xff,0x7f,0xef,0x1b,0x00,0x02,0x00,0x00,0xff,0x7f,0x1f,0x42,0xf2,0x1c,0x00,0x00,
0xff,0x7f,0x94,0x52,0x4a,0x29,0x00,0x00,0xff,0x7f,0xff,0x03,0x2f,0x01,0x00,0x00,
0xff,0x7f,0xef,0x03,0xd6,0x01,0x00,0x00,0xff,0x7f,0xb5,0x42,0xc8,0x3d,0x00,0x00,
0x74,0x7e,0xff,0x03,0x80,0x01,0x00,0x00,0xff,0x67,0xac,0x77,0x13,0x1a,0x6b,0x2d,
0xd6,0x7e,0xff,0x4b,0x75,0x21,0x00,0x00,0xff,0x53,0x5f,0x4a,0x52,0x7e,0x00,0x00,
0xff,0x4f,0xd2,0x7e,0x4c,0x3a,0xe0,0x1c,0xed,0x03,0xff,0x7f,0x5f,0x25,0x00,0x00,
0x6a,0x03,0x1f,0x02,0xff,0x03,0xff,0x7f,0xff,0x7f,0xdf,0x01,0x12,0x01,0x00,0x00,
0x1f,0x23,0x5f,0x03,0xf2,0x00,0x09,0x00,0xff,0x7f,0xea,0x03,0x1f,0x01,0x00,0x00,
0x9f,0x29,0x1a,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x7f,0x02,0x1f,0x00,0x00,0x00,
0xff,0x7f,0xe0,0x03,0x06,0x02,0x20,0x01,0xff,0x7f,0xeb,0x7e,0x1f,0x00,0x00,0x7c,
0xff,0x7f,0xff,0x3f,0x00,0x7e,0x1f,0x00,0xff,0x7f,0xff,0x03,0x1f,0x00,0x00,0x00,
0xff,0x03,0x1f,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x3f,0x03,0x93,0x01,0x00,0x00,
0x00,0x00,0x00,0x42,0x7f,0x03,0xff,0x7f,0xff,0x7f,0x8c,0x7e,0x00,0x7c,0x00,0x00,
0xff,0x7f,0xef,0x1b,0x80,0x61,0x00,0x00,0xff,0x7f,0x00,0x7c,0xe0,0x03,0x1f,0x7c,
0x1f,0x00,0xff,0x03,0x40,0x41,0x42,0x20,0x21,0x22,0x80,0x81,0x82,0x10,0x11,0x12,
0x12,0xb0,0x79,0xb8,0xad,0x16,0x17,0x07,0xba,0x05,0x7c,0x13,0x00,0x00,0x00,0x00,
};
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,82 +0,0 @@
#include <gameboy/gameboy.hpp>
#define VIDEO_CPP
namespace GameBoy {
Video video;
unsigned Video::palette_dmg(unsigned color) const {
unsigned R = monochrome[color][0] * 1023.0;
unsigned G = monochrome[color][1] * 1023.0;
unsigned B = monochrome[color][2] * 1023.0;
return (R << 20) + (G << 10) + (B << 0);
}
unsigned Video::palette_sgb(unsigned color) const {
unsigned R = (3 - color) * 341;
unsigned G = (3 - color) * 341;
unsigned B = (3 - color) * 341;
return (R << 20) + (G << 10) + (B << 0);
}
unsigned Video::palette_cgb(unsigned color) const {
unsigned r = (color >> 0) & 31;
unsigned g = (color >> 5) & 31;
unsigned b = (color >> 10) & 31;
unsigned R = (r * 26 + g * 4 + b * 2);
unsigned G = ( g * 24 + b * 8);
unsigned B = (r * 6 + g * 4 + b * 22);
R = min(960, R);
G = min(960, G);
B = min(960, B);
return (R << 20) + (G << 10) + (B << 0);
}
void Video::generate(Format format) {
if(system.dmg()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_dmg(n);
if(system.sgb()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_sgb(n);
if(system.cgb()) for(unsigned n = 0; n < (1 << 15); n++) palette[n] = palette_cgb(n);
if(format == Format::RGB24) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff);
}
}
if(format == Format::RGB16) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f);
}
}
if(format == Format::RGB15) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f);
}
}
}
Video::Video() {
palette = new unsigned[1 << 15];
}
Video::~Video() {
delete[] palette;
}
const double Video::monochrome[4][3] = {
{ 0.605, 0.734, 0.059 },
{ 0.543, 0.672, 0.059 },
{ 0.188, 0.383, 0.188 },
{ 0.059, 0.219, 0.059 },
};
}

View File

@@ -1,17 +0,0 @@
struct Video {
enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 };
unsigned *palette;
unsigned palette_dmg(unsigned color) const;
unsigned palette_sgb(unsigned color) const;
unsigned palette_cgb(unsigned color) const;
void generate(Format format);
Video();
~Video();
private:
static const double monochrome[4][3];
};
extern Video video;

View File

@@ -1,104 +0,0 @@
/*
libco.amd64 (2009-10-12)
author: byuu
license: public domain
*/
#define LIBCO_C
#include "libco.h"
#include <assert.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
static thread_local long long co_active_buffer[64];
static thread_local cothread_t co_active_handle = 0;
static void (*co_swap)(cothread_t, cothread_t) = 0;
#ifdef _WIN32
//ABI: Win64
static unsigned char co_swap_function[] = {
0x48, 0x89, 0x22, 0x48, 0x8B, 0x21, 0x58, 0x48, 0x89, 0x6A, 0x08, 0x48, 0x89, 0x72, 0x10, 0x48,
0x89, 0x7A, 0x18, 0x48, 0x89, 0x5A, 0x20, 0x4C, 0x89, 0x62, 0x28, 0x4C, 0x89, 0x6A, 0x30, 0x4C,
0x89, 0x72, 0x38, 0x4C, 0x89, 0x7A, 0x40, 0x48, 0x81, 0xC2, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83,
0xE2, 0xF0, 0x0F, 0x29, 0x32, 0x0F, 0x29, 0x7A, 0x10, 0x44, 0x0F, 0x29, 0x42, 0x20, 0x44, 0x0F,
0x29, 0x4A, 0x30, 0x44, 0x0F, 0x29, 0x52, 0x40, 0x44, 0x0F, 0x29, 0x5A, 0x50, 0x44, 0x0F, 0x29,
0x62, 0x60, 0x44, 0x0F, 0x29, 0x6A, 0x70, 0x44, 0x0F, 0x29, 0xB2, 0x80, 0x00, 0x00, 0x00, 0x44,
0x0F, 0x29, 0xBA, 0x90, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x69, 0x08, 0x48, 0x8B, 0x71, 0x10, 0x48,
0x8B, 0x79, 0x18, 0x48, 0x8B, 0x59, 0x20, 0x4C, 0x8B, 0x61, 0x28, 0x4C, 0x8B, 0x69, 0x30, 0x4C,
0x8B, 0x71, 0x38, 0x4C, 0x8B, 0x79, 0x40, 0x48, 0x81, 0xC1, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83,
0xE1, 0xF0, 0x0F, 0x29, 0x31, 0x0F, 0x29, 0x79, 0x10, 0x44, 0x0F, 0x29, 0x41, 0x20, 0x44, 0x0F,
0x29, 0x49, 0x30, 0x44, 0x0F, 0x29, 0x51, 0x40, 0x44, 0x0F, 0x29, 0x59, 0x50, 0x44, 0x0F, 0x29,
0x61, 0x60, 0x44, 0x0F, 0x29, 0x69, 0x70, 0x44, 0x0F, 0x29, 0xB1, 0x80, 0x00, 0x00, 0x00, 0x44,
0x0F, 0x29, 0xB9, 0x90, 0x00, 0x00, 0x00, 0xFF, 0xE0,
};
#include <windows.h>
void co_init() {
DWORD old_privileges;
VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
}
#else
//ABI: SystemV
static unsigned char co_swap_function[] = {
0x48, 0x89, 0x26, 0x48, 0x8B, 0x27, 0x58, 0x48, 0x89, 0x6E, 0x08, 0x48, 0x89, 0x5E, 0x10, 0x4C,
0x89, 0x66, 0x18, 0x4C, 0x89, 0x6E, 0x20, 0x4C, 0x89, 0x76, 0x28, 0x4C, 0x89, 0x7E, 0x30, 0x48,
0x8B, 0x6F, 0x08, 0x48, 0x8B, 0x5F, 0x10, 0x4C, 0x8B, 0x67, 0x18, 0x4C, 0x8B, 0x6F, 0x20, 0x4C,
0x8B, 0x77, 0x28, 0x4C, 0x8B, 0x7F, 0x30, 0xFF, 0xE0,
};
#include <unistd.h>
#include <sys/mman.h>
void co_init() {
unsigned long long addr = (unsigned long long)co_swap_function;
unsigned long long base = addr - (addr % sysconf(_SC_PAGESIZE));
unsigned long long size = (addr - base) + sizeof co_swap_function;
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
}
#endif
static void crash() {
assert(0); /* called only if cothread_t entrypoint returns */
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
cothread_t handle;
if(!co_swap) {
co_init();
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
size += 512; /* allocate additional space for storage */
size &= ~15; /* align stack to 16-byte boundary */
if(handle = (cothread_t)malloc(size)) {
long long *p = (long long*)((char*)handle + size); /* seek to top of stack */
*--p = (long long)crash; /* crash if entrypoint returns */
*--p = (long long)entrypoint; /* start of function */
*(long long*)handle = (long long)p; /* stack pointer */
}
return handle;
}
void co_delete(cothread_t handle) {
free(handle);
}
void co_switch(cothread_t handle) {
register cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,23 +0,0 @@
/*
libco
auto-selection module
license: public domain
*/
#if defined(__GNUC__) && defined(__i386__)
#include "x86.c"
#elif defined(__GNUC__) && defined(__amd64__)
#include "amd64.c"
#elif defined(__GNUC__) && defined(_ARCH_PPC)
#include "ppc.c"
#elif defined(__GNUC__)
#include "sjlj.c"
#elif defined(_MSC_VER) && defined(_M_IX86)
#include "x86.c"
#elif defined(_MSC_VER) && defined(_M_AMD64)
#include "amd64.c"
#elif defined(_MSC_VER)
#include "fiber.c"
#else
#error "libco: unsupported processor, compiler or operating system"
#endif

View File

@@ -1,93 +0,0 @@
/*
libco.x86 (2009-10-12)
author: byuu
license: public domain
*/
#define LIBCO_C
#include "libco.h"
#include <assert.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_MSC_VER)
#define fastcall __fastcall
#elif defined(__GNUC__)
#define fastcall __attribute__((fastcall))
#else
#error "libco: please define fastcall macro"
#endif
static thread_local long co_active_buffer[64];
static thread_local cothread_t co_active_handle = 0;
static void (fastcall *co_swap)(cothread_t, cothread_t) = 0;
//ABI: fastcall
static unsigned char co_swap_function[] = {
0x89, 0x22, 0x8B, 0x21, 0x58, 0x89, 0x6A, 0x04, 0x89, 0x72, 0x08, 0x89, 0x7A, 0x0C, 0x89, 0x5A,
0x10, 0x8B, 0x69, 0x04, 0x8B, 0x71, 0x08, 0x8B, 0x79, 0x0C, 0x8B, 0x59, 0x10, 0xFF, 0xE0,
};
#ifdef _WIN32
#include <windows.h>
void co_init() {
DWORD old_privileges;
VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
}
#else
#include <unistd.h>
#include <sys/mman.h>
void co_init() {
unsigned long addr = (unsigned long)co_swap_function;
unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE));
unsigned long size = (addr - base) + sizeof co_swap_function;
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
}
#endif
static void crash() {
assert(0); /* called only if cothread_t entrypoint returns */
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
cothread_t handle;
if(!co_swap) {
co_init();
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
size += 256; /* allocate additional space for storage */
size &= ~15; /* align stack to 16-byte boundary */
if(handle = (cothread_t)malloc(size)) {
long *p = (long*)((char*)handle + size); /* seek to top of stack */
*--p = (long)crash; /* crash if entrypoint returns */
*--p = (long)entrypoint; /* start of function */
*(long*)handle = (long)p; /* stack pointer */
}
return handle;
}
void co_delete(cothread_t handle) {
free(handle);
}
void co_switch(cothread_t handle) {
register cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,115 +0,0 @@
# Makefile
# author: byuu
# license: public domain
[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z
[0-9] = 0 1 2 3 4 5 6 7 8 9
[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ?
[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup])
[space] :=
[space] +=
#####
# platform detection
#####
ifeq ($(platform),)
uname := $(shell uname -a)
ifeq ($(uname),)
platform := win
delete = del $(subst /,\,$1)
else ifneq ($(findstring CYGWIN,$(uname)),)
platform := win
delete = del $(subst /,\,$1)
else ifneq ($(findstring Darwin,$(uname)),)
platform := osx
delete = rm -f $1
else
platform := x
delete = rm -f $1
endif
endif
ifeq ($(compiler),)
ifeq ($(platform),win)
compiler := gcc
else ifeq ($(platform),osx)
compiler := gcc-mp-4.6
else
compiler := gcc-4.6
endif
endif
c := $(compiler) -std=gnu99
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
ifeq ($(prefix),)
prefix := /usr/local
endif
#####
# function rwildcard(directory, pattern)
#####
rwildcard = \
$(strip \
$(filter $(if $2,$2,%), \
$(foreach f, \
$(wildcard $1*), \
$(eval t = $(call rwildcard,$f/)) \
$(if $t,$t,$f) \
) \
) \
)
#####
# function strtr(source, from, to)
#####
strtr = \
$(eval __temp := $1) \
$(strip \
$(foreach c, \
$(join $(addsuffix :,$2),$3), \
$(eval __temp := \
$(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \
) \
) \
$(__temp) \
)
#####
# function strupper(source)
#####
strupper = $(call strtr,$1,$([a-z]),$([A-Z]))
#####
# function strlower(source)
#####
strlower = $(call strtr,$1,$([A-Z]),$([a-z]))
#####
# function strlen(source)
#####
strlen = \
$(eval __temp := $(subst $([space]),_,$1)) \
$(words \
$(strip \
$(foreach c, \
$([all]), \
$(eval __temp := \
$(subst $c,$c ,$(__temp)) \
) \
) \
$(__temp) \
) \
)
#####
# function streq(source)
#####
streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1)
#####
# function strne(source)
#####
strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,)

View File

@@ -1,17 +0,0 @@
#ifndef NALL_ALGORITHM_HPP
#define NALL_ALGORITHM_HPP
#undef min
#undef max
namespace nall {
template<typename T, typename U> T min(const T &t, const U &u) {
return t < u ? t : u;
}
template<typename T, typename U> T max(const T &t, const U &u) {
return t > u ? t : u;
}
}
#endif

View File

@@ -1,72 +0,0 @@
#ifndef NALL_ANY_HPP
#define NALL_ANY_HPP
#include <typeinfo>
#include <nall/type_traits.hpp>
namespace nall {
struct any {
bool empty() const { return container; }
const std::type_info& type() const { return container ? container->type() : typeid(void); }
template<typename T> any& operator=(const T& value_) {
typedef typename type_if<
std::is_array<T>::value,
typename std::remove_extent<typename std::add_const<T>::type>::type*,
T
>::type auto_t;
if(type() == typeid(auto_t)) {
static_cast<holder<auto_t>*>(container)->value = (auto_t)value_;
} else {
if(container) delete container;
container = new holder<auto_t>((auto_t)value_);
}
return *this;
}
any() : container(0) {}
template<typename T> any(const T& value_) : container(0) { operator=(value_); }
private:
struct placeholder {
virtual const std::type_info& type() const = 0;
} *container;
template<typename T> struct holder : placeholder {
T value;
const std::type_info& type() const { return typeid(T); }
holder(const T& value_) : value(value_) {}
};
template<typename T> friend T any_cast(any&);
template<typename T> friend T any_cast(const any&);
template<typename T> friend T* any_cast(any*);
template<typename T> friend const T* any_cast(const any*);
};
template<typename T> T any_cast(any &value) {
typedef typename std::remove_reference<T>::type nonref;
if(value.type() != typeid(nonref)) throw;
return static_cast<any::holder<nonref>*>(value.container)->value;
}
template<typename T> T any_cast(const any &value) {
typedef const typename std::remove_reference<T>::type nonref;
if(value.type() != typeid(nonref)) throw;
return static_cast<any::holder<nonref>*>(value.container)->value;
}
template<typename T> T* any_cast(any *value) {
if(!value || value->type() != typeid(T)) return 0;
return &static_cast<any::holder<T>*>(value->container)->value;
}
template<typename T> const T* any_cast(const any *value) {
if(!value || value->type() != typeid(T)) return 0;
return &static_cast<any::holder<T>*>(value->container)->value;
}
}
#endif

View File

@@ -1,289 +0,0 @@
#ifndef NALL_ARRAY_HPP
#define NALL_ARRAY_HPP
#include <stdlib.h>
#include <algorithm>
#include <initializer_list>
#include <utility>
#include <nall/algorithm.hpp>
#include <nall/bit.hpp>
#include <nall/sort.hpp>
#include <nall/type_traits.hpp>
#include <nall/utility.hpp>
namespace nall {
template<typename T, typename Enable = void> struct array;
//non-reference array
//===================
template<typename T> struct array<T, typename std::enable_if<!std::is_reference<T>::value>::type> {
struct exception_out_of_bounds{};
protected:
T *pool;
unsigned poolsize, objectsize;
public:
unsigned size() const { return objectsize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) free(pool);
pool = nullptr;
poolsize = 0;
objectsize = 0;
}
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
pool = (T*)realloc(pool, newsize * sizeof(T));
poolsize = newsize;
objectsize = min(objectsize, newsize);
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2
objectsize = newsize;
}
T* get(unsigned minsize = 0) {
if(minsize > objectsize) resize(minsize);
return pool;
}
void append(const T data) {
operator()(objectsize) = data;
}
void append(const T data[], unsigned length) {
for(unsigned n = 0; n < length; n++) operator()(objectsize) = data[n];
}
void remove() {
if(size > 0) resize(size - 1); //remove last element only
}
void remove(unsigned index, unsigned count = 1) {
for(unsigned i = index; count + i < objectsize; i++) {
pool[i] = pool[count + i];
}
if(count + index >= objectsize) resize(index); //every element >= index was removed
else resize(objectsize - count);
}
void sort() {
nall::sort(pool, objectsize);
}
template<typename Comparator> void sort(const Comparator &lessthan) {
nall::sort(pool, objectsize, lessthan);
}
optional<unsigned> find(const T data) {
for(unsigned n = 0; n < size(); n++) if(pool[n] == data) return { true, n };
return { false, 0u };
}
void clear() {
memset(pool, 0, objectsize * sizeof(T));
}
array() : pool(nullptr), poolsize(0), objectsize(0) {
}
array(std::initializer_list<T> list) : pool(nullptr), poolsize(0), objectsize(0) {
for(auto &data : list) append(data);
}
~array() {
reset();
}
//copy
array& operator=(const array &source) {
if(pool) free(pool);
objectsize = source.objectsize;
poolsize = source.poolsize;
pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size,
memcpy(pool, source.pool, sizeof(T) * objectsize); //... but only copy used pool objects
return *this;
}
array(const array &source) : pool(nullptr), poolsize(0), objectsize(0) {
operator=(source);
}
//move
array& operator=(array &&source) {
if(pool) free(pool);
pool = source.pool;
poolsize = source.poolsize;
objectsize = source.objectsize;
source.pool = nullptr;
source.reset();
return *this;
}
array(array &&source) : pool(nullptr), poolsize(0), objectsize(0) {
operator=(std::move(source));
}
//access
inline T& operator[](unsigned position) {
if(position >= objectsize) throw exception_out_of_bounds();
return pool[position];
}
inline const T& operator[](unsigned position) const {
if(position >= objectsize) throw exception_out_of_bounds();
return pool[position];
}
inline T& operator()(unsigned position) {
if(position >= objectsize) resize(position + 1);
return pool[position];
}
inline const T& operator()(unsigned position, const T& data) {
if(position >= objectsize) return data;
return pool[position];
}
//iteration
T* begin() { return &pool[0]; }
T* end() { return &pool[objectsize]; }
const T* begin() const { return &pool[0]; }
const T* end() const { return &pool[objectsize]; }
};
//reference array
//===============
template<typename TR> struct array<TR, typename std::enable_if<std::is_reference<TR>::value>::type> {
struct exception_out_of_bounds{};
protected:
typedef typename std::remove_reference<TR>::type T;
T **pool;
unsigned poolsize, objectsize;
public:
unsigned size() const { return objectsize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) free(pool);
pool = nullptr;
poolsize = 0;
objectsize = 0;
}
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
pool = (T**)realloc(pool, sizeof(T*) * newsize);
poolsize = newsize;
objectsize = min(objectsize, newsize);
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(bit::round(newsize));
objectsize = newsize;
}
template<typename... Args>
bool append(T& data, Args&&... args) {
bool result = append(data);
append(std::forward<Args>(args)...);
return result;
}
bool append(T& data) {
if(find(data)) return false;
unsigned offset = objectsize++;
if(offset >= poolsize) resize(offset + 1);
pool[offset] = &data;
return true;
}
bool remove(T& data) {
if(auto position = find(data)) {
for(signed i = position(); i < objectsize - 1; i++) pool[i] = pool[i + 1];
resize(objectsize - 1);
return true;
}
return false;
}
optional<unsigned> find(const T& data) {
for(unsigned n = 0; n < objectsize; n++) if(pool[n] == &data) return { true, n };
return { false, 0u };
}
template<typename... Args> array(Args&&... args) : pool(nullptr), poolsize(0), objectsize(0) {
construct(std::forward<Args>(args)...);
}
~array() {
reset();
}
array& operator=(const array &source) {
if(pool) free(pool);
objectsize = source.objectsize;
poolsize = source.poolsize;
pool = (T**)malloc(sizeof(T*) * poolsize);
memcpy(pool, source.pool, sizeof(T*) * objectsize);
return *this;
}
array& operator=(const array &&source) {
if(pool) free(pool);
pool = source.pool;
poolsize = source.poolsize;
objectsize = source.objectsize;
source.pool = nullptr;
source.reset();
return *this;
}
T& operator[](unsigned position) const {
if(position >= objectsize) throw exception_out_of_bounds();
return *pool[position];
}
//iteration
struct iterator {
bool operator!=(const iterator &source) const { return position != source.position; }
T& operator*() { return source.operator[](position); }
iterator& operator++() { position++; return *this; }
iterator(const array &source, unsigned position) : source(source), position(position) {}
private:
const array &source;
unsigned position;
};
iterator begin() { return iterator(*this, 0); }
iterator end() { return iterator(*this, objectsize); }
const iterator begin() const { return iterator(*this, 0); }
const iterator end() const { return iterator(*this, objectsize); }
private:
void construct() {
}
void construct(const array& source) { operator=(source); }
void construct(const array&& source) { operator=(std::move(source)); }
template<typename... Args> void construct(T& data, Args&&... args) {
append(data);
construct(std::forward<Args>(args)...);
}
};
}
#endif

View File

@@ -1,101 +0,0 @@
#ifndef NALL_ATOI_HPP
#define NALL_ATOI_HPP
namespace nall {
//note: this header is intended to form the base for user-defined literals;
//once they are supported by GCC. eg:
//unsigned operator "" b(const char *s) { return binary(s); }
//-> signed data = 1001b;
//(0b1001 is nicer, but is not part of the C++ standard)
constexpr inline uintmax_t binary_(const char *s, uintmax_t sum = 0) {
return (
*s == '0' || *s == '1' ? binary_(s + 1, (sum << 1) | *s - '0') :
sum
);
}
constexpr inline uintmax_t octal_(const char *s, uintmax_t sum = 0) {
return (
*s >= '0' && *s <= '7' ? octal_(s + 1, (sum << 3) | *s - '0') :
sum
);
}
constexpr inline uintmax_t decimal_(const char *s, uintmax_t sum = 0) {
return (
*s >= '0' && *s <= '9' ? decimal_(s + 1, (sum * 10) + *s - '0') :
sum
);
}
constexpr inline uintmax_t hex_(const char *s, uintmax_t sum = 0) {
return (
*s >= 'A' && *s <= 'F' ? hex_(s + 1, (sum << 4) | *s - 'A' + 10) :
*s >= 'a' && *s <= 'f' ? hex_(s + 1, (sum << 4) | *s - 'a' + 10) :
*s >= '0' && *s <= '9' ? hex_(s + 1, (sum << 4) | *s - '0') :
sum
);
}
//
constexpr inline uintmax_t binary(const char *s) {
return (
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
*s == '%' ? binary_(s + 1) :
binary_(s)
);
}
constexpr inline uintmax_t octal(const char *s) {
return (
octal_(s)
);
}
constexpr inline intmax_t integer(const char *s) {
return (
*s == '+' ? +decimal_(s + 1) :
*s == '-' ? -decimal_(s + 1) :
decimal_(s)
);
}
constexpr inline uintmax_t decimal(const char *s) {
return (
decimal_(s)
);
}
constexpr inline uintmax_t hex(const char *s) {
return (
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
*s == '$' ? hex_(s + 1) :
hex_(s)
);
}
constexpr inline intmax_t numeral(const char *s) {
return (
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
*s == '0' ? octal_(s + 1) :
*s == '+' ? +decimal_(s + 1) :
*s == '-' ? -decimal_(s + 1) :
decimal_(s)
);
}
inline double fp(const char *s) {
return atof(s);
}
}
#endif

View File

@@ -1,90 +0,0 @@
#ifndef NALL_BASE64_HPP
#define NALL_BASE64_HPP
#include <string.h>
#include <nall/stdint.hpp>
namespace nall {
struct base64 {
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
output = new char[inlength * 8 / 6 + 6]();
unsigned i = 0, o = 0;
while(i < inlength) {
switch(i % 3) {
case 0: {
output[o++] = enc(input[i] >> 2);
output[o] = enc((input[i] & 3) << 4);
} break;
case 1: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 4));
output[o] = enc((input[i] & 15) << 2);
} break;
case 2: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 6));
output[o++] = enc(input[i] & 63);
} break;
}
i++;
}
return true;
}
static bool decode(uint8_t *&output, unsigned &outlength, const char *input) {
unsigned inlength = strlen(input), infix = 0;
output = new uint8_t[inlength]();
unsigned i = 0, o = 0;
while(i < inlength) {
uint8_t x = dec(input[i]);
switch(i++ & 3) {
case 0: {
output[o] = x << 2;
} break;
case 1: {
output[o++] |= x >> 4;
output[o] = (x & 15) << 4;
} break;
case 2: {
output[o++] |= x >> 2;
output[o] = (x & 3) << 6;
} break;
case 3: {
output[o++] |= x;
} break;
}
}
outlength = o;
return true;
}
private:
static char enc(uint8_t n) {
//base64 for URL encodings
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
return lookup_table[n & 63];
}
static uint8_t dec(char n) {
if(n >= 'A' && n <= 'Z') return n - 'A';
if(n >= 'a' && n <= 'z') return n - 'a' + 26;
if(n >= '0' && n <= '9') return n - '0' + 52;
if(n == '-') return 62;
if(n == '_') return 63;
return 0;
}
};
}
#endif

View File

@@ -1,62 +0,0 @@
#ifndef NALL_BIT_HPP
#define NALL_BIT_HPP
namespace nall {
template<unsigned bits>
constexpr inline uintmax_t uclamp(const uintmax_t x) {
enum : uintmax_t { b = 1ull << (bits - 1), y = b * 2 - 1 };
return y + ((x - y) & -(x < y)); //min(x, y);
}
template<unsigned bits>
constexpr inline uintmax_t uclip(const uintmax_t x) {
enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
return (x & m);
}
template<unsigned bits>
constexpr inline intmax_t sclamp(const intmax_t x) {
enum : intmax_t { b = 1ull << (bits - 1), m = b - 1 };
return (x > m) ? m : (x < -b) ? -b : x;
}
template<unsigned bits>
constexpr inline intmax_t sclip(const intmax_t x) {
enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
return ((x & m) ^ b) - b;
}
namespace bit {
//lowest(0b1110) == 0b0010
constexpr inline uintmax_t lowest(const uintmax_t x) {
return x & -x;
}
//clear_lowest(0b1110) == 0b1100
constexpr inline uintmax_t clear_lowest(const uintmax_t x) {
return x & (x - 1);
}
//set_lowest(0b0101) == 0b0111
constexpr inline uintmax_t set_lowest(const uintmax_t x) {
return x | (x + 1);
}
//count number of bits set in a byte
inline unsigned count(uintmax_t x) {
unsigned count = 0;
do count += x & 1; while(x >>= 1);
return count;
}
//round up to next highest single bit:
//round(15) == 16, round(16) == 16, round(17) == 32
inline uintmax_t round(uintmax_t x) {
if((x & (x - 1)) == 0) return x;
while(x & (x - 1)) x &= x - 1;
return x << 1;
}
}
}
#endif

View File

@@ -1,101 +0,0 @@
#ifndef NALL_BMP_HPP
#define NALL_BMP_HPP
#include <nall/file.hpp>
//BMP reader / writer
//author: byuu
//note: only 24-bit RGB and 32-bit ARGB uncompressed images supported
namespace nall {
struct bmp {
inline static bool read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height);
inline static bool write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha = false);
};
bool bmp::read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height) {
file fp;
if(fp.open(filename, file::mode::read) == false) return false;
if(fp.size() < 0x36) return false;
if(fp.readm(2) != 0x424d) return false;
fp.seek(0x000a);
unsigned offset = fp.readl(4);
unsigned dibsize = fp.readl(4);
if(dibsize != 40) return false;
signed headerWidth = fp.readl(4);
if(headerWidth < 0) return false;
signed headerHeight = fp.readl(4);
fp.readl(2);
unsigned bitsPerPixel = fp.readl(2);
if(bitsPerPixel != 24 && bitsPerPixel != 32) return false;
unsigned compression = fp.readl(4);
if(compression != 0) return false;
fp.seek(offset);
bool noFlip = headerHeight < 0;
width = headerWidth, height = abs(headerHeight);
data = new uint32_t[width * height];
unsigned bytesPerPixel = bitsPerPixel / 8;
unsigned alignedWidth = width * bytesPerPixel;
unsigned paddingLength = 0;
while(alignedWidth % 4) alignedWidth++, paddingLength++;
for(unsigned y = 0; y < height; y++) {
uint32_t *p = noFlip ? data + y * width : data + (height - 1 - y) * width;
for(unsigned x = 0; x < width; x++, p++) {
*p = fp.readl(bytesPerPixel);
if(bytesPerPixel == 3) *p |= 255 << 24;
}
if(paddingLength) fp.readl(paddingLength);
}
fp.close();
return true;
}
bool bmp::write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha) {
file fp;
if(fp.open(filename, file::mode::write) == false) return false;
unsigned bitsPerPixel = alpha ? 32 : 24;
unsigned bytesPerPixel = bitsPerPixel / 8;
unsigned alignedWidth = width * bytesPerPixel;
unsigned paddingLength = 0;
unsigned imageSize = alignedWidth * height;
unsigned fileSize = 0x36 + imageSize;
while(alignedWidth % 4) alignedWidth++, paddingLength++;
fp.writem(0x424d, 2); //signature
fp.writel(fileSize, 4); //file size
fp.writel(0, 2); //reserved
fp.writel(0, 2); //reserved
fp.writel(0x36, 4); //offset
fp.writel(40, 4); //DIB size
fp.writel(width, 4); //width
fp.writel(-height, 4); //height
fp.writel(1, 2); //color planes
fp.writel(bitsPerPixel, 2); //bits per pixel
fp.writel(0, 4); //compression method (BI_RGB)
fp.writel(imageSize, 4); //image data size
fp.writel(3780, 4); //horizontal resolution
fp.writel(3780, 4); //vertical resolution
fp.writel(0, 4); //palette size
fp.writel(0, 4); //important color count
for(unsigned y = 0; y < height; y++) {
const uint32_t *p = (const uint32_t*)((const uint8_t*)data + y * pitch);
for(unsigned x = 0; x < width; x++) fp.writel(*p++, bytesPerPixel);
if(paddingLength) fp.writel(0, paddingLength);
}
fp.close();
return true;
}
}
#endif

View File

@@ -1,214 +0,0 @@
#ifndef NALL_BPS_DELTA_HPP
#define NALL_BPS_DELTA_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpsdelta {
inline void source(const uint8_t *data, unsigned size);
inline void target(const uint8_t *data, unsigned size);
inline bool source(const string &filename);
inline bool target(const string &filename);
inline bool create(const string &filename, const string &metadata = "");
protected:
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
enum : unsigned { Granularity = 1 };
struct Node {
unsigned offset;
Node *next;
inline Node() : offset(0), next(nullptr) {}
inline ~Node() { if(next) delete next; }
};
filemap sourceFile;
const uint8_t *sourceData;
unsigned sourceSize;
filemap targetFile;
const uint8_t *targetData;
unsigned targetSize;
};
void bpsdelta::source(const uint8_t *data, unsigned size) {
sourceData = data;
sourceSize = size;
}
void bpsdelta::target(const uint8_t *data, unsigned size) {
targetData = data;
targetSize = size;
}
bool bpsdelta::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
source(sourceFile.data(), sourceFile.size());
return true;
}
bool bpsdelta::target(const string &filename) {
if(targetFile.open(filename, filemap::mode::read) == false) return false;
target(targetFile.data(), targetFile.size());
return true;
}
bool bpsdelta::create(const string &filename, const string &metadata) {
file modifyFile;
if(modifyFile.open(filename, file::mode::write) == false) return false;
uint32_t sourceChecksum = ~0, modifyChecksum = ~0;
unsigned sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
auto write = [&](uint8_t data) {
modifyFile.write(data);
modifyChecksum = crc32_adjust(modifyChecksum, data);
};
auto encode = [&](uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) {
write(0x80 | x);
break;
}
write(x);
data--;
}
};
write('B');
write('P');
write('S');
write('1');
encode(sourceSize);
encode(targetSize);
unsigned markupSize = metadata.length();
encode(markupSize);
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
Node *sourceTree[65536], *targetTree[65536];
for(unsigned n = 0; n < 65536; n++) sourceTree[n] = 0, targetTree[n] = 0;
//source tree creation
for(unsigned offset = 0; offset < sourceSize; offset++) {
uint16_t symbol = sourceData[offset + 0];
sourceChecksum = crc32_adjust(sourceChecksum, symbol);
if(offset < sourceSize - 1) symbol |= sourceData[offset + 1] << 8;
Node *node = new Node;
node->offset = offset;
node->next = sourceTree[symbol];
sourceTree[symbol] = node;
}
unsigned targetReadLength = 0;
auto targetReadFlush = [&]() {
if(targetReadLength) {
encode(TargetRead | ((targetReadLength - 1) << 2));
unsigned offset = outputOffset - targetReadLength;
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
}
};
while(outputOffset < targetSize) {
unsigned maxLength = 0, maxOffset = 0, mode = TargetRead;
uint16_t symbol = targetData[outputOffset + 0];
if(outputOffset < targetSize - 1) symbol |= targetData[outputOffset + 1] << 8;
{ //source read
unsigned length = 0, offset = outputOffset;
while(offset < sourceSize && offset < targetSize && sourceData[offset] == targetData[offset]) {
length++;
offset++;
}
if(length > maxLength) maxLength = length, mode = SourceRead;
}
{ //source copy
Node *node = sourceTree[symbol];
while(node) {
unsigned length = 0, x = node->offset, y = outputOffset;
while(x < sourceSize && y < targetSize && sourceData[x++] == targetData[y++]) length++;
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = SourceCopy;
node = node->next;
}
}
{ //target copy
Node *node = targetTree[symbol];
while(node) {
unsigned length = 0, x = node->offset, y = outputOffset;
while(y < targetSize && targetData[x++] == targetData[y++]) length++;
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = TargetCopy;
node = node->next;
}
//target tree append
node = new Node;
node->offset = outputOffset;
node->next = targetTree[symbol];
targetTree[symbol] = node;
}
{ //target read
if(maxLength < 4) {
maxLength = min((unsigned)Granularity, targetSize - outputOffset);
mode = TargetRead;
}
}
if(mode != TargetRead) targetReadFlush();
switch(mode) {
case SourceRead:
encode(SourceRead | ((maxLength - 1) << 2));
break;
case TargetRead:
//delay write to group sequential TargetRead commands into one
targetReadLength += maxLength;
break;
case SourceCopy:
case TargetCopy:
encode(mode | ((maxLength - 1) << 2));
signed relativeOffset;
if(mode == SourceCopy) {
relativeOffset = maxOffset - sourceRelativeOffset;
sourceRelativeOffset = maxOffset + maxLength;
} else {
relativeOffset = maxOffset - targetRelativeOffset;
targetRelativeOffset = maxOffset + maxLength;
}
encode((relativeOffset < 0) | (abs(relativeOffset) << 1));
break;
}
outputOffset += maxLength;
}
targetReadFlush();
sourceChecksum = ~sourceChecksum;
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
uint32_t outputChecksum = ~modifyChecksum;
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
modifyFile.close();
return true;
}
}
#endif

View File

@@ -1,152 +0,0 @@
#ifndef NALL_BPS_LINEAR_HPP
#define NALL_BPS_LINEAR_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpslinear {
inline void source(const uint8_t *data, unsigned size);
inline void target(const uint8_t *data, unsigned size);
inline bool source(const string &filename);
inline bool target(const string &filename);
inline bool create(const string &filename, const string &metadata = "");
protected:
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
enum : unsigned { Granularity = 1 };
filemap sourceFile;
const uint8_t *sourceData;
unsigned sourceSize;
filemap targetFile;
const uint8_t *targetData;
unsigned targetSize;
};
void bpslinear::source(const uint8_t *data, unsigned size) {
sourceData = data;
sourceSize = size;
}
void bpslinear::target(const uint8_t *data, unsigned size) {
targetData = data;
targetSize = size;
}
bool bpslinear::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
source(sourceFile.data(), sourceFile.size());
return true;
}
bool bpslinear::target(const string &filename) {
if(targetFile.open(filename, filemap::mode::read) == false) return false;
target(targetFile.data(), targetFile.size());
return true;
}
bool bpslinear::create(const string &filename, const string &metadata) {
file modifyFile;
if(modifyFile.open(filename, file::mode::write) == false) return false;
uint32_t modifyChecksum = ~0;
unsigned targetRelativeOffset = 0, outputOffset = 0;
auto write = [&](uint8_t data) {
modifyFile.write(data);
modifyChecksum = crc32_adjust(modifyChecksum, data);
};
auto encode = [&](uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) {
write(0x80 | x);
break;
}
write(x);
data--;
}
};
unsigned targetReadLength = 0;
auto targetReadFlush = [&]() {
if(targetReadLength) {
encode(TargetRead | ((targetReadLength - 1) << 2));
unsigned offset = outputOffset - targetReadLength;
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
}
};
write('B');
write('P');
write('S');
write('1');
encode(sourceSize);
encode(targetSize);
unsigned markupSize = metadata.length();
encode(markupSize);
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
while(outputOffset < targetSize) {
unsigned sourceLength = 0;
for(unsigned n = 0; outputOffset + n < min(sourceSize, targetSize); n++) {
if(sourceData[outputOffset + n] != targetData[outputOffset + n]) break;
sourceLength++;
}
unsigned rleLength = 0;
for(unsigned n = 1; outputOffset + n < targetSize; n++) {
if(targetData[outputOffset] != targetData[outputOffset + n]) break;
rleLength++;
}
if(rleLength >= 4) {
//write byte to repeat
targetReadLength++;
outputOffset++;
targetReadFlush();
//copy starting from repetition byte
encode(TargetCopy | ((rleLength - 1) << 2));
unsigned relativeOffset = (outputOffset - 1) - targetRelativeOffset;
encode(relativeOffset << 1);
outputOffset += rleLength;
targetRelativeOffset = outputOffset - 1;
} else if(sourceLength >= 4) {
targetReadFlush();
encode(SourceRead | ((sourceLength - 1) << 2));
outputOffset += sourceLength;
} else {
targetReadLength += Granularity;
outputOffset += Granularity;
}
}
targetReadFlush();
uint32_t sourceChecksum = crc32_calculate(sourceData, sourceSize);
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
uint32_t outputChecksum = ~modifyChecksum;
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
modifyFile.close();
return true;
}
}
#endif

View File

@@ -1,121 +0,0 @@
#ifndef NALL_BPS_METADATA_HPP
#define NALL_BPS_METADATA_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpsmetadata {
inline bool load(const string &filename);
inline bool save(const string &filename, const string &metadata);
inline string metadata() const;
protected:
file sourceFile;
string metadataString;
};
bool bpsmetadata::load(const string &filename) {
if(sourceFile.open(filename, file::mode::read) == false) return false;
auto read = [&]() -> uint8_t {
return sourceFile.read();
};
auto decode = [&]() -> uint64_t {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = read();
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
};
if(read() != 'B') return false;
if(read() != 'P') return false;
if(read() != 'S') return false;
if(read() != '1') return false;
decode();
decode();
unsigned metadataSize = decode();
char data[metadataSize + 1];
for(unsigned n = 0; n < metadataSize; n++) data[n] = read();
data[metadataSize] = 0;
metadataString = (const char*)data;
return true;
}
bool bpsmetadata::save(const string &filename, const string &metadata) {
file targetFile;
if(targetFile.open(filename, file::mode::write) == false) return false;
if(sourceFile.open() == false) return false;
sourceFile.seek(0);
auto read = [&]() -> uint8_t {
return sourceFile.read();
};
auto decode = [&]() -> uint64_t {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = read();
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
};
uint32_t checksum = ~0;
auto write = [&](uint8_t data) {
targetFile.write(data);
checksum = crc32_adjust(checksum, data);
};
auto encode = [&](uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) {
write(0x80 | x);
break;
}
write(x);
data--;
}
};
for(unsigned n = 0; n < 4; n++) write(read());
encode(decode());
encode(decode());
unsigned sourceLength = decode();
unsigned targetLength = metadata.length();
encode(targetLength);
sourceFile.seek(sourceLength, file::index::relative);
for(unsigned n = 0; n < targetLength; n++) write(metadata[n]);
unsigned length = sourceFile.size() - sourceFile.offset() - 4;
for(unsigned n = 0; n < length; n++) write(read());
uint32_t outputChecksum = ~checksum;
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
targetFile.close();
return true;
}
string bpsmetadata::metadata() const {
return metadataString;
}
}
#endif

View File

@@ -1,219 +0,0 @@
#ifndef NALL_BPS_PATCH_HPP
#define NALL_BPS_PATCH_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpspatch {
inline bool modify(const uint8_t *data, unsigned size);
inline void source(const uint8_t *data, unsigned size);
inline void target(uint8_t *data, unsigned size);
inline bool modify(const string &filename);
inline bool source(const string &filename);
inline bool target(const string &filename);
inline string metadata() const;
inline unsigned size() const;
enum result : unsigned {
unknown,
success,
patch_too_small,
patch_invalid_header,
source_too_small,
target_too_small,
source_checksum_invalid,
target_checksum_invalid,
patch_checksum_invalid,
};
inline result apply();
protected:
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
filemap modifyFile;
const uint8_t *modifyData;
unsigned modifySize;
filemap sourceFile;
const uint8_t *sourceData;
unsigned sourceSize;
filemap targetFile;
uint8_t *targetData;
unsigned targetSize;
unsigned modifySourceSize;
unsigned modifyTargetSize;
unsigned modifyMarkupSize;
string metadataString;
};
bool bpspatch::modify(const uint8_t *data, unsigned size) {
if(size < 19) return false;
modifyData = data;
modifySize = size;
unsigned offset = 4;
auto decode = [&]() -> uint64_t {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = modifyData[offset++];
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
};
modifySourceSize = decode();
modifyTargetSize = decode();
modifyMarkupSize = decode();
char buffer[modifyMarkupSize + 1];
for(unsigned n = 0; n < modifyMarkupSize; n++) buffer[n] = modifyData[offset++];
buffer[modifyMarkupSize] = 0;
metadataString = (const char*)buffer;
return true;
}
void bpspatch::source(const uint8_t *data, unsigned size) {
sourceData = data;
sourceSize = size;
}
void bpspatch::target(uint8_t *data, unsigned size) {
targetData = data;
targetSize = size;
}
bool bpspatch::modify(const string &filename) {
if(modifyFile.open(filename, filemap::mode::read) == false) return false;
return modify(modifyFile.data(), modifyFile.size());
}
bool bpspatch::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
source(sourceFile.data(), sourceFile.size());
return true;
}
bool bpspatch::target(const string &filename) {
file fp;
if(fp.open(filename, file::mode::write) == false) return false;
fp.truncate(modifyTargetSize);
fp.close();
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
target(targetFile.data(), targetFile.size());
return true;
}
string bpspatch::metadata() const {
return metadataString;
}
unsigned bpspatch::size() const {
return modifyTargetSize;
}
bpspatch::result bpspatch::apply() {
if(modifySize < 19) return result::patch_too_small;
uint32_t modifyChecksum = ~0, targetChecksum = ~0;
unsigned modifyOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
auto read = [&]() -> uint8_t {
uint8_t data = modifyData[modifyOffset++];
modifyChecksum = crc32_adjust(modifyChecksum, data);
return data;
};
auto decode = [&]() -> uint64_t {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = read();
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
};
auto write = [&](uint8_t data) {
targetData[outputOffset++] = data;
targetChecksum = crc32_adjust(targetChecksum, data);
};
if(read() != 'B') return result::patch_invalid_header;
if(read() != 'P') return result::patch_invalid_header;
if(read() != 'S') return result::patch_invalid_header;
if(read() != '1') return result::patch_invalid_header;
modifySourceSize = decode();
modifyTargetSize = decode();
modifyMarkupSize = decode();
for(unsigned n = 0; n < modifyMarkupSize; n++) read();
if(modifySourceSize > sourceSize) return result::source_too_small;
if(modifyTargetSize > targetSize) return result::target_too_small;
while(modifyOffset < modifySize - 12) {
unsigned length = decode();
unsigned mode = length & 3;
length = (length >> 2) + 1;
switch(mode) {
case SourceRead:
while(length--) write(sourceData[outputOffset]);
break;
case TargetRead:
while(length--) write(read());
break;
case SourceCopy:
case TargetCopy:
signed offset = decode();
bool negative = offset & 1;
offset >>= 1;
if(negative) offset = -offset;
if(mode == SourceCopy) {
sourceRelativeOffset += offset;
while(length--) write(sourceData[sourceRelativeOffset++]);
} else {
targetRelativeOffset += offset;
while(length--) write(targetData[targetRelativeOffset++]);
}
break;
}
}
uint32_t modifySourceChecksum = 0, modifyTargetChecksum = 0, modifyModifyChecksum = 0;
for(unsigned n = 0; n < 32; n += 8) modifySourceChecksum |= read() << n;
for(unsigned n = 0; n < 32; n += 8) modifyTargetChecksum |= read() << n;
uint32_t checksum = ~modifyChecksum;
for(unsigned n = 0; n < 32; n += 8) modifyModifyChecksum |= read() << n;
uint32_t sourceChecksum = crc32_calculate(sourceData, modifySourceSize);
targetChecksum = ~targetChecksum;
if(sourceChecksum != modifySourceChecksum) return result::source_checksum_invalid;
if(targetChecksum != modifyTargetChecksum) return result::target_checksum_invalid;
if(checksum != modifyModifyChecksum) return result::patch_checksum_invalid;
return result::success;
}
}
#endif

View File

@@ -1,152 +0,0 @@
#ifndef NALL_COMPOSITOR_HPP
#define NALL_COMPOSITOR_HPP
#include <nall/intrinsics.hpp>
namespace nall {
struct compositor {
inline static bool enabled();
inline static bool enable(bool status);
#if defined(PLATFORM_X)
enum class Compositor : unsigned { Unknown, Metacity, Xfwm4 };
inline static Compositor detect();
inline static bool enabled_metacity();
inline static bool enable_metacity(bool status);
inline static bool enabled_xfwm4();
inline static bool enable_xfwm4(bool status);
#endif
};
#if defined(PLATFORM_X)
//Metacity
bool compositor::enabled_metacity() {
FILE *fp = popen("gconftool-2 --get /apps/metacity/general/compositing_manager", "r");
if(fp == 0) return false;
char buffer[512];
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
if(!memcmp(buffer, "true", 4)) return true;
return false;
}
bool compositor::enable_metacity(bool status) {
FILE *fp;
if(status) {
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager true", "r");
} else {
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager false", "r");
}
if(fp == 0) return false;
pclose(fp);
return true;
}
//Xfwm4
bool compositor::enabled_xfwm4() {
FILE *fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
if(fp == 0) return false;
char buffer[512];
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
if(!memcmp(buffer, "true", 4)) return true;
return false;
}
bool compositor::enable_xfwm4(bool status) {
FILE *fp;
if(status) {
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
} else {
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'false'", "r");
}
if(fp == 0) return false;
pclose(fp);
return true;
}
//General
compositor::Compositor compositor::detect() {
Compositor result = Compositor::Unknown;
FILE *fp;
char buffer[512];
fp = popen("pidof metacity", "r");
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Metacity;
pclose(fp);
fp = popen("pidof xfwm4", "r");
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Xfwm4;
pclose(fp);
return result;
}
bool compositor::enabled() {
switch(detect()) {
case Compositor::Metacity: return enabled_metacity();
case Compositor::Xfwm4: return enabled_xfwm4();
default: return false;
}
}
bool compositor::enable(bool status) {
switch(detect()) {
case Compositor::Metacity: return enable_metacity(status);
case Compositor::Xfwm4: return enable_xfwm4(status);
default: return false;
}
}
#elif defined(PLATFORM_WINDOWS)
bool compositor::enabled() {
HMODULE module = GetModuleHandleW(L"dwmapi");
if(module == 0) module = LoadLibraryW(L"dwmapi");
if(module == 0) return false;
auto pDwmIsCompositionEnabled = (HRESULT (WINAPI*)(BOOL*))GetProcAddress(module, "DwmIsCompositionEnabled");
if(pDwmIsCompositionEnabled == 0) return false;
BOOL result;
if(pDwmIsCompositionEnabled(&result) != S_OK) return false;
return result;
}
bool compositor::enable(bool status) {
HMODULE module = GetModuleHandleW(L"dwmapi");
if(module == 0) module = LoadLibraryW(L"dwmapi");
if(module == 0) return false;
auto pDwmEnableComposition = (HRESULT (WINAPI*)(UINT))GetProcAddress(module, "DwmEnableComposition");
if(pDwmEnableComposition == 0) return false;
if(pDwmEnableComposition(status) != S_OK) return false;
return true;
}
#else
bool compositor::enabled() {
return false;
}
bool compositor::enable(bool) {
return false;
}
#endif
}
#endif

View File

@@ -1,126 +0,0 @@
#ifndef NALL_CONFIG_HPP
#define NALL_CONFIG_HPP
#include <nall/file.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
namespace nall {
namespace configuration_traits {
template<typename T> struct is_boolean { enum { value = false }; };
template<> struct is_boolean<bool> { enum { value = true }; };
template<typename T> struct is_signed { enum { value = false }; };
template<> struct is_signed<signed> { enum { value = true }; };
template<typename T> struct is_unsigned { enum { value = false }; };
template<> struct is_unsigned<unsigned> { enum { value = true }; };
template<typename T> struct is_double { enum { value = false }; };
template<> struct is_double<double> { enum { value = true }; };
template<typename T> struct is_string { enum { value = false }; };
template<> struct is_string<string> { enum { value = true }; };
}
class configuration {
public:
enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t };
struct item_t {
uintptr_t data;
string name;
string desc;
type_t type;
inline string get() const {
switch(type) {
case boolean_t: return { *(bool*)data };
case signed_t: return { *(signed*)data };
case unsigned_t: return { *(unsigned*)data };
case double_t: return { *(double*)data };
case string_t: return { "\"", *(string*)data, "\"" };
}
return "???";
}
inline void set(string s) {
switch(type) {
case boolean_t: *(bool*)data = (s == "true"); break;
case signed_t: *(signed*)data = integer(s); break;
case unsigned_t: *(unsigned*)data = decimal(s); break;
case double_t: *(double*)data = fp(s); break;
case string_t: s.trim("\""); *(string*)data = s; break;
}
}
};
vector<item_t> list;
template<typename T>
inline void append(T &data, const char *name, const char *desc = "") {
item_t item = { (uintptr_t)&data, name, desc };
if(configuration_traits::is_boolean<T>::value) item.type = boolean_t;
else if(configuration_traits::is_signed<T>::value) item.type = signed_t;
else if(configuration_traits::is_unsigned<T>::value) item.type = unsigned_t;
else if(configuration_traits::is_double<T>::value) item.type = double_t;
else if(configuration_traits::is_string<T>::value) item.type = string_t;
else item.type = unknown_t;
list.append(item);
}
//deprecated
template<typename T>
inline void attach(T &data, const char *name, const char *desc = "") {
append(data, name, desc);
}
inline virtual bool load(const string &filename) {
string data;
if(data.readfile(filename) == true) {
data.replace("\r", "");
lstring line;
line.split("\n", data);
for(unsigned i = 0; i < line.size(); i++) {
if(auto position = qstrpos(line[i], "#")) line[i][position()] = 0;
if(!qstrpos(line[i], " = ")) continue;
lstring part;
part.qsplit(" = ", line[i]);
part[0].trim();
part[1].trim();
for(unsigned n = 0; n < list.size(); n++) {
if(part[0] == list[n].name) {
list[n].set(part[1]);
break;
}
}
}
return true;
} else {
return false;
}
}
inline virtual bool save(const string &filename) const {
file fp;
if(fp.open(filename, file::mode::write)) {
for(unsigned i = 0; i < list.size(); i++) {
string output;
output.append(list[i].name, " = ", list[i].get());
if(list[i].desc != "") output.append(" # ", list[i].desc);
output.append("\r\n");
fp.print(output);
}
fp.close();
return true;
} else {
return false;
}
}
};
}
#endif

View File

@@ -1,66 +0,0 @@
#ifndef NALL_CRC32_HPP
#define NALL_CRC32_HPP
#include <nall/stdint.hpp>
namespace nall {
const uint32_t crc32_table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) {
return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff];
}
inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) {
uint32_t crc32 = ~0;
for(unsigned i = 0; i < length; i++) {
crc32 = crc32_adjust(crc32, data[i]);
}
return ~crc32;
}
}
#endif

View File

@@ -1,153 +0,0 @@
#ifndef NALL_DIRECTORY_HPP
#define NALL_DIRECTORY_HPP
#include <nall/intrinsics.hpp>
#include <nall/sort.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
#if defined(PLATFORM_WINDOWS)
#include <nall/windows/utf8.hpp>
#else
#include <dirent.h>
#include <stdio.h>
#include <sys/types.h>
#endif
namespace nall {
struct directory {
static bool exists(const string &pathname);
static lstring folders(const string &pathname, const string &pattern = "*");
static lstring files(const string &pathname, const string &pattern = "*");
static lstring contents(const string &pathname, const string &pattern = "*");
};
#if defined(PLATFORM_WINDOWS)
inline bool directory::exists(const string &pathname) {
DWORD result = GetFileAttributes(utf16_t(pathname));
if(result == INVALID_FILE_ATTRIBUTES) return false;
return (result & FILE_ATTRIBUTE_DIRECTORY);
}
inline lstring directory::folders(const string &pathname, const string &pattern) {
lstring list;
string path = pathname;
path.transform("/", "\\");
if(!strend(path, "\\")) path.append("\\");
path.append("*");
HANDLE handle;
WIN32_FIND_DATA data;
handle = FindFirstFile(utf16_t(path), &data);
if(handle != INVALID_HANDLE_VALUE) {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(name);
}
}
while(FindNextFile(handle, &data) != false) {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(name);
}
}
}
FindClose(handle);
}
if(list.size() > 0) list.sort();
for(auto &name : list) name.append("/"); //must append after sorting
return list;
}
inline lstring directory::files(const string &pathname, const string &pattern) {
lstring list;
string path = pathname;
path.transform("/", "\\");
if(!strend(path, "\\")) path.append("\\");
path.append("*");
HANDLE handle;
WIN32_FIND_DATA data;
handle = FindFirstFile(utf16_t(path), &data);
if(handle != INVALID_HANDLE_VALUE) {
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(name);
}
while(FindNextFile(handle, &data) != false) {
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(name);
}
}
FindClose(handle);
}
if(list.size() > 0) list.sort();
return list;
}
inline lstring directory::contents(const string &pathname, const string &pattern) {
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
lstring files = directory::files(pathname, pattern);
for(auto &file : files) folders.append(file);
return folders;
}
#else
inline bool directory::exists(const string &pathname) {
DIR *dp = opendir(pathname);
if(!dp) return false;
closedir(dp);
return true;
}
inline lstring directory::folders(const string &pathname, const string &pattern) {
lstring list;
DIR *dp;
struct dirent *ep;
dp = opendir(pathname);
if(dp) {
while(ep = readdir(dp)) {
if(!strcmp(ep->d_name, ".")) continue;
if(!strcmp(ep->d_name, "..")) continue;
if(ep->d_type & DT_DIR) {
if(wildcard(ep->d_name, pattern)) list.append(ep->d_name);
}
}
closedir(dp);
}
if(list.size() > 0) list.sort();
for(auto &name : list) name.append("/"); //must append after sorting
return list;
}
inline lstring directory::files(const string &pathname, const string &pattern) {
lstring list;
DIR *dp;
struct dirent *ep;
dp = opendir(pathname);
if(dp) {
while(ep = readdir(dp)) {
if(!strcmp(ep->d_name, ".")) continue;
if(!strcmp(ep->d_name, "..")) continue;
if((ep->d_type & DT_DIR) == 0) {
if(wildcard(ep->d_name, pattern)) list.append(ep->d_name);
}
}
closedir(dp);
}
if(list.size() > 0) list.sort();
return list;
}
inline lstring directory::contents(const string &pathname, const string &pattern) {
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
lstring files = directory::files(pathname, pattern);
for(auto &file : files) folders.append(file);
return folders;
}
#endif
}
#endif

View File

@@ -1,115 +0,0 @@
#ifndef NALL_DL_HPP
#define NALL_DL_HPP
//dynamic linking support
#include <nall/intrinsics.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
#include <dlfcn.h>
#elif defined(PLATFORM_WINDOWS)
#include <windows.h>
#include <nall/windows/utf8.hpp>
#endif
namespace nall {
struct library {
bool opened() const { return handle; }
bool open(const char*, const char* = "");
bool open_absolute(const char*);
void* sym(const char*);
void close();
library() : handle(0) {}
~library() { close(); }
library& operator=(const library&) = delete;
library(const library&) = delete;
private:
uintptr_t handle;
};
#if defined(PLATFORM_X)
inline bool library::open(const char *name, const char *path) {
if(handle) close();
handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY);
if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY);
return handle;
}
inline bool library::open_absolute(const char *name) {
if(handle) close();
handle = (uintptr_t)dlopen(name, RTLD_LAZY);
return handle;
}
inline void* library::sym(const char *name) {
if(!handle) return 0;
return dlsym((void*)handle, name);
}
inline void library::close() {
if(!handle) return;
dlclose((void*)handle);
handle = 0;
}
#elif defined(PLATFORM_OSX)
inline bool library::open(const char *name, const char *path) {
if(handle) close();
handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY);
if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY);
return handle;
}
inline bool library::open_absolute(const char *name) {
if(handle) close();
handle = (uintptr_t)dlopen(name, RTLD_LAZY);
return handle;
}
inline void* library::sym(const char *name) {
if(!handle) return 0;
return dlsym((void*)handle, name);
}
inline void library::close() {
if(!handle) return;
dlclose((void*)handle);
handle = 0;
}
#elif defined(PLATFORM_WINDOWS)
inline bool library::open(const char *name, const char *path) {
if(handle) close();
string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll");
handle = (uintptr_t)LoadLibraryW(utf16_t(filepath));
return handle;
}
inline bool library::open_absolute(const char *name) {
if(handle) close();
handle = (uintptr_t)LoadLibraryW(utf16_t(name));
return handle;
}
inline void* library::sym(const char *name) {
if(!handle) return 0;
return (void*)GetProcAddress((HMODULE)handle, name);
}
inline void library::close() {
if(!handle) return;
FreeLibrary((HMODULE)handle);
handle = 0;
}
#else
inline bool library::open(const char*, const char*) { return false; }
inline void* library::sym(const char*) { return 0; }
inline void library::close() {}
#endif
};
#endif

View File

@@ -1,13 +0,0 @@
#ifndef NALL_DSP_HPP
#define NALL_DSP_HPP
#include <algorithm>
#ifdef __SSE__
#include <xmmintrin.h>
#endif
#define NALL_DSP_INTERNAL_HPP
#include <nall/dsp/core.hpp>
#undef NALL_DSP_INTERNAL_HPP
#endif

View File

@@ -1,51 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct Buffer {
double **sample;
uint16_t rdoffset;
uint16_t wroffset;
unsigned channels;
void setChannels(unsigned channels) {
for(unsigned c = 0; c < this->channels; c++) {
if(sample[c]) delete[] sample[c];
}
if(sample) delete[] sample;
this->channels = channels;
if(channels == 0) return;
sample = new double*[channels];
for(unsigned c = 0; c < channels; c++) {
sample[c] = new double[65536]();
}
}
inline double& read(unsigned channel, signed offset = 0) {
return sample[channel][(uint16_t)(rdoffset + offset)];
}
inline double& write(unsigned channel, signed offset = 0) {
return sample[channel][(uint16_t)(wroffset + offset)];
}
inline void clear() {
for(unsigned c = 0; c < channels; c++) {
for(unsigned n = 0; n < 65536; n++) {
sample[c][n] = 0;
}
}
rdoffset = 0;
wroffset = 0;
}
Buffer() {
channels = 0;
}
~Buffer() {
setChannels(0);
}
};
#endif

View File

@@ -1,167 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
#include <math.h>
#include <nall/stdint.hpp>
namespace nall {
//precision: can be float, double or long double
#define real float
struct DSP;
struct Resampler {
DSP &dsp;
real frequency;
virtual void setFrequency() = 0;
virtual void clear() = 0;
virtual void sample() = 0;
Resampler(DSP &dsp) : dsp(dsp) {}
};
struct DSP {
enum class ResampleEngine : unsigned {
Nearest,
Linear,
Cosine,
Cubic,
Hermite,
Average,
Sinc,
};
inline void setChannels(unsigned channels);
inline void setPrecision(unsigned precision);
inline void setFrequency(real frequency); //inputFrequency
inline void setVolume(real volume);
inline void setBalance(real balance);
inline void setResampler(ResampleEngine resamplingEngine);
inline void setResamplerFrequency(real frequency); //outputFrequency
inline void sample(signed channel[]);
inline bool pending();
inline void read(signed channel[]);
inline void clear();
inline DSP();
inline ~DSP();
protected:
friend class ResampleNearest;
friend class ResampleLinear;
friend class ResampleCosine;
friend class ResampleCubic;
friend class ResampleAverage;
friend class ResampleHermite;
friend class ResampleSinc;
struct Settings {
unsigned channels;
unsigned precision;
real frequency;
real volume;
real balance;
//internal
real intensity;
real intensityInverse;
} settings;
Resampler *resampler;
inline void write(real channel[]);
#include "buffer.hpp"
Buffer buffer;
Buffer output;
inline void adjustVolume();
inline void adjustBalance();
inline signed clamp(const unsigned bits, const signed x);
};
#include "resample/nearest.hpp"
#include "resample/linear.hpp"
#include "resample/cosine.hpp"
#include "resample/cubic.hpp"
#include "resample/hermite.hpp"
#include "resample/average.hpp"
#include "resample/sinc.hpp"
#include "settings.hpp"
void DSP::sample(signed channel[]) {
for(unsigned c = 0; c < settings.channels; c++) {
buffer.write(c) = (real)channel[c] * settings.intensityInverse;
}
buffer.wroffset++;
resampler->sample();
}
bool DSP::pending() {
return output.rdoffset != output.wroffset;
}
void DSP::read(signed channel[]) {
adjustVolume();
adjustBalance();
for(unsigned c = 0; c < settings.channels; c++) {
channel[c] = clamp(settings.precision, output.read(c) * settings.intensity);
}
output.rdoffset++;
}
void DSP::write(real channel[]) {
for(unsigned c = 0; c < settings.channels; c++) {
output.write(c) = channel[c];
}
output.wroffset++;
}
void DSP::adjustVolume() {
for(unsigned c = 0; c < settings.channels; c++) {
output.read(c) *= settings.volume;
}
}
void DSP::adjustBalance() {
if(settings.channels != 2) return; //TODO: support > 2 channels
if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
}
signed DSP::clamp(const unsigned bits, const signed x) {
const signed b = 1U << (bits - 1);
const signed m = (1U << (bits - 1)) - 1;
return (x > m) ? m : (x < -b) ? -b : x;
}
void DSP::clear() {
buffer.clear();
output.clear();
resampler->clear();
}
DSP::DSP() {
setResampler(ResampleEngine::Hermite);
setResamplerFrequency(44100.0);
setChannels(2);
setPrecision(16);
setFrequency(44100.0);
setVolume(1.0);
setBalance(0.0);
clear();
}
DSP::~DSP() {
if(resampler) delete resampler;
}
#undef real
}
#endif

View File

@@ -1,72 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleAverage : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
inline void sampleLinear();
ResampleAverage(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleAverage::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleAverage::clear() {
fraction = 0.0;
}
void ResampleAverage::sample() {
//can only average if input frequency >= output frequency
if(step < 1.0) return sampleLinear();
fraction += 1.0;
real scalar = 1.0;
if(fraction > step) scalar = 1.0 - (fraction - step);
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) += dsp.buffer.read(c) * scalar;
}
if(fraction >= step) {
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) /= step;
}
dsp.output.wroffset++;
fraction -= step;
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) = dsp.buffer.read(c) * fraction;
}
}
dsp.buffer.rdoffset++;
}
void ResampleAverage::sampleLinear() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -1);
real b = dsp.buffer.read(n, -0);
real mu = fraction;
channel[n] = a * (1.0 - mu) + b * mu;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -1,44 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleCosine : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleCosine(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleCosine::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleCosine::clear() {
fraction = 0.0;
}
void ResampleCosine::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -1);
real b = dsp.buffer.read(n, -0);
real mu = fraction;
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
channel[n] = a * (1.0 - mu) + b * mu;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -1,50 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleCubic : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleCubic(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleCubic::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleCubic::clear() {
fraction = 0.0;
}
void ResampleCubic::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -3);
real b = dsp.buffer.read(n, -2);
real c = dsp.buffer.read(n, -1);
real d = dsp.buffer.read(n, -0);
real mu = fraction;
real A = d - c - a + b;
real B = a - b - A;
real C = c - a;
real D = b;
channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -1,62 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleHermite : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleHermite(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleHermite::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleHermite::clear() {
fraction = 0.0;
}
void ResampleHermite::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -3);
real b = dsp.buffer.read(n, -2);
real c = dsp.buffer.read(n, -1);
real d = dsp.buffer.read(n, -0);
const real tension = 0.0; //-1 = low, 0 = normal, +1 = high
const real bias = 0.0; //-1 = left, 0 = even, +1 = right
real mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
mu1 = fraction;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

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