Compare commits

...

161 Commits
v105 ... v107

Author SHA1 Message Date
Tim Allen
7786206a4f Update to bsnes v107 release.
[This is specifically a release of bsnes, not the whole higan suite, even though
it contains all the higan source. -Ed.]

byuu says:

Today I am posting the first release of the new bsnes emulator.

bsnes is designed to be a revival of the classic bsnes design, focusing
specifically on performance and ease of use for SNES emulation.

In addition to all of the features of higan, bsnes supports the following
features:

  - 300% faster (than higan) scanline-based, multi-threaded graphics renderer
  - option to disable sprite limits in games
  - option to enable hires mode 7 graphics
  - option to enable more accurate pixel-based graphics renderer
  - option to overclock SuperFX games by up to 800%
  - periodic auto-saving of game save RAM
  - save state manager with state screenshots
  - several new save state hotkeys such as increment/decrement slot#
  - option to auto-save states when unloading a game or closing the emulator
  - option to auto-load aforementioned states when loading games
  - save state undo and redo support (with associated hotkeys)
  - speed override modes (50%, 75%, 100%, 150%, 200%)
  - recent games list
  - frame advance mode
  - screenshot hotkey
  - path selection for games, patches, saves, cheats, states, and screenshots
  - dynamic video, audio, input driver changes
  - direct loading and playing of games without the use of the higan library
  - ZIP archive and multiple file extension support for games
  - firmware folder for unappended coprocessor firmware (see documentation for
    more)
  - compatibility with sd2snes and Snes9X MSU1 game file naming
  - compatibility with higan gamepaks (game folders)
  - soft-patching support for both BPS and IPS patches
  - menubar that does not pause emulation when entered
  - video pixel shaders (requires OpenGL 3.2)
  - built-in game database with over 1,200 games to ensure perfect memory
    mapping
  - (Linux, BSD only:) audio dynamic rate control to eliminate stuttering
  - and much more!

The one feature I regret not being able to support in this release is Windows
dynamic rate control. I put in my best attempt, but XAudio2's API is simply not
fine-grained enough, and the WASAPI driver is not mature enough. I hope that DRC
support can be added to the Windows port in the near future, and I would like to
offer a large cash bounty to anyone who can help me make this happen.
2019-02-22 17:48:52 +11:00
Tim Allen
fbc1571889 Update to v106r85 release.
byuu says:

The bad instruction was due to the instruction before it fetching one
too many bytes. Didn't notice right away as the disassembler got it
right.

The register map was incorrect on the active 16-bit flags.

I fixed and improved some other things along those lines. Hooked up some
basic KnGE (VPU) timings, made it print out VRAM and some of the WRAM
onto the screen each frame, tried to drive Vblank and Hblank IRQs, but
... I don't know for sure what vector addresses they belong to.

MAME says "INT4" for Vblank, and says nothing for Hblank. I am wildly
guessing INT4==SWI 4==0xffff10, but ... I have no idea. I'm also not
emulating the interrupts properly based on line levels, I'm just firing
on the 0→1 transitions. Sounds like Vblank is more nuanced too, but I
guess we'll see.

Emulation is running further along now, even to the point of it
successfully enabling the KnGE IRQs, but VRAM doesn't appear to get much
useful stuff written into it yet.

I reverted the nall/primitive changes, so request for testing is I guess
rescinded, for whatever it was worth.
2019-01-22 11:26:20 +11:00
Tim Allen
53843934c0 Update to v106r84 release.
byuu says:

Changelog:

  - fixed a few TLCS900H CPU and disassembler bugs
  - hooked up a basic Neo Geo Pocket emulator skeleton and memory map;
    can run a few instructions from the BIOS
  - emulated the flash memory used by Neo Geo Pocket games
  - added sourcery to the higan source archives
  - fixed ternary expressions in sfc/ppu-fast [hex_usr]
2019-01-21 16:27:24 +11:00
Tim Allen
37b610da53 Update to v106r83 release.
byuu says:

Changelog:

  - reverted nall/inline-if.hpp usage for now, since the
    nall/primitives.hpp math operators still cast to (u)int64_t
  - improved nall/primitives.hpp more; integer8 x = -128; print(-x) will
    now print 128 (unary operator+ and - cast to (u)int64_t)
  - renamed processor/lr35902 to processor/sm83; after the Sharp SM83
    CPU core [gekkio discovered the name]
  - a few bugfixes to the TLCS900H CPU core
  - completed the disassembler for the TLCS900H core

As a result of reverting most of the inline if stuff, I guess the
testing priority has been reduced. Which is probably a good thing,
considering I seem to have a smaller pool of testers these days.

Indeed, the TLCS900H core has ended up at 131KiB compared to the M68000
core at 128KiB. So it's now the largest CPU core in all of higan. It's
even more ridiculous because the M68000 core would ordinarily be quite a
bit smaller, had I not gone overboard with the extreme templating to
reduce instruction decoding overhead (you kind of have to do this for
RISC CPUs, and the inverted design of the TLCS900H kind of makes it
infeasible to do the same there.)

This CPU core is bound to have dozens of extremely difficult CPU bugs,
and there's no easy way for me to test them. I would greatly appreciate
any help in looking over the core for bugs. A fresh pair of eyes to spot
a mistake could save me up to several days of tedious debugging work.

The core still isn't ready to actually be tested: I have to hook up
cartridge loading, a memory bus, interrupts, timers, and the micro DMA
controller before it's likely that anything happens at all.
2019-01-19 12:34:17 +11:00
Tim Allen
2d9ce59e99 Update to v106r82 release.
byuu says:

Half of the disassembler is implemented now. Well, the decoding half
anyway. I'm splitting the decoding and string building into separate
components this time around, on account of the instruction encoding
being in reverse order. The string building portion hasn't been written
yet, either.

We're up to 112KiB now, compared to 128KiB for the 68K.
2019-01-18 10:59:49 +11:00
Tim Allen
559a6585ef Update to v106r81 release.
byuu says:

First 32 instructions implemented in the TLCS900H disassembler. Only 992
to go!

I removed the use of anonymous namespaces in nall. It was something I
rarely used, because it rarely did what I wanted.

I updated all nested namespaces to use C++17-style namespace Foo::Bar {}
syntax instead of classic C++-style namespace Foo { namespace Bar {}}.

I updated ruby::Video::acquire() to return a struct, so we can use C++17
structured bindings. Long term, I want to get away from all functions
that take references for output only. Even though C++ botched structured
bindings by not allowing you to bind to existing variables, it's even
worse to have function calls that take arguments by reference and then
write to them. From the caller side, you can't tell the value is being
written, nor that the value passed in doesn't matter, which is terrible.
2019-01-16 13:02:24 +11:00
Tim Allen
25145f59cc Update to v106r80 release.
byuu says:

Any usage of natural and integer cast to 64-bit math operations now.
Hopefully this will be the last of the major changes for a bit on
nall/primitives, at least until serious work begins on removing implicit
conversion to primitive types.

I also completed the initial TLCS900H core, sans SWI (kind of a ways off
from support interrupts.) I really shouldn't say completed, though. The
micro DMA unit is missing, interrupt priority handling is missing,
there's no debugger, and, of course, there's surely dozens of absolutely
critical CPU bugs that are going to be an absolute hellscape nightmare
to track down.

It was a damn shame, right up until the very last eight instructions,
[CP|LD][I|D](R), the instruction encoding was consistent. Of course,
there could be other inconsistencies that I missed. In fact, that's
somewhat likely ... sigh.
2019-01-16 00:09:50 +11:00
Tim Allen
17fc6d8d51 Update to v106r79 release.
byuu says:

This WIP is just work on nall/primitives ...

Basically, I'm coming to the conclusion that it's just not practical to
try and make Natural/Integer implicitly castable to primitive signed and
unsigned integers. C++ just has too many edge cases there.

I also want to get away from the problem of C++ deciding that all math
operations return 32-bit values, unless one of the parameters is 64-bit,
in which case you get a 64-bit value. You know, so things like
array[-1] won't end up accessing the 4 billionth element of the array.
It's nice to be fancy and minimally size operations (eg 32-bit+32-bit =
33-bit), but it's just too unintuitive. I think all
Natural<X>+Natural<Y> expessions should result in a Natural<64> (eg
natural) type.

nall/primitives/operators.hpp has been removed, and new
Natural<>Natural / Integer<>Integer casts exist. My feeling is that
signed and unsigned types should not be implicitly convertible where
data loss can occur. In the future, I think an integer8*natural8 is
fine to return an integer64, and the bitwise operators are probably all
fine between the two types. I could probably add
(Integer,Natural)+Boolean conversions as well.

To simplify expressions, there are new user-defined literals for _b
(boolean), _n (natural), _i (integer), _r (real), _n# (eg _n8),
_i# (eg _i8), _r# (eg _r32), and _s (nall::string).

In the long-term, my intention is to make the conversion and cast
constructors explicit for primitive types, but obviously that'll shatter
most of higan, so for now that won't be the case.

Something I can do in the future is allow implicit conversion and
casting to (u)int64_t. That may be a nice balance.
2019-01-15 15:33:20 +11:00
Tim Allen
6871e0e32a Update to v106r78 release.
byuu says:

I've implemented a lot more TLCS900H instructions. There are currently
20 missing spots, all of which are unique instructions (well, MINC and
MDEC could be considered pairs of 3 each), from a map of 1024 slots.

After that, I have to write the disassembler. Then the memory bus. Then
I get to start the fun process of debugging this monstrosity.

Also new is nall/inline-if.hpp. Note that this file is technically a war
crime, so be careful when opening it. This replaces ternary() from the
previous WIP.
2019-01-14 17:16:28 +11:00
Tim Allen
bb1dd8c609 Update to v106r77 release.
byuu says:

So this turned out to be a rather unproductive ten-hour rabbit hole, but
...

I reworked nall/primitives.hpp a lot. And because the changes are
massive, testing of this WIP for regressions is critically important. I
really can't stress that enough, we're almost certainly going to have
some hidden regressions here ...

We now have a nall/primitives/ subfolder that splits up the classes into
manageable components. The bit-field support is now shared between both
Natural and Integer. All of the assignment operator overloads are now
templated and take references instead of values. Things like the
GSU::Register class are non-copyable on account of the function<>
object inside of it, and previously only operator= would work with
classes like that.

The big change is nall/primitives/operators.hpp, which is a really
elaborate system to compute the minimum number of bits needed for any
operation, and to return a Natural<T> or Integer<T> when one or both of
the arguments are such a type.

Unfortunately, it doesn't really work yet ... Kirby's Dream Land 3
breaks if we include operators.hpp. Zelda 3 runs fine with this, but I
had to make a huge amount of core changes, including introducing a new
ternary(bool, lhs, rhs) function to nall/algorithm to get past
Natural<X> and Natural<Y> not being equivalent (is_integral types get a
special exemption to ternary ?: type equivalence, yet it's impossible to
simulate with our own classes, which is bullshit.) The horrifying part
is that ternary() will evaluate both lhs and rhs, unlike ?:

I converted some of the functions to test ? uint(x) : uint(y), and
others to ternary(test, x, y) ... I don't have a strong preference
either way yet.

But the part where things may have gotten broken is in the changes to
where ternary() was placed. Some cases like in the GBA PPU renderer, it
was rather unclear the order of evaluations, so I may have made a
mistake somewhere.

So again, please please test this if you can. Or even better, look over
the diff.

Longer-term, I'd really like the enable nall/primitives/operators.hpp,
but right now I'm not sure why Kirby's Dream Land 3 is breaking. Help
would be appreciated, but ... it's gonna be really complex and difficult
to debug, so I'm probably gonna be on my own here ... sigh.
2019-01-13 17:25:14 +11:00
Tim Allen
c9f7c6c4be Update to v106r76 release.
byuu says:

I added some useful new functions to nall/primitives:

    auto Natural<T>::integer() const -> Integer<T>;
    auto Integer<T>::natural() const -> Natural<T>;

These let you cast between signed and unsigned representation without
having to care about the value of T (eg if you take a Natural<T> as a
template parameter.) So for instance when you're given an unsigned type
but it's supposed to be a sign-extended type (example: signed
multiplication), eg Natural<T> → Integer<T>, you can just say:

    x = y.integer() * z.integer();

The TLCS900H core gained some more pesky instructions such as DAA, BS1F,
BS1B.

I stole an optimization from RACE for calculating the overflow flag on
addition. Assuming: z = x + y + c;

    Before: ~(x ^ y) & (x ^ z) & signBit;
    After: (x ^ z) & (y ^ z) & signBit;

Subtraction stays the same. Assuming: z = x - y - c;

    Same: (x ^ y) & (x ^ z) & signBit;

However, taking a speed penalty, I've implemented the carry computation
in a way that doesn't require an extra bit.

Adding before:

    uint9 z = x + y + c;
    c = z & 0x100;

Subtracting before:

    uint9 z = x - y - c;
    c = z & 0x100;

Adding after:

    uint8 z = x + y + c;
    c = z < x || z == x && c;

Subtracting after:

    uint8 z = x - y - c;
    c = z > x || z == x && c;

I haven't been able to code golf the new carry computation to be any
shorter, unless I include an extra bit, eg for adding:

    c = z < x + c;

But that defeats the entire point of the change. I want the computation
to work even when T is uintmax_t.

If anyone can come up with a faster method, please let me know.

Anyway ... I also had to split off INC and DEC because they compute
flags differently (word and long modes don't set flags at all, byte mode
doesn't set carry at all.)

I also added division by zero support, although I don't know if it's
actually hardware accurate. It's what other emulators do, though.
2019-01-11 12:51:18 +11:00
Tim Allen
95d0020297 Update to v106r75 release.
byuu says:

So tired ... so much left to do still ... sigh.

If someone's up for some code golf, open to suggestions on how to handle
the INTNEST control register. It's the only pure 16-bit register on the
system, and it breaks my `map`/`load`/`store<uint8,16,32>` abstraction.
Basically what I suspect happens is when you access INTNEST in 32-bit
mode, the upper 16-bits are just undefined (probably zero.) But
`map<uint32>(INTNEST)` must return a uint32& or nothing at all. So for the
time being, I'm just making store(ControlRegister) check if it's the
INTNEST ID, and clearing the upper bits of the written byte in that
case. It's hacky, but ... it's the best I can think of.

I added LDX, which is a 900H-only instruction, and the control register
map is for the 900/H CPU. I found the detailed differences between the
CPUs, and it doesn't look likely that I'm gonna support the 900 or
900/H1 at all. Not that there was a reason to anyway, but it's nice to
support more stuff when possible. Oh well.

The 4-byte instruction fetch queue is going to have to get implemented
inside fetch, or just not implemented at all ... not like I'd be able to
figure out the details of it anyway.

The manual isn't clear on how the MULA flags are calculated, but since
MUL doesn't set any flags, I assume the flags are based on the addition
after the multiplication, eg:

    uint32 a = indirect<int16>(XDE) * indirect<int16>(XHL);
    uint32 b = reg16; //opcode parameter
    uint32 c = a + b; //flags set based on a+b

No idea if it's right or not. It doesn't set carry or half-carry, so
it's not just simply the same as calling algorithmAdd.

Up to almost 70KB, not even halfway done, don't even have a disassembler
started yet. There's a real chance this could overtake the 68K for the
biggest CPU core in higan, although at this point I'm still thinking the
68K will end up larger.
2019-01-10 13:21:18 +11:00
Tim Allen
41148b1024 Update to v106r74 release.
byuu says:

So I spent the better part of eight hours refactoring the TLCS900H core
to be more flexible in light of new edge cases.

I'm now including the size information inside of the types (eg
Register<Byte>, Memory<Word>) rather than as parameters to the
instruction handlers. This allows me to eg implement RETI without
needing template arguments in all the instructions. pop(SR), pop(PC) can
deduce how much to pop off of the stack. It's still highly templated,
but not unrolling the 3-bit register indexes and instead going through
the switch table to access registers is going to hurt the performance a
good deal.

A benefit of this is that Register{A} != Register{WA} != Register{XWA}
anymore, despite them sharing IDs.

I also renamed read/write to load/store for the CPU core, because
implicit conversions are nasty. They all call the virtual read/write.

I added more instructions, improved the memory addressing mode support,
and some other things.

I got rid of Byte, Word, Long because there's too many alternate sizes
needed: int8, int16, uint24, etc.

Ran into a really annoying C++ case ...

    struct TLCS900H {
      template<typename T> auto store(Register<T> target, T source) -> void;
    };

If you call store(Register<uint32>(x), uint16(y)); it errors out since
the T types don't match. But you can't specialize it:

    template<typename T, typename U> auto store(Register<T>, U) -> void;
    template<typename U> auto TLCS900H::store<uint32, U>(Register<uint32>, U) -> void;

Because somehow it's 2019 and we still can't do partial template
specialization inside classes ...

So as a result, I had to make T source be type uint32 even for
Register<uint8> and Register<uint16>. Doesn't matter too much, just
annoying.
2019-01-09 10:36:03 +11:00
Tim Allen
dbee893408 Update to v106r73 release.
byuu says:

This probably won't fix the use of register yet (I imagine ruby and hiro
will complain now), but ... oh well, it's a start. We'll get it
compiling again eventually.

I added JP, JR, JRL, LD instructions this time around. I'm also starting
to feel that Byte, Word, Long labels for the TLCS900H aren't really
working. There's cases of needing uint24, int8, int16, ... it may just
be better to name the types instead of trying to be fancy.

At this point, all of the easy instructions are in. Now it's down to a
whole lot of very awkward bit-manipulation and special-use instructions.
Sigh.
2019-01-07 18:59:04 +11:00
Tim Allen
cb86cd116c Update to v106r72 release.
byuu says:

For this WIP, I added more TLCS900H instructions. All of the
ADC,ADD,SBB/SBC,SUB,AND,OR,XOR.CP,PUSH,POP instructions are in.

Still an incredible amount of work left to do on this core ... it has all kinds
of novel instructions that aren't on any other processors.

Still no disassembler support yet, so I can't even test what I'm doing. Fun!
2019-01-05 18:04:27 +11:00
Tim Allen
1a889ae232 Update to v106r71 release.
byuu says:

I started working on the Toshiba TLCS900H CPU core today.

It's basically, "what if we took the Z80, added in 32-bit support, added
in SPARC register windows, added a ton of additional addressing modes,
added control registers, and added a bunch of additional instructions?"
-- or in other words, it's basically hell for me.

It took several hours just to wrap my head around the way the opcode
decoder needed to function, but I think I have a decent strategy for
implementing it now.

I should have all of the first-byte register/memory address decoding in
place, although I'm sure there's lots of bugs. I don't have anything in
the way of a disassembler yet.
2019-01-05 11:35:26 +11:00
Tim Allen
79be6f2355 Update to v106r70 release.
byuu says:

Changelog:

  - Interface::displays() -> vector<Display> → Interface::display() -> Display
  - <Platform::videoRefresh(display>, ...) → <Platform::videoFrame>(...)
  - <Platform::audioSample>(...) → <Platform::audioFrame>(...)
  - higan, icarus: use AboutDialog class instead of ad-hoc
    implementations
      - about dialog is now modal, but now has a clickable website URL
  - icarus: reverted if constexpr for now
  - MSX: implemented basic CPU, VDP support

I took out the multiple displays support thing because it was never
really implemented fully (Emulator::Video and the GUIs both ignored it)
or used anyway. If it ends up necessary in the future, I'll worry about
it then.

There's enough MSX emulation now to run Mr. Do! without sound or input.
I'm shipping higan with C-BIOS 0.29a, although it likely won't be good
enough in the future (eg it can't do BASIC, floppy disk, or cassette
loading.) I have keyboard and (not working) AY-3-8910 support in a
different branch, so that won't take too long to implement. Main problem
is naming all the darned keyboard keys. I think I need to change
settings.bml's input mapping lines so that the key names are values
instead of node names, so that any characters can appear inside of them.

It turns out my MSX set uses .rom for the file extensions ... gods. So,
icarus can't really import them like this. I may have to re-design
icarus' importer to stop caring about the file extension and instead ask
you what kind of games you are importing. There's no way icarus can
heuristically guess what systems the images belong to, because many
systems don't have any standardized magic bytes.

I'm struggling with where to put SG-1000, SC-3000, ColecoVision, Coleco
Adam stuff. I think they need to be split to two separate higan
subfolders (sg and cv, most likely ...) The MS/GG share a very
customized and extended VDP that the other systems don't have. The Sega
and Coleco older hardware share the same TMS9918 as the MSX, yet have
very different memory maps and peripherals that I don't want to mix
together. Especially if we start getting into the computer-variants
more.
2019-01-03 21:05:20 +11:00
Tim Allen
cac3858f65 Document that we now require GCC7 and/or C++17 features. 2019-01-03 20:43:08 +11:00
Tim Allen
1fd6d983da Build with Ubuntu LTS instead of Debian Stable.
Debian has served us well, but byuu would like to start using C++17 features
which generally requires GCC7. Debian Stable only has GCC6 right now, while
Ubuntu LTS has the required version, so that should get things going again.
2019-01-03 20:37:30 +11:00
Tim Allen
aaf094e7c4 Update to v106r69 release.
byuu says:

The biggest change was improving WonderSwan emulation. With help from
trap15, I tracked down a bug where I was checking the wrong bit for
reverse DMA transfers. Then I also emulated VTOTAL to support variable
refresh rate. Then I improved HyperVoice emulation which should be
unsigned samples in three of four modes. That got Fire Lancer running
great. I also rewrote the disassembler. The old one disassembled many
instructions completely wrong, and deviated too much from any known x86
syntax. I also emulated some of the quirks of the V30 (two-byte POP into
registers fails, SALC is just XLAT mirrored, etc) which probably don't
matter unless someone tries to run code to verify it's a NEC CPU and not
an Intel CPU, but hey, why not?

I also put more work into the MSX skeleton, but it's still just a
skeleton with no real emulation yet.
2019-01-02 10:52:08 +11:00
Tim Allen
3159285eaa Update to v106r68 release.
byuu says:

Changelog:

  - nall: converted range, iterator, vector to 64-bit
  - added (very poor) ColecoVision emulation (including Coleco Adam
    expansion)
  - added MSX skeleton
  - added Neo Geo Pocket skeleton
  - moved audio,video,resource folders into emulator folder
  - SFC heuristics: BS-X Town cart is "ZBSJ" [hex_usr]

The nall change is for future work on things like BPA: I need to be able
to handle files larger than 4GB. It is extremely possible that there are
still some truncations to 32-bit lurking around, and even more
disastrously, possibly some -1s lurking that won't sign-extend to
`(uint64_t)0-1`. There's a lot more classes left to do: `string`,
`array_view`, `array_span`, etc.
2018-12-22 21:28:15 +11:00
Tim Allen
90da691717 Update to v106r67 release.
byuu says:

Changelog:

  - added all pre-requisite to make install rule (note: only for higan,
    icarus so far)
  - added SG-1000 emulation
  - added SC-3000 emulation (no keyboard support yet)
  - added MS graphics mode 1 emulation (SC-1000)
  - added MS graphics mode 2 emulation (F-16 Fighter)
  - improve Audio::process() to prevent a possible hang
  - higan: repeat monaural audio to both left+right speakers
  - icarus: add heuristics for importing MSX games (not emulated in
    higan yet in this WIP)
  - added DC bias removal filter [jsd1982]
  - improved Audio::Stream::reset() [jsd1982]

I was under the impression that the 20hz highpass filter would have
removed DC bias ... if not, then I don't know why I added that filter to
all of the emulation cores that have it. In any case, if anyone is up
for helping me out ... if we could analyze the output with and without
the DC bias filter to see if it's actually helping, then I'll enable it
if it is. To enable it, edit
higan/audio/stream.cpp::addDCRemovalFilter() and remove the return
statement at the top of the function.
2018-12-21 11:01:14 +11:00
Tim Allen
598076e400 Fix typo in CI script, introduced in commit 23dd28. 2018-12-20 19:58:33 +11:00
Tim Allen
075f540ec4 The libretro core is broken after v106, we know it's broken, no need to test. 2018-12-20 16:12:43 +11:00
Tim Allen
41eccf6ec4 Update .gitignore.
At some point, higan moved system metadata from higan/profile to higan/systems.
Also, higan's Super Famicom core added config options of its own, which it now
writes out.
2018-12-20 12:15:34 +11:00
Tim Allen
4c4e79aa0e Update to v106r66 release.
byuu says:

Changelog:

  - moved to GCC 8.2 and C++17
  - fixed compilation under FreeBSD 12.0
  - don't read beyond the file size in
    SuperFamicom::Cartridge::loadMemory
  - add missing I/O cycle HuC6280::instructionImmediate
  - serialize Mega Drive's Game Genie state
  - serialize SPC7110::Thread information
  - enable 30-bit color depth support under the GLX/OpenGL 2.0 driver
    (doesn't work with OpenGL 3.2 yet)

The 30-bit color depth option isn't super useful, but why not? I need to
update ruby to detect that the display is actually capable of it before
exposing an option that can result in the driver failing to initialize,
however.
2018-12-20 11:55:47 +11:00
Tim Allen
0b44399c0a docs: Review and update docs for v107.
Changes include:

- The "Library" menu was replaced with the "Systems" menu
- The "Settings" menu was reorganised
- Game Boy rumble is now under the MBC5 "controller" for the cartridge "port",
  instead of being presented as a part of the base console
- Import instructions now mention that icarus ships with some firmware files,
  and describe the "Firmware" directory that icarus will use for firmware
  it needs.
- Apparently the correct name is "MSU1", not "MSU-1"
- v107 changes the way MSU1 data is stored in game folders
- PowerFest '94 import instructions removed, since I can't get it to work
  with v107
- Links to the official forum have been replaced with links to the unofficial
  forum archive, since the official forum is shutting down
- Links to Mercurial Magic updated to point at qwertymodo's archive, since
  hex_usr is no longer developing it
- Links to nSide updated, since hex_usr no longer uses GitHub.
- Windows build instructions now describe a compiler that is actually
  maintained, instead of stale TDM64-GCC.
- Linux build instructions now mention higan requires SDL 2.0.
- minor wording changes, typos, broken links fixed, etc.
2018-11-16 16:09:30 +11:00
Tim Allen
23dd28952b Update build instructions.
The latest WIP renames icarus/database and icarus/firmware to
icarus/Database and icarus/Firmware (in title case).

Also, somewhere along the line we stopped building icarus for higan/Linux,
which seems an oversight.
2018-10-04 22:17:49 +10:00
Tim Allen
03b06257d3 Update to v106r65 release.
byuu says:

This synchronizes bsnes/higan with many recent internal nall changes.

This will be the last WIP until I am situated in Japan. Apologies for the
bugfixes that didn't get applied yet, I ran out of time.
2018-10-04 20:12:11 +10:00
Tim Allen
336d20123f Update to v106r64 release.
byuu says:

Changelog:

  - sfc: completed BS Memory Cassette emulation (sans bugs, of course --
    testing appreciated)
  - bsnes: don't strip - on MSU1 track names in game ROM mode
    [hex_usr]

I'm going with "metadata.bml" for the flash metadata filename for the
time being, but I'll say that it's subject to change. I'll have to make
a new extension for it to be supported with bsnes.
2018-09-13 21:13:00 +10:00
Tim Allen
c58169945c Update to v106r63 release.
byuu says:
Changelog:

  - gb/mbc7: rewrote the 93LCx6 EEPROM emulation
  - sfc/slot/bsmemory: rewrote the flash emulation for Satellaview
    cartridges

As of this release, flash-based BS Memory cartridges will be writable.
So without the bsnes patch to disable write limits, some games will lock
out after a few plays.
2018-09-11 21:16:58 +10:00
Tim Allen
c2d0ed4ca8 Update to v106r62 release.
byuu says:

Changelog:

  - sfc/cx4: added missing instructions [info from Overload]
  - sfc/cx4: added instruction cache emulation [info from ikari]
  - sfc/sa1: don't let CPU access SA1-only I/O registers, and vice versa
  - sfc/sa1: fixed IRQs that were broken from the recent WIP
  - sfc/sa1: significantly improved bus conflict emulation
      - all tests match hardware now, other than HDMA ROM↔ROM, which
        is 0.5 - 0.8% too fast
  - sfc/cpu: fixed a bug with DMA→CPU alignment timing
  - sfc/cpu: removed the DMA pipe; performs writes on the same cycles as
    reads [info from nocash]
  - sfc/memory: fix a crashing bug due to not clearing Memory size field
    [hex_usr]
  - bsnes/gb: use .rtc for real-time clock file extensions on the Game
    Boy [hex_usr]
  - ruby/cgl: compilation fix [Sintendo]

Now let's see if I can accept being off by ~0.65% on one of twelve SA1
timing tests for the time being and prioritize much more important
things or not.
2018-09-10 12:11:19 +10:00
Tim Allen
3d34517f3e Update to v106r61 release.
byuu says:

This release adds ikari's Cx4 notes to bsnes. It fixes the MMX2 intro's
boss fight sequence to be frame perfect to real hardware. It's also very
slightly faster than before.

I've also added an option to toggle the CPU↔coprocessor cycle
synchronization to the emulation settings panel, so you don't have to
recompile to get the more accurate SA1 timings. I'm most likely going to
default this to disabled in bsnes, and *maybe* enabled in higan out of
the box.

StaticRAM (wasn't used) and MappedRAM are gone from the Super Famicom
core. Instead, there's now ReadableMemory, WritableMemory, and
ProtectedMemory (WritableMemory with a toggle for write protection.)
Cartridge::loadMap now takes a template Memory object, which bypasses an
extra virtual function call on memory accesses, but it doesn't really
impact speed much. Whatever.
2018-09-04 15:44:35 +10:00
Tim Allen
a3e0f6da25 Update to v106r60 release.
byuu says:

I added (imperfect) memory conflict timing to the SA1.

Before:

  - WRAM↔↔ROM ran 7% too fast
  - ROM↔↔ROM ran 100% too fast
  - WRAM↔↔IRAM ran 7% too fast
  - ROM↔↔IRAM ran 7% too fast
  - IRAM↔↔IRAM ran 287% too fast
  - BWRAM↔↔BWRAM ran 100% too fast
  - HDMA ROM↔↔ROM ran 15% too fast
  - HDMA WRAM↔↔ROM ran 15% too fast
  - DMA ROM↔↔ROM ran 100% too fast

After:

  - ROM↔↔ROM runs 14% too fast
  - HDMA WRAM↔↔ROM runs 7% too fast
  - DMA ROM↔↔ROM runs 4% too fast

If you enable this with the fast PPU + DSP, your framerate in SA1 games
will drop by 51%. And even if you disable it, you'll still lose 9% speed
in SA1 games, and 2% speed in non-SA1 games, because of changes needed
to make this support possible.

By default, I'm leaving this off. Compile with `-DACCURATE_SA1` (or
uncomment the line in sfc/sfc.hpp) if you want to try it out.

This'll almost certainly cause some SA1 regressions, so I guess we'll
tackle those as they arise.
2018-09-03 00:06:41 +10:00
Tim Allen
bd814f0358 Update to v106r59 release.
byuu says:

Changelog:

  - fixed bug in Emulator::Game::Memory::operator bool()
  - nall: renamed view<string> back to `string_view`
  - nall:: implemented `array_view`
  - Game Boy: split cartridge-specific input mappings (rumble,
    accelerometer) to their own separate ports
  - Game Boy: fixed MBC7 accelerometer x-axis
  - icarus: Game Boy, Super Famicom, Mega Drive cores output internal
    header game titles to heuristics manifests
  - higan, icarus, hiro/gtk: improve viewport geometry configuration;
    fixed higan crashing bug with XShm driver
  - higan: connect Video::poll(),update() functionality
  - hiro, ruby: several compilation / bugfixes, should get the macOS
    port compiling again, hopefully [Sintendo]
  - ruby/video/xshm: fix crashing bug on window resize
      - a bit hacky; it's throwing BadAccess Xlib warnings, but they're
        not fatal, so I am catching and ignoring them
  - bsnes: removed Application::Windows::onModalChange hook that's no
    longer needed [Screwtape]
2018-08-26 16:49:54 +10:00
Tim Allen
f9adb4d2c6 Update to v106r58 release.
byuu says:

The main thing I worked on today was emulating the MBC7 EEPROM.

And... I have many things to say about that, but not here, and not now...

The missing EEPROM support is why the accelerometer was broken. Although
it's not evidently clear that I'm emulating the actual values
incorrectly. I'll think about it and get it fixed, though.

bsnes went from ~308fps to ~328fps, and I don't even know why. Probably
something somewhere in the 140KB of changes to other things made in this
WIP.
2018-08-21 13:17:12 +10:00
Tim Allen
9a6ae6dacb Update to 20180809 release.
byuu says:

The Windows port can now run the emulation while navigating menus,
moving windows, and resizing windows. The main window also doesn't try
so hard to constantly clear itself. This may leave a bit of unwelcome
residue behind in some video drivers during resize, but under most
drivers, it lets you resize without a huge amount of flickering.

On all platforms, I now also run the emulation during MessageWindow
modal events, where I didn't before.

I'm thinking we should probably mute the audio during modal periods,
since it can generate a good deal of distortion.

The tooltip timeout was increased to ten seconds.

On Windows, the enter key can now activate buttons, so you can more
quickly dismiss MessageDialog windows. This part may not actually work
... I'm in the middle of trying to get messages out of the global
`Application_windowProc` hook and into the individual `Widget_windowProc`
hooks, so I need to do some testing.

I fixed a bug where changing the input driver wouldn't immediately
reload the input/hotkey settings lists properly.

I also went from disabling the driver "Change" button when the currently
active driver is selected in the list, to instead setting it to say
"Reload", and I also added a tool tip to the input driver reload button,
advising that if you're using DirectInput or SDL, you can hit "Reload"
to rescan for hotplugged gamepads without needing to restart the
emulator. XInput and udev have auto hotswap support. If we can ever get
that into DirectInput and SDL, then I'll remove the tooltip. But
regardless, the reload functionality is nice to have for all drivers.

I'm not sure what should happen when a user changes their driver
selection while a game is loaded, gets the warning dialog, chooses not
to change it, and then closes the emulator. Currently, it will make the
change happen the next time you start the emulator. This feels a bit
unexpected, but when you change the selection without a game loaded, it
takes immediate effect. So I'm not really sure what's best here.
2018-08-10 15:02:59 +10:00
Tim Allen
1e4affe5f9 Update to 20180808 release.
byuu says:

This release fixes the XAudio 2.1 and WASAPI drivers on Windows, and
extends XAudio to support device selection (eg headphones, speakers,
monitor, etc.) It also adds DRC to XAudio, however it's not currently
working.

The code is courtesy of Talarubi, I just botched it somewhere upon
porting it to the newer version of ruby.
2018-08-09 14:16:46 +10:00
Tim Allen
93a6a1ce7e Update to v106r57 release.
byuu says:

I've added tool tips to hiro for Windows, GTK, and Qt. I'm unsure how to
add them for Cocoa. I wasted am embarrassing ~14 hours implementing tool
tips from scratch on Windows, because the `TOOLTIPS_CLASS` widget just
absolutely refused to show up, no matter what I tried. As such, they're
not quite 100% native, but I would really appreciate any patch
submissions to help improve my implementation.

I added tool tips to all of the confusing settings in bsnes. And of
course, for those of you who don't like them, there's a configuration
file setting to turn them off globally.

I also improved Mega Drive handling of the Game Genie a bit, and
restructured the way the Settings class works in bsnes.

Starting now, I'm feature-freezing bsnes and higan. From this point
forward:

  - polishing up and fixing bugs caused by the ruby/hiro changes
  - adding DRC to XAudio2, and maybe exclusive mode to WGL
  - correcting FEoEZ (English) to load and work again out of the box

Once that's done, a final beta of bsnes will go out, I'll fix any
reported bugs that I'm able to, and then v107 should be ready. This time
with higan being functional, but marked as v107 beta. v108 will restore
higan to production status again, alongside bsnes.
2018-08-08 18:46:58 +10:00
Tim Allen
3b4e8b6d75 Update to v106r56 release.
byuu says:

I fixed all outstanding bugs that I'm aware of, including all of the
errata I listed yesterday.

And now it's time for lots of regression testing.

After that, I need to add Talarubi's XAudio2 DRC code, and then get a
new public bsnes WIP out for final testing.

New errata: when setting an icon (nall::image) larger than a Canvas on
Windows, it's not centering the image, so you end up seeing the overscan
area in the state manager previews, and the bottom of the image gets cut
off. I also need to forcefully disable the Xlib screensaver disable
support. I think I'll remove the GUI option to bypass it as well, and
just force screensaver disable always on with Windows. I'll improve it
in the future to toggle the effect between emulator pauses.
2018-08-06 17:46:00 +10:00
Tim Allen
b2b51d544f Make genius and icarus also ignore dynamically-generated dependencies. 2018-08-06 17:41:55 +10:00
Tim Allen
5da4532771 Update to v106r55 release.
byuu says:

Everything *should* be working again, but of course that won't
actually be the case. Here's where things stand:

  - bsnes, higan, icarus, and genius compile and run fine on FreeBSD
    with GTK
  - ruby video and audio drivers are untested on Windows, macOS, and
    Linux
  - hiro is untested on macOS
  - bsnes' status bar is not showing up properly with hiro/qt
  - bsnes and higan's about screen is not showing up properly with
    hiro/qt (1x1 window size)
  - bsnes on Windows crashes often when saving states, and I'm not sure
    why ... it happens inside Encode::RLE
  - bsnes on Windows crashes with ruby.input.windows (unsure why)
  - bsnes on Windows fails to show the verified emblem on the status bar
    properly
  - hiro on Windows flickers when changing tabs

To build the Windows bsnes and higan ports, use

    ruby="video.gdi audio.directsound"

Compilation error logs for Linux will help me fix the inevitable list of
typos there. I can fix the typos on other platforms, I just haven't
gotten to it yet.
2018-08-05 19:00:15 +10:00
Tim Allen
552d385031 Fix ST018 firmware hashes. 2018-08-05 09:06:51 +10:00
Tim Allen
0595e9e866 Remove redundant use of "classic". 2018-08-05 09:02:30 +10:00
Tim Allen
23da4e4e91 Don't mention console dates in the documentation.
The WonderSwan Color came out in 2000 and the GBA in 2001, so technically
they're not "video-game consoles of the 1980s and 1990s". Since there's no
elegant way to talk about the 2000-2009 timespan, let's just not mention
dates at all.
2018-08-04 21:45:44 +10:00
Tim Allen
41e127a07c Update to v106r54 release.
byuu says:

Changes to hiro will break all but the GTK target. Not that it matters
much given that the only ruby drivers that function are all on BSD
anyway.

But if you are fortunate enough to be able to run this ... you'll find
lots of polishing improvements to the bsnes GUI. I posted some
screenshots on Twitter, if anyone were interested.
2018-08-04 21:44:00 +10:00
Tim Allen
5d135b556d Update to v106r53 release.
byuu says:

Okay, so the WIPs-within-WIPs thing wasn't achieving its desired effect,
and it ended up causing me to have to redo some work on hiro since my
last local snapshot was of r52. So, heck it. I'll just do mostly
non-functional WIPs for a bit, and worry about the fallout years later
when I'm trying to find an emulation regression and cursing that the
WIPs aren't compiling.

I ported all of the ruby input drivers to the new syntax, as well as the
OpenAL driver. If you patch the ruby drivers for Linux with this in
mind, bsnes should compile and run there again.

Also, the bsnes program icon has returned, now that the new hiro layout
code is mature enough and I can simply add and remove the icon as a
Canvas instead of having to try and render into a viewport. The icon
shows up instantly with the main window.
2018-08-01 19:07:28 +10:00
Tim Allen
2335bb0df8 Update to 20180731 release.
byuu says:

I've completed moving all the class objects from `unique_pointer<T>` to
just T. The one exception is the Emulator::Interface instance. I can
absolutely make that a global object, but only in bsnes where there's
just the one emulation core.

I also moved all the SettingsWindow and ToolsWindow panels out to their
own global objects, and fixed a very difficult bug with GTK TabFrame
controls.

The configuration settings panel is now the emulator settings panel. And
I added some spacing between bold label sections on both the emulator
and driver settings panels.

I gave fixing ComboButtonItem my best shot, given I can't reproduce the
crash. Probably won't work, though.

Also made a very slight consistency improvement to ruby and renamed
driverName() to driver().

...

An important change ... as a result of moving bsnes to global objects,
this means that the constructors for all windows run before the
presentation window is displayed. Before this change, only the
presentation window was constructed first berore displaying it, followed
by the construction of the rest of the GUI windows.

The upside to this is that as soon as you see the main window, the GUI
is ready to go without a period where it's unresponsive.

The downside to this is it takes about 1.5 seconds to show the main
window, compared to around 0.75 seconds before.

I've no intention of changing that back. So if the startup time becomes
a problem, then we'll just have to work on optimizing hiro, so that it
can construct all the global Window objects quicker. The main way to do
that would be to not do calls to the Layout::setGeometry functions for
every widget added, and instead wait until the window is displayed. But
I don't have an easy way to do that, because you want the widget
geometry values to be sane even before the window is visible to help
size certain things.
2018-07-31 20:56:45 +10:00
Tim Allen
212da0a966 Update to 20180730 release.
byuu says:

These WIPs-within-WIPs are getting more and more broken ... this isn't
going the way I wanted.

But ... this time around, I've revamped the entire ruby API again, to
solve a bunch of tough problems that have always made using ruby really
clunky.

But there are *so many* ruby drivers that it's going to take a long
time to work through them all. This WIP is only going to run bsnes, and
only on FreeBSD, and only with some drivers.

hiro's Application::initialize() now calls hiro::initialize(), which you
define inside of your hiro apps. This lets you call
Application::setName(...) before anything else in hiro runs. This is
essential on Xorg to set program icons, for instance.

With the ruby rewrite and the change to hiro, I can get away from the
need to make everything in bsnes/higan pointers to objects, and can now
just declare them as regular objects.
2018-07-31 12:23:12 +10:00
Tim Allen
5deba5cbc1 Update to 20180729 release.
byuu wrote:

Sigh ...

asio.hpp needs #include <nall/windows/registry.hpp>

[Since the last WIP, byuu also posted the following message. -Ed.]

ruby drivers have all been updated (but not tested outside of BSD), and
I redesigned the settings window. The driver functionality all exists on
a new "Drivers" panel, the emulator/hack settings go to a
"Configuration" panel, and the video/audio panels lose driver settings.
As does the settings menu and its synchronize options.

I want to start pushing toward a v107 release. Critically, I will need
DirectSound and ALSA to support dynamic rate control. I'd also like to
eliminate the other system manifest.bml files. I need to update the
cheat code database format, and bundle at least a few quark shaders --
although I still need to default to Direct3D on Windows.

Turbo keys would be nice, if it's not too much effort. Aside from
netplay, it's the last significant feature I'm missing.

I think for v107, higan is going to be a bit rough around the edges
compared to bsnes. And I don't think it's practical to finish the bsnes
localization support.

I'm thinking we probably want another WIP to iron out any critical
issues, but this time there should be a feature freeze with the next
WIP.
2018-07-29 23:24:38 +10:00
Tim Allen
716c95f279 Update to 20180728 release.
byuu says:

Sigh, I seem to be spiraling a bit here ... but the work is very
important. Hopefully I can get a solid WIP together soon. But for now...

I've integrated dynamic rate control into ruby::Audio via
setDynamic(bool) for now. It's very demanding, as you would expect. When
it's not in use, I realized the OSS driver's performance was pretty bad
due to calling write() for every sample for every channel. I implemented
a tiny 256-sample buffer and bsnes went from 290fps to 330fps on my
FreeBSD desktop. It may be possible to do the same buffering with DRC,
but for now, I'm not doing so, and adjusting the audio input frequency
on every sample.

I also added ruby::Video::setFlush(bool), which is available only in the
OpenGL drivers, and this causes glFinish() to be called after swapping
display buffers. I really couldn't think of a good name for this, "hard
GPU sync" sounds kind of silly. In my view, flush is what commits queued
events. Eg fflush(). OpenGL of course treats glFlush differently (I
really don't even know what the point of it is even after reading the
manual ...), and then has glFinish ... meh, whatever. It's
setFlush(bool) until I come up with something better. Also as expected,
this one's a big hit to performance.

To implement the DRC, I started putting helper functions into the ruby
video/audio/input core classes. And then the XVideo driver started
crashing. It took hours and hours and hours to track down the problem:
you have to clear XSetWindowAttributes to zero before calling
XCreateWindow. No amount of `--sync`, `gdb break gdk_x_error`, `-Og`,
etc will make Xlib be even remotely helpful in debugging errors like
this.

The GLX, GLX2, and XVideo drivers basically worked by chance before. If
the stack frame had the right memory cleared, it worked. Otherwise it'd
crash with BadValue, and my changing things broke that condition on the
XVideo driver. So this has been fixed in all three now.

Once XVideo was running again, I realized that non-power of two video
sizes were completely broken for the YUV formats. It took a while, but I
managed to fix all of that as well.

At this point, most of ruby is going to be broken outside of FreeBSD, as
I still need to finish updating all the drivers.
2018-07-28 21:25:42 +10:00
Tim Allen
876b4be1d2 Update to 20180726 release.
byuu says:

Once again, I wasn't able to complete a full WIP revision.

This WIP-WIP adds very sophisticated emulation of the Sega Genesis
Lock-On and Game Genie cartridges ... essentially, through recursion and
a linked list, higan supports an infinite nesting of cartridges.

Of course, on real hardware, after you stack more than three or four
cartridges, the power draw gets too high and things start glitching out
more and more as you keep stacking. I've heard that someone chained up
to ten Sonic & Knuckles cartridges before it finally became completely
unplayable.

And so of course, higan emulates this limitation as well ^-^. On the
fourth cartridge and beyond, it will become more and more likely that
address and/or data lines "glitch" out randomly, causing various
glitches. It's a completely silly easter egg that requires no speed
impact whatsoever beyond the impact of the new linked list cartridge
system.

I also designed the successor to Emulator::Interface::cap,get,set. Those
were holdovers from the older, since-removed ruby-style accessors.

In its place is the new Emulator::Interface::configuration,configure
API. There's the usual per-property access, and there's also access to
read and write all configurable options at once. In essence, this
enables introspection into core-specific features.

So far, you can control processor version#s, PPU VRAM size, video
settings, and hacks. As such, the .sys/manifest.bml files are no longer
necessary. Instead, it all goes into .sys/configuration.bml, which is
generated by the emulator if it's missing.

higan is going to take this even further and allow each option under
"Systems" to have its own editable configuration file. So if you wanted,
you could have a 1/1/1 SNES menu option, and a 2/1/3 SNES menu option.
Or a Model 1 Genesis option, and a Model 2 Genesis option. Or the
various Game Boy model revisions. Or an "SNES-Fast" and "SNES-Accurate"
option.

I've not fully settled on the syntax of the new configuration API. I
feel it might be useful to provide type information, but I really quite
passionately hate any<T> container objects. For now it's all
string-based, because strings can hold anything in nall.

I might also change the access rules. Right now it's like:
emulator→configure("video/blurEmulation", true); but it might be nicer
as "Video::Blur Emulation", or "Video.BlurEmulation", or something like
that.
2018-07-26 20:36:43 +10:00
Tim Allen
22bd4b9277 Update to v106r52 release.
byuu says:

I stand corrected, I managed to create and even larger diff than ever.
This one weighs in at 309KiB `>__>`

I'll have to create a changelog later, I'm too tired right now to go
through all of that.
2018-07-25 22:24:03 +10:00
Tim Allen
f1a4576ac4 Update to 20180724 release.
byuu says:

I failed to complete a WIP, have five of eight cores updated with some
major changes to Emulator::Interface. I'll just post a quick temporary
WIP in the off chance someone wants to look over the new interface and
comment on it.

Also implemented screen saver suppression into hiro/GTK.

I should also add ... a plan of mine is to develop target-bsnes into a
more generic user interface, with the general idea being that
target-higan is for multiple Emulator::Interface cores at the same time,
and target-bsnes is for just one Emulator::Interface core.

The idea being that if one were to compile target-bsnes with the GBA
core, it'd become bgba, for instance.
I don't plan on releasing single-core emulators like this, but ... I don't see any downsides to being more flexible.
2018-07-24 23:41:41 +10:00
Tim Allen
0aedb3430c Update to v106r51 release.
byuu says:

Changelog:

  - added `Emulator::Interface::connected(uint port) -> uint device;`
  - higan, bsnes: updated emulators to use the new
    Emulator::Interface::connected() function
  - hiro: fixed Object::cast<T> finally

So, Emulator::Interface::connected() solves two annoying problems at the
same time.

First, on first run of the emulator when the settings file is blank, it
will retrieve the default "sane" device ID, which is usually a gamepad
for a controller port, or nothing for an expansion/extension port.

Second, if you were to select a multi-port device, like the NES Four
Score, the core will set the other port to the Four Score device as
well, and the GUIs query connected() right after any call to connect(),
so it gets updated without needing a system for the emulation core to
send messages alerting the GUI of changes.
2018-07-21 21:49:48 +10:00
Tim Allen
35ff15f83e Update to v106r50 release.
byuu says:

Changelog:

  - emulator/video,audio: various cleanups
  - emulator/audio: removed reverb effect (it breaks very badly on
    high-frequency systems)
  - emulator/audio: the Nyquist anti-aliasing lowpass filter is now
    generated automatically instead of set per-core
      - at 44.1KHz output, it's set to 22KHz; at 48KHz, it's set to
        22KHz; at 96KHz, it's set to 25KHz
      - this filter now takes the bsnes emulation speed setting into
        account
  - all system/video.cpp files removed; inlined in System::power() and
    Interface::set() instead
  - sfc/cpu: pre-compute `HTIME` as `HTIME+1<<2` for faster comparisons of
    HIRQs
  - sfc/cpu: re-add check to block IRQs on the last dot of each frame
    (minor speed hit)
  - hiro/gtk3: fixed headers for Linux compilation finally
  - hiro/gtk,qt: fixed settings.cpp logic so initial values are used
    when no settings.bml file exists
  - hiro/gtk: started a minor experiment to specify theming information
    in settings.bml files
  - nall/dsp: allow the precision type (double) to be overridden (to
    float)
  - nall: add some helpers for generating pre-compiled headers
      - it was a failure to try using them for higan, however ...
  - nall: add some helpers for reading fallback values from empty
    `Markup::Node[search]` statements

Todo:

  - CRITICAL: a lot of my IRQ/NMI/HDMA timing tests are failing with the
    fast PPU ... need to figure out why
  - space between Emulator::video functions and Emulator::audio
    functions in gb/system/system.cpp
  - remove Audio/Reverb/Enable from settings.bml in target-bsnes
2018-07-21 21:06:40 +10:00
Tim Allen
65a3e6c676 Update to v106r49 release.
byuu says:

This is a fairly radical WIP with extreme changes to lots of very
important parts.

The result is a ~7% emulation speedup (with bsnes, unsure how much it
helps higan), but it's quite possible there are regressions. As such, I
would really appreciate testing as many games as possible ... especially
the old finnicky games that had issues with DMA and/or interrupts.

One thing to note is that I removed an edge case test that suppresses
IRQs from firing on the very last dot of every field, which is a
behavior I've verified on real hardware in the past. I feel that the
main interrupt polling function (the hottest portion of the entire
emulator) is not the appropriate place for it, and I should instead
factor it into assignment of NMITIMEN/VTIME/HTIME using the new
io.irqEnable (==virqEnable||hirqEnable) flag. But since I haven't done
that yet ... there's an old IRQ test ROM of mine that'll fail for this
WIP. No commercial games will ever rely on this, so it's fine for
testing.

Changelog:

  - sfc/cpu.smp: inlined the global status functions
  - sfc/cpu: added readRAM, writeRAM to use a function pointer instead
    of a lambda for WRAM access
  - sfc/cpu,smp,ppu/counter: updated reset functionality to new style
    using class inline initializers
  - sfc/cpu: fixed power(false) to invoke the reset vector properly
  - sfc/cpu: completely rewrote DMA handling to have per-channel
    functions
  - sfc/cpu: removed unused joylatch(), io.joypadStrobeLatch
  - sfc/cpu: cleaned up io.cpp handlers
  - sfc/cpu: simplified interrupt polling code using
    nall::boolean::flip(),raise(),lower() functions
  - sfc/ppu/counter: cleaned up the class significantly and also
    optimized things for efficiency
  - sfc/ppu/counter: emulated PAL 1368-clock long scanline when
    interlace=1, field=1, vcounter=311
  - sfc/smp: factored out the I/O and port handlers to io.cpp
2018-07-19 19:01:44 +10:00
Tim Allen
393c2395bb Update to v106r48 release.
byuu says:

The problems with the Windows and Qt4 ports have all been resolved,
although there's a fairly gross hack on a few Qt widgets to not destruct
once Application::quit() is called to avoid a double free crash (I'm
unsure where Qt is destructing the widgets internally.) The Cocoa port
compiles again at least, though it's bound to have endless problems. I
improved the Label painting in the GTK ports, which fixes the background
color on labels inside TabFrame widgets.

I've optimized the Makefile system even further.

I added a "redo state" command to bsnes, which is created whenever you
load the undo state. There are also hotkeys for both now, although I
don't think they're really something you want to map hotkeys to.

I moved the nall::Locale object inside hiro::Application, so that it can
be used to translate the BrowserDialog and MessageDialog window strings.

I improved the Super Game Boy emulation of `MLT_REQ`, fixing Pokemon
Yellow's custom border and probably more stuff.

Lots of other small fixes and improvements. Things are finally stable
once again after the harrowing layout redesign catastrophe.

Errata:

  - ICD::joypID should be set to 3 on reset(). joypWrite() may as well
    take uint1 instead of bool.
  - hiro/Qt: remove pWindow::setMaximumSize() comment; found a
    workaround for it
  - nall/GNUmakefile: don't set object.path if it's already set (allow
    overrides before including the file)
2018-07-16 16:16:26 +10:00
Tim Allen
6090c63958 Update to v106r47 release.
byuu says:

This is probably the largest code-change diff I've done in years.

I spent four days working 10-16 hours a day reworking layouts in hiro
completely.

The result is we now have TableLayout, which will allow for better
horizontal+vertical combined alignment.

Windows, GTK2, and now GTK3 are fully supported.

Windows is getting the initial window geometry wrong by a bit.

GTK2 and GTK3 work perfectly. I basically abandoned trying to detect
resize signals, and instead keep a list of all hiro windows that are
allocated, and every time the main loop runs, it will query all of them
to see if they've been resized. I'm disgusted that I have to do this,
but after fighting with GTK for years, I'm about sick of it. GTK was
doing this crazy thing where it would trigger another size-allocate
inside of a previous size-allocate, and so my layouts would be halfway
through resizing all the widgets, and then the size-allocate would kick
off another one. That would end up leaving the rest of the first layout
loop with bad widget sizes. And if I detected a second re-entry and
blocked it, then the entire window would end up with the older geometry.
I started trying to build a message queue system to allow the second
layout resize to occur after the first one completed, but this was just
too much madness, so I went with the simpler solution.

Qt4 has some geometry problems, and doesn't show tab frame layouts
properly yet.

Qt5 causes an ICE error and tanks my entire Xorg display server, so ...
something is seriously wrong there, and it's not hiro's fault. Creating
a dummy Qt5 application without even using hiro, just int main() {
TestObject object; } with object performing a dynamic\_cast to a derived
type segfaults. Memory is getting corrupted where GCC allocates the
vtables for classes, just by linking in Qt. Could be somehow related to
the -fPIC requirement that only Qt5 has ... could just be that FreeBSD
10.1 has a buggy implementation of Qt5. I don't know. It's beyond my
ability to debug, so this one's going to stay broken.

The Cocoa port is busted. I'll fix it up to compile again, but that's
about all I'm going to do.

Many optimizations mean bsnes and higan open faster. GTK2 and GTK3 both
resize windows very quickly now.

higan crashes when you load a game, so that's not good. bsnes works
though.

bsnes also has the start of a localization engine now. Still a long way
to go.

The makefiles received a rather substantial restructuring. Including the
ruby and hiro makefiles will add the necessary compilation rules for
you, which also means that moc will run for the qt4 and qt5 targets, and
windres will run for the Windows targets.
2018-07-14 13:59:29 +10:00
Tim Allen
0c55796060 Update to v106r46 release.
byuu says:

Changelog:

  - bsnes, higan: simplified make output; reordered rules
  - hiro: added Window::set(Minimum,Maximum)Size() [only implemented in
    GTK+ so far]
  - bsnes: only allow the window to be shrunk to the 1x multiplier size
  - bsnes: refactored Integral Scaling checkbox to {Center, Scale,
    Stretch} radio selection
  - nall: call fflush() after nall::print() to stdout or stderr [needed
    for msys2/bash]
  - bsnes, higan: program/interface.cpp renamed to program/platform.cpp
  - bsnes: trim ".shader/" from names in Settings→Shader menu
  - bsnes: Settings→Shader menu updated on video driver changes
  - bsnes: remove missing games from recent files list each time it is
    updated
  - bsnes: video multiplier menu generated dynamically based on largest
    monitor size at program startup
  - bsnes: added shrink window and center window function to video
    multiplier menu
  - bsnes: de-minimize presentation window when exiting fullscreen mode
    or changing video multiplier
  - bsnes: center the load game dialog against the presentation window
    (important for multi-monitor setups)
  - bsnes: screenshots are not immediate instead of delayed one frame
  - bsnes: added frame advance menu option and hotkey
  - bsnes: added enable cheats checkbox and hotkey; can be used to
    quickly enable/disable all active cheats

Errata:

  - hiro/Windows: `SW_MINIMIZED`, `SW_MAXIMIZED `=> `SW_MINIMIZE`,
    `SW_MAXIMIZE`
  - hiro/Windows: add pMonitor::workspace()
  - hiro/Windows: add setMaximized(), setMinimized() in
    pWindow::construct()
  - bsnes: call setCentered() after setMaximized(false)
2018-07-08 14:58:27 +10:00
Tim Allen
372e9ef42b Update to v106r45 release.
byuu says:

Changelog:

  - sfc/ppu-fast: added hires mode 7 option (doubles the sampling rate
    of mode 7 pixels to reduce aliasing)
  - sfc/ppu-fast: fixed mode 7 horizontal screen flip [hex_usr]
  - bsnes: added capture screenshot function and path selection
      - for now, it saves as BMP. I need a deflate implementation that
        won't add an external dependency for PNG
      - the output resolution is from the emulator: (256 or 512)x(240 or
        480 minus overscan cropping if enabled)
      - it captures the NEXT output frame, not the current one ... but
        it may be wise to change this behavior
      - it'd be a problem if the core were to exit and an image was
        captured halfway through frame rendering
  - bsnes: recovery state renamed to undo state
  - bsnes: added manifest viewer tool
  - bsnes: mention if game has been verified or not on the status bar
    message at load time
  - bsnes, nall: fixed a few missing function return values
    [SuperMikeMan]
  - bsnes: guard more strongly against failure to load games to avoid
    crashes
  - hiro, ruby: various fixes for macOS [Sintendo]
  - hiro/Windows: paint on `WM_ERASEBKGND` to prevent status bar
    flickering at startup
  - icarus: SPC7110 heuristics fixes [hex_usr]

Errata:

  - sfc/ppu-fast: remove debug hires mode7 force disable comment from
    PPU::power()

[The `WM_ERASEBKGND` fix was already present in the 106r44 public
beta -Ed.]
2018-07-02 11:57:04 +10:00
Tim Allen
40a5fbe605 Update to v106r44 public beta release:
byuu says (in the public announcement):

I'm releasing a beta version of bsnes, for the purpose of gathering feedback and
ensuring that the first official release of bsnes is as solid as possible.

With the exception of dynamic rate control for automatic audio/video sync, and
no pack-in video shaders or cheat code database, it is mostly feature complete.
However, please do not form a lasting opinion of bsnes based on this beta.
2018-06-28 16:35:49 +10:00
Tim Allen
ec960c5172 Update to v106r44 release.
byuu says:

Changelog:

  - hiro/Windows: use `WS_CLIPSIBLINGS` on Label to prevent resize
    drawing issues
  - bsnes: correct viewport resizing
  - bsnes: speed up window resizing a little bit
  - bsnes: fix the cheat editor list enable checkbox
  - bsnes: fix the state manager filename display in game ROM mode
  - bsnes: fix the state manager save/rename/remove functionality in
    game ROM mode
  - bsnes: correct path searching for IPS and BPS patches in game ROM
    mode
  - bsnes: patch BS-X town cartridge to disable play limits
  - bsnes: do not load (program,data,expansion).(rom,flash) from disk in
    game pak mode
      - this is required to support soft-patching and ROM hacks
  - bsnes: added speed mode selection (50%, 75%, 100%, 150%, 200%);
    maintains proper pitch
  - bsnes: added icons to the menubar
      - this is particularly useful to tell game ROMs from game paks in
        the load recent game menu
  - bsnes: added emblem at bottom left of status bar to indicate if a
    game is verified or not
      - verified means it is in the icarus verified game dump database
      - the verified diamond is orange; the unverified diamond is blue
  - bsnes: added an option (which defaults to off) to warn when loading
    unverified games
      - working around a bug in GTK, I have to use the uglier
        MessageWindow instead of MessageDialog
  - bsnes: added (non-functional) link to <https://doc.byuu.org/bsnes/>
    to the help menu
  - bsnes: added GUI setting to toggle memory auto-save feature
  - bsnes: added GUI setting to toggle capturing a backup save state
    when closing the emulator
  - bsnes: made auto-saving states on exit an option
  - bsnes: added an option to auto-load the auto-saved state on load
      - basically, the two combined implements auto-resume
  - bsnes: when firmware is missing, offer to take the user to the
    online help documentation
  - bsnes: added fast PPU option to disable the sprite limit
      - increase from 32 items/line + 34 tiles/line to 128 items/line +
        128 tiles/line
      - technically, 1024 tiles/line are possible with 128 sprites at
        64-width
      - but this is just a waste of cache locality and worst-case
        performance; it'll never happen

Errata:

  - hiro/Windows: fallthrough on Canvas `WM_ERASEBKGND` to prevent
    startup flicker
2018-06-28 16:28:27 +10:00
Tim Allen
b14c6bf155 Update to v106r43 release.
byuu says:

Changelog:

  - bsnes: added video settings panel
  - bsnes: added audio settings panel
  - bsnes: disable assign/clear buttons at startup for hotkeys panel
  - bsnes: program initialization restructured: drivers initialize last
      - this lets me reinitialize the settings panel values on driver
        changes
      - so eg things like input/hotkey remappings should work after
        input driver changes now
      - ... but I had to disable the window icon for this ... it takes
        too long to show up this way
  - bsnes: added synchronize video/audio options to settings menu
  - bsnes: added audio skew slider for video/audio synchronization
  - bsnes: state manager edit/remove works on game ROM .bsz archives now
  - bsnes: removed View→Color Emulation; default to 150% gamma instead
    (it's a touch brighter but similar)

At this point, I'm pretty much ready to make an initial beta release for
wider testing.

Please use this WIP to indicate any must-fix issues before I do so.
2018-06-27 11:56:27 +10:00
Tim Allen
5b97fa2415 Update to v106r42 release.
byuu says:

Changelog:

  - emulator: added `Thread::setHandle(cothread_t)`
  - icarus: added special heuristics support for the Tengai Maykou Zero
    fan translation
      - board identifier is: EXSPC7110-RAM-EPSONRTC (match on SPC7110 +
        ROM size=56mbit)
      - board ROM contents are: 8mbit program, 40mbit data, 8mbit
        expansion (sizes are fixed)
  - bsnes: show messages on game load, unload, and reset
  - bsnes: added support for BS Memory and Sufami Turbo games
  - bsnes: added support for region selection (Auto [default], NTSC,
    PAL)
  - bsnes: correct presentation window size from 223/239 to 224/240
  - bsnes: add SA-1 internal RAM on cartridges with BS Memory slot
  - bsnes: fixed recovery state to store inside .bsz archive
  - bsnes: added support for custom manifests in both game pak and game
    ROM modes
  - bsnes: added icarus game database support (manifest → database →
    heuristics)
  - bsnes: added flexible SuperFX overclocking
  - bsnes: added IPS and BPS soft-patching support to all ROM types
    (sfc,smc,gb,gbc,bs,st)
      - can load patches inside of ZIP archives (matches first “.ips” or
        “.bps” file)
  - bsnes/ppu: cache interlace/overscan/vdisp (277 → 291fps with fast
    PPU)
  - hiro/Windows: faster painting of Label widget on expose
  - hiro/Windows: immediately apply LineEdit::setBackgroundColor changes
  - hiro/Qt: inherit Window backgroundColor when one is not assigned to
    Label

Errata:

  - sfc/ppu-fast: remove `renderMode7Hires()` function (the body isn't in
    the codebase)
  - bsnes: advanced note label should probably use a lighter text color
    and/or smaller font size instead of italics

I didn't test the soft-patching at all, as I don't have any patches on
my dev box. If anyone wants to test, that'd be great. The Tengai Makyou
Zero fan translation would be a great test case.
2018-06-26 13:17:26 +10:00
Tim Allen
f70a20bc42 Update to v106r41 release.
byuu says:

Changelog:

  - hiro: added Label::set(Background,Foreground)Color (not implemented
    on Cocoa backend)
  - hiro: added (Horizontal,Vertical)Layout::setPadding()
      - setMargin(m) is now an alias to setPadding({m, m, m, m})
  - hiro/Windows: update Label rendering to draw to an offscreen canvas
    to prevent flickering
  - sfc: reverted back to 224/240-line height (from 223/239-line height
    in earlier v106 WIPs)
  - bsnes: new multi-segment status bar added
  - bsnes: exiting fullscreen mode will resize and recenter window
      - this is required; the window geometry gets all scrambled when
        toggling fullscreen mode
  - bsnes: updated to a new logo [Ange Albertini]

Errata:

  - hiro/Windows: try to paint Label backgroundColor quicker to avoid
    startup flicker
      - `WM_ERASEBKGND` fallthrough to `WM_PAINT` seems to work
  - hiro/Qt: use Window backgroundColor for Label when no Label
    backgroundColor set
  - bsnes: update size multipliers in presentation.cpp to 224/240 (main
    window size is off in this WIP)
2018-06-24 14:53:44 +10:00
Tim Allen
470e27323d accuracy/fast is now a runtime toggle, not compile-time. 2018-06-11 14:54:49 +10:00
Tim Allen
5a8c814e25 Update to v106r40 release.
byuu says:

Changelog:

  - hiro: added BrowserDialog::openObject() [match file *or* folder
    by filters]
  - hiro: BrowserDialog accept button is now disabled when it would
    otherwise do nothing
      - eg openFile without a folder to enter or file to open selected
      - eg saveFile without a file name or with a file name that matches
        a folder name
  - bsnes: added support for gamepaks (game folders)
  - bsnes: store all save states inside per-game .bsz (ZIP) archives
    instead of .bst/ folders
      - this reduces the number of state files from 10+ to 1; without
        having folders sort before files
  - hiro: both gtk2 and gtk3 now use cairo to render Canvas; supports
    sx,sy [BearOso]
  - higan, bsnes: fast PPU/DSP are now run-time options instead of
    compile-time options
  - bsnes: disable fast PPU when loading Air Strike Patrol / Desert
    Fighter
  - bsnes: disable fast DSP when loading Koushien 2
  - bsnes: added options to advanced panel to disable fast PPU and/or
    fast DSP
2018-06-11 14:50:18 +10:00
Tim Allen
91bb781b73 Update to v106r39 release.
byuu says:

Changelog:

  - ruby/video: implement onUpdate() callback to signal when redraws are
    necessary
  - ruby/video/GLX,GLX2,XVideo,XShm: implement onUpdate() support
  - bsnes: implement Video::onUpdate() support to redraw Viewport icon
    as needed
  - bsnes: save RAM before ruby driver changes
  - sfc/sa1: clip signed multiplication to 32-bit [Jonas Quinn]
  - sfc/sa1: handle negative dividends in division [Jonas Quinn]
  - hiro/gtk3: a few improvements
  - bsnes: added empty stub video and audio settings panels
  - bsnes: restructured advanced settings panel
  - bsnes: experiment: input/hotkeys name column bolded and colored for
    increased visual distinction
  - bsnes: added save button to state manager
2018-06-10 18:07:19 +10:00
Tim Allen
15b67922b3 Update to v106r38 release.
byuu says:

Changelog:

  - hiro: added Qt5 support
  - hiro: added GTK3 support (currently runs very poorly)
  - bsnes: number of recent games and quick state slots can be changed
    programmatically now
      - I may expose this as a configuration file setting, but probably
        not within the GUI
  - nall: use -Wno-everything when compiling with Clang
      - sorry, Clang's meaningless warning messages are just endless ...
2018-06-10 18:06:02 +10:00
Tim Allen
173a5d67bc Update to v106r37 release.
byuu says:

Changelog:

  - bsnes: cheat code “enabled” option changed to “enable”
  - bsnes: connected “Cancel” action on add/edit cheat code window
  - hiro: improved BrowserDialog::selectFolder() behavior
      - can choose “Select” inside of a target folder when no items are
        selected
  - bsnes: implemented state manager
  - bsnes: save a recovery state before loading a state, quitting, or
    changing drivers
  - bsnes: input settings, hotkey settings, cheat editor, state manager
    entries are now batchable
      - this allows bulk clearing/deleting of entries
  - bsnes: cheat code list now auto-sorts alphabetically instead of
    using up/down move arrows

I know most people will probably prefer to order cheat codes the way
they want, but the issue is that the state manager can't really work
this way. Each state is a file on disk. So yes, we could store a
states-manifest.bml to track the order of the states, or try to insert
numbers into the filenames and do bulk filesystem rename operations on
sorting, but then we would run into oddities when users delete state
files manually. And really, manual sorting is just clumsy. If you really
want a specific ordering, you can prefix cheats/states with numeric
indices instead.
2018-06-07 21:48:41 +10:00
Tim Allen
ec9729a9e1 Update to v106r36 release.
byuu says:

Changelog:

  - nall: renamed array to adaptive_array; marked it as deprecated
  - nall: created new array class; which is properly static (ala
    std::array) with optional bounds-checking
  - sfc/ppu-fast: converted unmanaged arrays to use nall/array (no speed
    penalty)
  - bsnes: rewrote the cheat code editor to a new design
  - nall: string class can stringify pointer types directly now, so
    pointer() was removed
  - nall: added array_view and pointer types (still unsure if/how I'll
    use pointer)
2018-06-04 12:44:57 +10:00
Tim Allen
77ac5f9e88 Update to v106r35 release.
byuu says:

Changelog:

  - sfc/ppu-fast: fixed overscan crash
  - sfc/ppu-fast: fixed direct color mode
  - sfc: reconnected MSU1 support
      - higan: game.sfc/msu1/data.rom, game.sfc/msu1/track-#.pcm
      - bsnes: game.msu, game-#.pcm
  - bsnes: added cheat code editor
  - bsnes: added cheat code database support
  - sfc/ppu-fast: clear overscan lines when overscan disabled
  - sfc: output 223/239 lines instead of 224/240 lines
  - bsnes: fix aspect correction calculation
  - bsnes: crop line 224 when overscan masking is enabled
  - bsnes: exposed Expansion Port menu; but hid “21fx” from the list of
    devices
  - bsnes: tools menu is hidden until a game is loaded
  - ruby/input/keyboard/quartz: fixed compilation error

So only bsnes the automated overscan cropping option. In higan, you can
crop however many lines you like from the top or bottom of the image.
But for bsnes, it automatically eats sixteen lines. My view right now is
that if bsnes is meant to be the casual gaming emulator, that it should
eat line 224 in this mode. Most games show content here, but because of
the way the SNES PPU works, the very last line ends up on its very own
tile row (line 0 isn't rendered), if the scroll registers don't account
for it. There's a small number of games that will draw junk data to the
very last scanline of the frame as a result of this. So I chose, at
least for now, to hide it. Users can obviously disable overscan cropping
to see this scanline. I'm open to being convinced not to do this, if
someone has a compelling reason.

We're pretty much screwed one way or the other with no overscan masking.
If we output 239 lines, then most games will render 7 blank lines + 224
drawn lines + 8 blank lines, and the black top and bottom aren't
centered. But if we output 240 lines to get 8 + 224 + 8, then games that
do use overscan will have a blank line at the very bottom of the window.

I'm also trying out a modified cheat code file format. It's been forever
since I bothered to look at it, and the “cartridge” parent node doesn't
match what I'm doing with trying to rename “cartridge” to “game” in
manifests. And indeed, the idea of requiring a root node is rather
superfluous for a cheat code file. Current format looks like this:

    cheat
      description: foo
      code: 7e2000=20+7e2001=30?40
      enabled

    cheat
      description: bar
      code: 7e4000=80

Open to discussing this, and I'd like to sync up with Snes9X before they
push out a new release, and I'll agree to finalize and never change this
format again.

I chose to use .cht for the extension when using game files (eg
gamename.cht)
2018-06-03 23:14:42 +10:00
Tim Allen
8c337d4ac6 Make sure the libretro core builds with the accuracy profile. 2018-06-03 15:01:54 +10:00
Tim Allen
73354923eb Add another task to the release procedure. 2018-06-03 14:55:45 +10:00
Tim Allen
3aa90590ca bsnes binaries do not need the systems directory. 2018-06-02 14:43:40 +10:00
Tim Allen
52d0cd8dfb Now that byuu is ready for bsnes bug reports, I'll enable automated builds.
Also, since byuu changes the defaults according to what he's working on, let's
be explicit tat higan gets the accuracy core and bsnes gets the performance
core.
2018-06-02 13:21:16 +10:00
Tim Allen
c67fb2c726 Update to v106r34 release.
byuu says:

Changelog:

  - sfc/ppu-fast:
      - don't use mosaicSize unless mosaicEnable is set
      - fix background tiles that aren't 8x8 in size
      - flush (render) queued lines whenever VRAM or OAM are modified
        mid-frame
      - queue tile outputs to buffer for object rendering final pass
      - fix object window mask indexing
      - disable color bleed when output width is 256 pixels
      - handle reset(bool) events
      - implemented save states
  - icarus: fixed SPC7110-RAM-EPSONRTC mapping typo [hex_usr]
  - bsnes: fixed overscan masking mode when output height is 240

Todo:

  - sfc/ppu-fast: should not have deleted the tilecache freeing in
    ~PPU()
  - ruby/input/carbon: change setPath() call to setPathID()

Errata:

  - Rendering Ranger R2 crashes at startup, seems to be an issue with
    the expansion port device

Bug reports on the new fast SNES PPU are now welcome.
2018-06-02 12:47:37 +10:00
Tim Allen
5d29700fa1 Update to v106r33 release.
byuu says:

Changelog:

  - nall/GNUmakefile: added `openmp=(true,false)` option; can be toggled
    when building higan/bsnes
      - defaults to disabled on macOS, because Xcode doesn't stupidly
        doesn't ship with support for it
  - higan/GNUmakefile: forgot to switch target,profile back from
    bsnes,fast to higan,accurate
      - this is just gonna happen from time to time, sorry
  - sfc/dsp: when using the fast profile, the DSP syncs per sample
    instead of per clock
      - should only negatively impact Koushien 2, but is a fairly
        significant speedup otherwise
  - sfc/ppc,ppu-fast: optimized the code a bit (ppu 130fps to 133fps)
  - sfc/ppu-fast: basic vertical mosaic support (not accurate, but
    should look okay hopefully)
  - sfc/ppu-fast: added missing mode7 hflip support
  - sfc/ppu-fast: added support to render at 256-width and/or 240-height
      - gives a decent speed boost, and also allows all of the older
        quark shaders to work nicely again
      - it does violate the contract of Emulator::Interface, but oh
        well, it works fine in the bsnes GUI
  - sfc/ppu-fast: use cached CGRAM values for mode7 and sprites
  - sfc/ppu-fast: use global range/time over flags in object rendering
      - may not actually work as we intended since it's a race condition
        even if it's only ORing the flags
      - really don't want to have to make those variables atomic if I
        don't have to
  - sfc/ppu-fast: should fully support interlace and overscan modes now
  - hiro/cocoa: updated macOS Gatekeeper disable support to work on
    10.13+
  - ruby: forgot to fix macOS input driver, sorry
  - nall/GNUmakefile: if uname is present, then just default to rm
    instead of del (fixes Msys)

Note: blur emulation option will break pretty badly in 256x240 output
mode. I'll fix it later.
2018-05-31 17:06:55 +10:00
Tim Allen
5e7fdbe2c0 Update to v106r32 release.
byuu says:

Changelog:

  - sfc/ppu-fast: everything other than vertical mosaic and interlace
    support is in

Games are quite playable now, and you're welcome to try things out, but
please don't report bugs yet. It's still too early for that.
2018-05-29 21:26:48 +10:00
Tim Allen
51e3fcd3fa Update to v106r31 release.
byuu says:

Changelog:

  - sfc/ppu-fast: added a barebones background renderer; very incomplete

Right now, the 2bpp Mega Man X2 splash screen is rendering correctly,
but everything else looks really garbled. I'm thinking my tile cache
conversions from 4bpp to bitmap pixels is wrong, but I'm not seeing any
obvious issues.

If anyone wants to take a look at it, I'd appreciate it. The renderer is
mostly modeled after ppu-performance's.
2018-05-28 11:51:38 +10:00
Tim Allen
18852bcbe2 We definitely don't need genius with bsnes for Windows. 2018-05-28 11:44:06 +10:00
Tim Allen
bcc2627793 Don't hack up nall/GNUmakefile for Windows builds.
All our changes are now in official nall releases, so we don't need to mess with
it anymore.
2018-05-28 11:42:52 +10:00
Tim Allen
685cec6583 Update to v106r30 release.
byuu says:

Changelog:

  - nall/GNUmakefile: fixed findstring parameter arguments [Screwtape]
  - nall/Windows: always include -mthreads -lpthread for all
    applications
  - nall/memory: code restructuring

I really wanted to work on the new PPU today, but I thought I'd spend a
few minutes making some minor improvements to nall::memory, that was
five and a half hours ago. Now I have a 67KiB diff of changes. Sigh.
2018-05-28 11:16:27 +10:00
Tim Allen
6882bd98cf Update to v106r29 release.
byuu says:

Changelog:

  - sfc/ppu: collapsed folders to a single directory to match all other
    emulated processors
  - sfc/ppu-fast: implemented I/O registers
2018-05-27 09:04:43 +10:00
Tim Allen
6c8e3c885d Update to v106r28 release.
byuu says:

Changelog:

  - SNES: started on skeleton of the new parallel PPU core

To build the new PPU core, set profile=fast via GNU make. The old core
is profile=accurate.

The names of the profiles, and the name of the folder for the fast PPU
are subject to change.

The new PPU core doesn't do anything but demonstrate the proof of
concept: every scanline, make a copy of all the PPU registers and CGRAM.
Share the VRAM and OAM. Batch render all scanlines at once using OpenMP
at the end of each frame and blit the result.

With no PPU core at all, bsnes runs 91% faster than with the accuracy
PPU (230fps vs 120fps.) That's the absolute theoretical best-case
scenario. With the skeleton in place, we're already around 220fps. It'll
go down more as the PPU line renderer starts to do real work. I don't
know where things will end up yet. I suppose we'll find out in time.

My own copy of TDM/GCC can't use OpenMP on Windows, so ... it won't
parallelize if you build with that. I'm going to have to switch to a
different MinGW distribution once this is complete, I suppose.
2018-05-26 13:29:14 +10:00
Tim Allen
8f5bc80f01 Ignore generated file dependency information. 2018-05-25 18:07:03 +10:00
Tim Allen
2b8df2e70e Update to v106r27 release.
byuu says:

Changelog:

  - nall: merged Path::config() and Path::local() to Path::userData()
      - ~/.local/share or %appdata or ~/Library/ApplicationSupport
  - higan, bsnes: render main window icon onto viewport instead of
    canvas
      - should hopefully fix a brief flickering glitch that appears on
        Windows
  - icarus: improved Super Famicom heuristics for Starfox / Starwing RAM
  - ruby/Direct3D: handle viewport size changes in lock() instead of
    output()
      - fixes icon disappearing when resizing main window
  - hiro/Windows: remove WS_DISABLED from StatusBar to fix window
    resize grip
      - this is experimental: I initially used WS_DISABLED to work
        around a focus bug
      - yet trying things now, said bug seems(?) to have gone away at
        some point ...
  - bsnes: added advanced settings panel with real-time driver change
    support

I'd like feedback on the real-time driver change, for possible
consideration into adding this to higan as well.

Some drivers just crash, it's a fact of life. The ASIO driver in
particular likes to crash inside the driver itself, without any error
messages ever returned to try and catch.

When you try to change a driver with a game loaded, it gives you a scary
warning, asking if you want to proceed.

When you change a driver, it sets a crash flag, and if the driver
crashes while initializing, then restarting bsnes will disable the
errant driver. If it fails in a recoverable way, then it sets the driver
to “None” and warns you that the driver cannot be used.

What I'm thinking of further adding is to call emulator→save() to
write out the save RAM contents beforehand (although the periodic
auto-saving RAM will handle this anyway when it's enabled), and possibly
it might be wise to capture an emulator save state, although those can't
be taken without advancing the emulator to the next frame, so that might
not be a good idea.

I'm also thinking we should show some kind of message somewhere when a
driver is set to “None”. The status bar can be hidden, so perhaps on the
title bar? Or maybe just a warning on startup that a driver is set to
“None”.
2018-05-25 18:02:38 +10:00
Tim Allen
ec4ab1dc11 Update GitLab CI settings.
We shouldn't need to specify static compiler helpers anymore,
and ruby now uses SDL2.0 rather than 1.2.
2018-05-24 13:10:24 +10:00
Tim Allen
5961ea9c03 Update to v106r26 release.
byuu says:

Changelog:

  - nall: added -static-libgcc -static-libstdc++ to Windows/GCC link
    flags
  - bsnes, higan: added program icons to main window when game isn't
    loaded
  - bsnes: improved recent games menu sorting
  - bsnes: fixed multi-game recent game loading on Windows
  - bsnes: completed path override support
  - bsnes, higan: added screensaver suppression on Windows
  - icarus: add 32K volatile RAM to SuperFX boards that report no RAM
    (fixes Starfox)
  - bsnes, higan: added automatic dependency generation [Talarubi]
  - hiro/GTK: appending actions to menus restores enabled() state
  - higan: use board node inside manifest.bml if it exists
  - bsnes: added blur emulation and color emulation options to view menu
  - ruby: upgraded input.sdl to SDL 2.0 (though it makes no functional
    difference sadly)
  - ruby: removed video.sdl (due to deprecating SDL 1.2)
  - nall, ruby: improvements to HID class (generic vendor and product
    IDs)

Errata:

  - bsnes, higan: on Windows, Application::Windows::onScreenSaver needs
    `[&]` lambda capture, not `[]`
      - find it in presentation/presentation.cpp
2018-05-24 12:14:17 +10:00
Tim Allen
3353efd3a1 Update to v106r25 release.
byuu says:

Changelog:

  - bsnes:
      - added full input mapping support (multi-mapping, digital+analog
        inputs, rumble, hotkeys, etc)
      - can now load multi-part games (eg Super Game Boy) from the
        command-line
      - added recent games menu with list clear function; supports
        multi-part games (sorting logic incomplete)
      - added automatic binding of gamepads on new configuration files
      - added view scaling support with aspect correction, overscan
        cropping, and integral scaling modes
      - added video shader support
      - added status bar (can be hidden)
      - added save states (both menu and hotkeys)
      - added fullscreen mode support
      - added support for loading compressed (ZIP) archives for any
        supported media type (SNES, GB, etc)
      - added frame counter
      - added auto-memory saving
      - added pause / block-input modes when main window loses focus
      - added --fullscreen command-line option to start bsnes in
        fullscreen mode
      - added input settings panel
      - added hotkeys settings panel
      - added path settings panel (paths aren't actually used set, but
        can be assigned)
  - higan: fixed macOS install rule [Sintendo]
  - higan: minor UI code cleanups
  - nall: renamed Processor to Architecture to fix macOS builds
    [Sintendo]

Yeah, you read right: recent games menu, path settings. And dynamic rate
control + screensaver suppression is on the todo list. I'm not fucking
around this time. I really want to make something special here.
2018-05-23 13:45:24 +10:00
Tim Allen
a73a94f331 Update to v106r24 release.
byuu says:

Changelog:
* yes.

But seriously, a list of changes on a pre-alpha GUI is going to get annoying.

Basically, work on embedding stuff in the binary, firmware loading (both
appended to the ROM and in a firmware/ subfolder) added, SGB games can be
loaded, config file holds more values for driver settings, added ruby drivers to
other platforms, etc.
2018-05-20 14:39:29 +10:00
Tim Allen
7ee1534093 bsnes won't need icarus to load games. 2018-05-19 15:39:48 +10:00
Tim Allen
87e2154ea1 Update the link to the nightly builds. 2018-05-19 12:54:41 +10:00
Tim Allen
d8bd1fca1f Temporarily disable bsnes binaries at byuu's request.
Once it gets to the point of actually doing something, we can re-enable them.
2018-05-19 12:52:59 +10:00
Tim Allen
7acbf5c3dd Ignore bsnes binaries too. 2018-05-19 12:52:26 +10:00
Tim Allen
ea11c6d098 Update to v106r23 release.
byuu says:

Changelog:

  - bsnes: work on the new GUI; can load games now, but no input support
    yet
  - icarus: heuristics game/label uses filename instead of internal
    header name
2018-05-19 12:51:34 +10:00
Tim Allen
f5b96e9e9e Set up automatic WIP builds for the new bsnes front-end too. 2018-05-18 15:26:41 +10:00
Tim Allen
6078cdacbb Update to v106r22 release.
byuu says:

Changelog:

  - created new bsnes target (it currently does nothing)
  - Super Famicom: fixed BS Memory pack support in the MCC emulation
  - icarus: fixed manifest-free support for BS Memory flash-based
    cartridges
  - icarus: database improvements
2018-05-18 15:21:22 +10:00
Tim Allen
c2648faeab Mention the final manifest spec. 2018-05-17 20:20:31 +10:00
Tim Allen
2e14bd1c81 Let's prefer footnote-style link syntax. 2018-05-17 20:11:07 +10:00
Tim Allen
cd5dde0f62 Update more docs for v107. 2018-05-17 20:10:57 +10:00
Tim Allen
e5f19e49d4 Update importing docs for v107. 2018-05-17 17:10:06 +10:00
Tim Allen
c6ed8bb4b1 Warn that icarus in command-line mode doesn't offer much feedback. 2018-05-17 17:09:28 +10:00
Tim Allen
c24eb6e592 Update some import instructions. 2018-05-17 14:32:43 +10:00
Tim Allen
d537eaa0fd Start updating the docs for v107. 2018-05-17 14:32:42 +10:00
Tim Allen
8bbbc5e737 Update to v106r21 release.
byuu says:

Changelog:

  - higan: target-tomoko has been renamed to target-higan
  - Super Famicom: event has been renamed to
    processor(architecture=uPD78214)
  - Super Famicom: SNES-EVENT supported once more; under board IDs
    EVENT-CC92 and EVENT-PF94
  - Super Famicom: SNES-EVENT preliminarily set up to use DIP switch
    settings ala the Nintendo Super System (incomplete)
  - Super Famicom: MCC PSRAM moved inside the MCU, as it is remappable
  - Super Famicom: MCC emulation rewritten from scratch; it is now
    vastly more accurate than before
  - Super Famicom: added BSC-1A5B9P-01 board definition to database;
    corrected BS-MCC-RAM board definition
  - Super Famicom: moved SHVC-LN3B-01 RAM outside of
    processor(identifier=SDD1)
  - higan: when selecting a default game to load for a new system entry,
    it will change the system option to match the media type
  - higan: the load text box on the system entry window is now editable;
    can be used to erase entries
  - icarus: fixed bug in Famicom importing
  - icarus: importing unappended SNES coprocessor firmware will now
    rename the firmware properly
  - hiro/GTK,Qt: WM_CLASS is now set correctly in `argv[0]`, so
    applications should show “higan”, “icarus” instead of “hiro” now

Note: if you wish to run the BS-X town cartridge, the database currently
lists the download RAM as type “PSRAM”. This needs to be changed to
“RAM” in order to load properly. Otherwise, the emulator will bomb
out on the load window, because BSC-1A5B9P-01 expects PSRAM to always be
present, but it won't find it with the wrong memory type. I'll correct
this in the database in a later release. For now, you can copy the game
portion of the manifest to a new manifest.bml file and drop it into the
gamepak folder until I fix the database.
2018-05-17 13:37:29 +10:00
Tim Allen
210306e661 Update to v106r20 release.
byuu says:
Changelog:

  - Super Famicom: fixed loading of BS Memory and Sufami Turbo
    cartridges
  - Super Famicom: renamed NSS to DIP; as that's really all it is, it's
    not true NSS emulation
  - Super Famicom: slot loading now happens inside of board parsing
    instead of generically in loadCartridge()
  - Super Famicom: BS-X cartridges with flash memory now serialize their
    data and write it out to disk¹
  - icarus: fixed Famicom game importing (hopefully) and set file import
    title to “Load ROM File”

¹: there's no emulation of write commands yet, so the data is never
going to change anyway. This is just in preparation for more advanced
emulation of BS Memory cartridges.
2018-05-15 00:13:30 +10:00
Tim Allen
6847058210 Update to v106r19 release.
byuu says:

Changelog:

  - Super Famicom: everything outside of Nintendo Super System, Campus
    Challenge '92 and Powerfest '94 should play
  - Super Famicom: removed RAM from coprocessor/event (should use global
    RAM)
  - Super Famicom: removed RAM from SDD1 (should use global RAM)
  - icarus: fixed Super Famicom game importing [hex_usr]

Also worth reminding that you'll need to disable database lookup in
order to run the BS-X Town cartridge right now. Plus, Star Ocean's
database entry still has the RAM in the wrong spot. The MSU1 code is not
looking at the right locations for data, so it's not going to work in
this release either.

I need to figure out what to call coprocessor/event and coprocessor/nss,
as neither are slots or processors like everything else.

Outside of those issues, all games for all systems should be playable,
at least to the extent they were in v106.
2018-05-13 23:00:48 +10:00
Tim Allen
b7dca2f317 Update the CI builds to match the new directory structure. 2018-05-09 14:04:45 +10:00
Tim Allen
b69909be8d Update to v106r18 release.
byuu says:

Changelog:

  - major restructuring of board manifests
  - cleanup of generic board names
  - Super Famicom: updates to SA1, SuperFX, Cx4, SPC7110, EpsonRTC,
    SharpRTC load/save code
  - Super Famicom: added experimental SuperFX plot dithering fix
    [qwertymodo]
  - higan, icarus: rename shared folders to lowercase names; put .sys
    folders into new subfolder
      - Video Shaders/ → shaders/

      - Database/ → database/

      - Firmware/ → firmware/

      - \*.sys/ → systems/\*.sys/

So right now, only standard SNES games, SA-1, SuperFX, and Cx4 games
load. I have not tested SPC7110 or RTC support, because icarus import
seems to be completely broken? It's creating blank folders when I try it
now. I'll have to fix that ...

Since we are now up to thirteen systems, I've put the .sys folders into
a subfolder. This should declutter the main higan-windows release folder
a good deal. Linux users will need to re-run make install, or manually
move things into a new systems/ subfolder.

Same goes for icarus: lowercase the database/ and firmware/ folders or
re-run make install.

I don't know if qwertymodo's SuperFX fix is exactly correct or not.
Hopefully it is, but I didn't write a test ROM or anything to be
certain. Since SuperFX games should run, if people could please play
through some of them and look for any regressions, that'd be very much
appreciated.
2018-05-09 12:12:06 +10:00
Tim Allen
8617711ea2 Update to v106r17 release.
byuu says:

Changelog:

  - tomoko: the library menu is now called the systems menu (even in
    code)
  - tomoko: added icons to menus (disambiguates systems menu entries)
  - icarus: added missing .ws, .wsc extensions to scan dialog search
    list
  - higan: added Benesse - Pocket Challenge V2 emulation¹

¹: the Benesse - Pocket Challenge V2 is a WonderSwan (ASWAN) SoC
inside a custom designed shell. Games made for the WonderSwan (mostly)
run on the Pocket Challenge V2 and vice versa. The big difference is
that the Benesse has a different number of input buttons, that are also
named differently. Of course, right now, I don't know what the buttons
are named or where they're mapped on the 16-input keypad matrix I/O
port. It's also possible that the internal EEPROM doesn't exist, it
definitely has a unique (and also undumped) IPLROM, and other things.
The ROMs have their own .pc2 file extension. So it's getting its own
system entry.

What I'm going to do for v107 and above is utilize the new systems
configuration to mark the Benesse as hidden by default from the main
menu. I don't think anyone in the world will actually care or want to
play this, but there was really no reason not to add it.
2018-04-25 19:34:43 +10:00
Tim Allen
540d960e30 ikari_01 pointed out they contributed to commit bc0b86891
https://board.byuu.org/viewtopic.php?p=53609#p53609
2018-04-17 21:48:09 +10:00
Tim Allen
8023d9cbe8 Update to v106r15 release.
byuu says:

Changelog:

  - main menu renamed again (Library→System→Systems)
  - the 'Hidden' checkbox on system properties was moved to the main
    list as a 'Show' checkbox instead
  - the move up/move down buttons on the systems panel now function
  - added icons to indicate 'system' versus 'game boot' entries in the
    systems list

I still didn't add ComboEdit to the Windows hiro port, so once again
this will be Linux/BSD only.

I polished the browse button for selecting a boot game. It'll use a list
of all bootable media file extensions, so that if you double-click any
supported game, it'll select it instead of going inside the folder. If
you pick a non-bootable folder, like say a Sufami Turbo cartridge, it
will go inside the folder instead, because it will treat it like any
other regular folder.

Having a checkbox next to text in the same cell doesn't work so well on
lists that have an onActivate action. Say you tried to double-click the
“Name” field on the Systems tab, it would toggle the checkbox twice
before popping open the system properties editor window. So because of
this, I made the show checkbox its own column. And for consistency, I
did the same for the slot# on the cheat editor window.

As a bit of really pedantic polish: if there are no systems enabled,
then the main menu won't show the separator before the “Load ROM Image”
option anymore.

I think something is wrong with the Markup::Node::insert syntax, but I
realized I have Markup::Node::swap, and just used that for the up/down
arrows on the systems panel for now. But we should probably fix insert()
at some point ... sigh.
2018-04-17 19:18:14 +10:00
Tim Allen
0ea17abfea Update to v106r15 release.
byuu says:

Changelog:

  - Super Game Boy: fixed loading of boot ROM
  - hiro: added ComboEdit::setEditable(bool = true);
  - tomoko: added new systems settings panel

Note!!: this release will not compile on Windows or macOS due to the
missing ComboEdit control! I'll try to merge in hex's implementation
for the Windows release here soon. macOS users will probably be out of
luck for a while, sorry.

The new systems panel is an idea I've been meaning to implement for
quite a while, but finally got around to starting on it. It's still
fairly unpolished, but the basic idea is there for Linux/BSD users to
try out now.

So imagine the Super Game Boy, BS-X Satellaview, Sufami Turbo, and the
associated BS Memory Pack-slotted SNES cartridges. To play any of those,
you needed to choose Nintendo→Super Famicom, and then select the
relevant cartridge, and then select any slotted cartridges to play with
it.

This was acceptable-ish, if not ideal. But now imagine in the future if
we wanted to support the Famicom Disk System, which is technically a
cartridge that plugs into the Famicom deck. Or the PC Engine CD, which
has one of three special HuCards that must be inserted (ignoring the
Turbo Duo where it's built-in—I'm going to be emulating the Super CD
as if you're using a stock PCE CD.) Or the Mega CD, where there are
probably a half dozen or more BIOS + hardware revisions that are
region-specific, which connect to an expansion port that is identical to
the cartridge port save for the Mega Drive seeing an I/O register bit
toggled here.

In all of these cases, it's going to be a real pain to have to choose
the 'BIOS' every time you want to play a game for them.

I can't distribute these BIOSes with higan due to copyright
restrictions, and trying to ship dummy folders for every possible
combination would become quite odious, and difficult for people to use
(compare to setting up the Game Boy Advance system BIOS.)

And so I've created the new systems settings panel. Here, you can manage
a list of systems that show up under the higan library menu (now renamed
to “System”), where each entry contains name, boot, and hidden
parameters.

The name parameter is what shows up in the system menu. You can call any
system higan emulates whatever you like here. Don't like “Super
Famicom”? Change it to “SNES”, then.

The boot parameter is a combo edit with a dropdown for all of the
systems higan emulates. If you choose one of these, then the higan
system menu option will work exactly like in previous releases, and
prompt you for a cartridge. But if you choose the browse button next to
the combo edit control, you'll get to pick any gamepak from the higan
library of your choosing.

So you could choose the SGB2 BIOS, and name the menu option “Super Game
Boy 2”, and when you choose the menu option, it will load the SFC core,
load the SGB2 BIOS, and only prompt you for the Game Boy game you wish
to play on it. The same deal goes for the FDS, PCE-CD, Mega CD, Mega
Drive Sonic & Knuckles lock-on cartridge, BS-X Satellaview, SD Gundam
G-Next, etc. Whatever you want to be in the menu, you can put in there
by pointing higan at the appropriate 'BIOS' gamepak to load.

Astute readers have probably already noticed, but you can technically
use this on non-slotted games as well, thus creating instant boot
options for your absolute favorite games, if you so wanted. Point it at
Zelda 3, and you can boot it instantly from the main menu, without any
need for file selection.

The hidden option is a way to hide the system entries from the system
menu. Primarily this would be a fast way for users to disable emulation
cores they never use in higan, without having to remove the options.

The major concession with this change is the collapsing of the
per-manufacturer submenus. What this means is you will now have all
twelve higan emulated systems in the main menu by default. This makes
the list rather long, but ... oh well. I may try to offer some form of
grouping in the future, but the grouping defeats the “list order =
display order” design, and I'm not willing to auto-sort the list. I want
people to be able to control the ordering of the system menu, and have
added (as yet non-functional) sorting arrows for that purpose. I also
don't have a combined tree+table view widget in higan to try to and
group things. But ... we'll see how things go in the future.

Another idea is to add a specialty load option that opens up the user's
Emulation library path, and lets you pick a gamepak for any system,
which would boot the same way as when you drop a gamepak onto the higan
executable or main window. So say you almost never play Wonderswan
games, this would be a way to play them without them cluttering your
system menu list.

The “import ROM files” option has been removed. All it does is launch
icarus directly. I would rather users become familiar with using icarus.
The “load ROM file” option remains.

Anyway, this is all still a work in progress, so please give it time and
don't overload me with too many suggested changes right now, thanks :3
2018-04-16 18:58:13 +10:00
Tim Allen
8f61c267c5 Update to v106r14 release.
byuu says:

Changelog:

  - game/memory/type/battery → game/memory/volatile
  - (manufacturer.)content.type → (architecture.)content.type
  - nall: Markup::find() strips spaces from values in comparisons
  - higan: updated game manifest loading/saving code for all cores
  - GBA: flash memory ID is internally selected based on the
    manufacturer and memory size
  - SFC: ST018 (ARM6) frequency can be modified via game manifest now
  - WS: EEPROM::name removed (not useful)
  - icarus, genius: battery→volatile updates

I did my best to look over the diff between r13 and r14, but it's 84KiB
excluding the game database changes. It's just too much for me. I'd
greatly appreciate if someone could look over it and check for any
errors in this update. But more than likely, I suppose we'll iron out
any issues by determining which games fail to load.

Right now, I know the Super Game Boy support doesn't seem to work. But
all non-SFC cores should work fully, and all normal + NEC DSP SFC games
should work as well. Unsure about the rest.

Also, I'm planning to change the Game Boy “MBC1M” mapper to “MBC1#A” to
indicate it's an alternate wiring configuration of the stock MBC1, and
not a new mapper type.
2018-04-15 15:49:53 +10:00
Tim Allen
eaa2c1f6c0 Update to v106r13 release.
byuu says:

Changelog:

  - game/memory/category → game/memory/content
  - game/memory/model → game/memory/architecture
  - game/memory/identity → game/memory/identifier
  - Super Famicom: memory/content=Bitmap → memory/content=Save
  - Super Famicom: memory/architecture=DMG,MGB →
    memory/architecture=LR35902

The game manifest field names are now officially set in stone. I won't
change them again, I'll only add new fields if required.

As for the values in the field, I'm still undecided on the manufacturer
of the ST018, and I could be talked into different identifiers for the
Super Game Boy (SGB1/SGB2, DMG/MGB, or just ICD(2)?)

The board manifest format is still in flux, as is the choice of what to
name firmware files (it's between manufacturer and architecture, where
I'm leaning toward the latter currently.)

Board memory to Game memory mappings will require both the manufacturer
and architecture fields to match.

I'll be updating doc.byuu.org soon with the finalized game manifest
format.
2018-04-09 09:50:42 +10:00
Tim Allen
985610c167 Update to v106r12 release.
byuu says:

Changelog:

  - Emulator: update to final manifest syntax
  - Super Famicom: new board syntax (still experimental)
  - Super Famicom: match (manufacturer.)category.type instead of
    (model.)category.type

Errata:

  - Markup::Node::find() needs to be extended to support multiple
    subtype matches
  - Sufami Turbo ROM/RAM nodes are part of separate gamepaks; need to
    refactor this
2018-04-03 17:40:03 +10:00
Tim Allen
72b824cf1a Update to v106r11 release.
byuu says:

Changelog:

  - genius: improve sorting when game name is identical (eg revisions)
  - icarus, genius: update to finalized manifest syntax
2018-03-14 14:51:35 +11:00
Tim Allen
2dd35f984d Update to v106r10 release.
byuu says:

Changelog:

  - manifest: memory/battery now resides under type at
    memory/type/battery
  - genius: volatile option changed to battery; auto-disables when not
    RAM or RTC type
  - higan: added new Emulator::Game class to parse manifests for all
    emulated systems consistently
  - Super Famicom: board manifest appended to manifest viewer now
  - Super Famicom: cartridge class updated to use Emulator::Game objects
  - hiro: improve suppression of userland callbacks once
    Application::quit() is called
      - this fixes a crash in genius when closing the window with a tree
        view item selected

My intention is to remove Emulator::Interface::sha256(), as it's not
really useful. They'll be removed from save states as well. I never
bothered validating the SHA256 within them, because that'd be really
annoying for ROM hackers.

I also intend to rename Emulator::Interface::title() to label() instead.

Most everything is still broken. The SNES still needs all the board
definitions updated, all the other cores need to move to using
Emulator::Game.
2018-03-06 09:42:10 +11:00
Tim Allen
e216912ca3 Update to v106r09 release.
byuu says:

Changelog:

  - higan, icarus, genius: new manifest syntax (work in progress)

Pretty much only LoROM and HiROM SNES games will load right now, and RAM
will only work right if the save.ram file already exists to pull its
file size from (a temporary cheap hack was used.)

Basically, I'm just getting this out there for evaluation.

One minor errata is that I switched icarus to using “memory/battery” to
indicate battery-backed RAM, whereas genius still uses “memory/volatile”
to indicate non-battery-backed RAM.

I intend to make it “memory/battery” in genius, and have the field
auto-enable when RAM or RTC is selected for type (obviously allowing it
to be unchecked for volatile memory.)

I need to update all 64 production boards, and 25 of 29 generic boards,
to use the new slot syntax; and I also need to update every single core
in higan to use the new manifest game syntax. I want to build out a
generic manifest game parser that all emulation cores will use.

Once I finish this, I'll also need to write a database converter to
update all of my licensed game dumps to the new database syntax.

I also need to write up something for doc.byuu.org explaining the new
manifest game syntax. The manifest board syntax will still be “internal”
and subject to revisions, but once v107 is out, the gamepak manifest
format will be set in stone sans extensions.
2018-03-05 15:34:07 +11:00
Tim Allen
a4a3d611a6 The SGB2 timing change is fixed! 2018-02-21 20:58:39 +11:00
Tim Allen
5c55cc2c94 Update to v106r08 release.
byuu says:

Changelog:

  - Game Boy: fixed RAM/RTC saving¹
  - Super Famicom: ICD2 renamed to ICD (there exists an SGB prototype
    with a functionally identical ICD1)
  - Sufami Turbo: removed short-circuiting when loading an unlinkable
    cartridge into slot A²
  - Super Game Boy: the 20971520hz clock of the SGB2 is now emulated
  - Super Famicom: BSC-1Lxx (SA1) boards now prompt for BS memory
    cartridges; and can make use of them³
  - Super Famicom: fixed a potential for out-of-bounds reads with BS
    Memory flash carts

¹: I'm using a gross hack of replacing `type: ` with `type:` so that
`memory(type=...)` will match without the extra spaces. I need to
think about whether I want the BPath query syntax to strip whitespace or
not. But longer term, I want to finalize game/memory's design, and build
a higan/emulation/manifest parser that produces a nicer interface to
reading manifests for all cores, which will make this irrelevant for
higan anyway.

²: I don't think it's appropriate for higan to enforce this. Nothing
stops you from inserting games that can't be linked into a real Sufami
Turbo. I do short-circuit if you cancel the first load, but I may allow
loading an empty slot A with a populated slot B. I think the BIOS does
something when you do that. Probably just yells at you.

³: I know it's emulated correctly now, but I still don't know what
the heck changes when you load the SD Gundam G Next - Unit & Map
Collection BS Memory cartridge with SD Gundam G Next to actually test
it.
2018-02-21 20:53:49 +11:00
Tim Allen
c49d3b2006 Update to v106r07 release.
byuu says:

Changelog:

  - Super Game Boy: for the 50th time, higan won't segfault if you
    cancel the Game Boy cartridge load request
  - icarus: moved to new manifest syntax for all remaining systems
  - Game Boy: moved to new manifest syntax

Errata:

  - Game Boy: save RAM does not appear to be working for some reason
  - Famicom: higan won't even start to run this system; it just acts
    like a cartridge was never loaded ...
  - cores outside of the Super Famicom and Game Boy/Color will not run
    due to icarus/higan manifest syntax differences
2018-02-21 11:12:09 +11:00
Tim Allen
61091167b8 Disable Windows builds of genius. 2018-02-16 12:53:19 +11:00
Tim Allen
610d42d573 Update the docs to describe the new v106r06 firmware import system. 2018-02-16 12:33:32 +11:00
Tim Allen
f8a6cc2cbd Include icarus firmware with nightly builds. 2018-02-16 12:09:23 +11:00
Tim Allen
3a175ad2b0 Update to v106r06 release.
byuu says:

Changelog:

  - icarus: new Firmware/ folder, which is used to import external
    firmware when it's missing from the ROM image
  - icarus: improved Super Famicom heuristics; including Shift-JIS to
    UTF-8 encoding of game titles

Errata:

  - if firmware isn't appended, it still cuts out the size from the
    memory/program.rom file
  - boards.bml is still missing the new Japanese production boards
2018-02-16 12:07:49 +11:00
Tim Allen
5e330da4e8 Update to v106r05 release.
byuu says:

Changelog:

  - Super Famicom: added remaining generic board types
  - icarus: improved Super Famicom heuristics
  - icarus: reworked BS Memory heuristics
  - icarus: reworked Sufami Turbo heuristics

Notes: this is really complicated, and is going to take a long time to
work 100% smoothly again.

Starting off, I am trying to get rid of the weird edge case zero-byte
SRAM mapping for the Cx4. It has the RAM region present, but returns
logic low (0x00) instead of open bus, when SRAM isn't present. I started
by making it `map=ram` instead of `ram/map`, which is gross, and then it ended
up detecing the map tag ending in RAM and pulling the Cx4 data RAM into that
slot. Ugh. The preservation board mapping is still as it was before and will
need to be updated once I get the syntax down.

The BS Memory and Sufami Turbo moving to the new `game/memory`
ending means I can't use the SuperFamicom::Cartridge::loadMemory
function that looks at the old-style rom/ram tags. Because I didn't
write more code, the result is those sub-carts won't load now.

The old heuristics were short-circuiting on SA1 before bothering with
BS-X slots, so that's why SD Gundam G-Next wasn't asking for a data
pack. The problem is, I don't know where the BS-X pack maps to on this
cartridge. It's at c0-ef on the other BS-X slotted cartridges, but
that's mapped to the SA1 on regular SA1 cartridges, so ... for now, it's
not actually mapped in.

I'm still struggling with naming conventions on all these boards. I'll
make a public post about that, though.
2018-02-11 08:45:44 +11:00
Tim Allen
c38a771f22 Update to v106r04 release.
byuu says:

Changelog:

  - nall: `Markup::Node::operator[]` now uses `find()` instead of `lookup()`
    behind the scenes
  - Super Famicom: RAM memory ordering is now independent of ROM memory
    ordering
  - Super Famicom: added 19 new generic board definitions
  - icarus: improved Super Famicom heuristics generation

Not putting it in the changelog, but the SPC7110 RAM now has write
protection disabled again.

99% of games should now be playable with heuristics. The exceptions
should be:

  - 4MB LoROM games with SRAM (Ys 3, FE: Thracia 776)
  - 2MB DSP LoROM games
  - BS-X Town
  - BS-X slotted games
  - SA1 BSX slotted games
  - SPC7110 games without the RTC (Momotarou Dentetsu Happy, Super Power
    League 4)
  - SPC7110 7MB fan translation (wasn't supported earlier either)
  - ExLoROM games (wasn't supported earlier either)
  - Sufami Turbo
  - Campus Challenge '92 and Powerfest '94
  - ST010 is going to run at 15MHz instead of 11MHz
  - MSU1 (needs to be supported in higan, not icarus)

I'll add support for most of these before the release of v107.
2018-02-08 21:32:46 +11:00
Tim Allen
3d8be92550 Update to v106r3 release.
byuu says:

Changelog:

  - Super Famicom: update to newer board markup syntax
  - Super Famicom: update all mapped ROMs to be write-protected
      - errata: SPC7110 set ram.writeProtect(true), I'll fix it in the
        next WIP
  - icarus: rewrote the Super Famicom heuristics module from scratch

Instead of icarus heuristics generating higan-specific mappings, it now
generates generic board IDs that can be used by any emulator. I had
originally planned to print out real PCB ID codes here, but these board
mappings are meant to be more generic, and I don't want them to look
real. The pseudo-codes are easy to parse, for example: `DSP-LOROM-NVRAM`
for Super Mario Kart, `SUPERFX-RAM` for Doom.

I'm going to make a `Boards (Generic).bml` file that will contain mapping
definitions for every board. Until this is done, any games not in the SNES
preservation database will fail to play because the mapping information is
now missing.
2018-02-05 20:58:02 +11:00
Tim Allen
38fbcd5277 Include the new genius tool in higan nightlies, because why not? 2018-02-01 19:29:18 +11:00
Tim Allen
0cb3529547 higan's makefiles no longer require overriding the compiler. 2018-02-01 19:26:42 +11:00
Tim Allen
2f81b5a3e7 Update to v106r2 release.
byuu says:

Changelog:

  - Super Famicom: added support for loading manifests without embedded
    mapping information¹
  - genius: initial commit
  - various Makefile cleanups

¹: so the idea here is to try and aim for a stable manifest format,
and to allow direct transposition of icarus/genius database entries into
manifest files. The exact mechanics of how this is going to work is
currently in flux, but we'll get there.

For right now, `Super Famicom.sys` gains `boards.bml`, which is the raw
database from my board-editor tool, and higan itself tries to load
`boards.bml`, match an entry to game/board from the game's `manifest.bml`
file, and then transform it into the format currently used by higan. It
does this only when the game's `manifest.bml` file lacks a board node.
When such a board node exists, it works as previous versions of higan
did.

The only incompatible change right now is information/title is now
located at game/label. I may transition window title display to just use
the filenames instead.

Longer term, some thought is going to need to go into the format of the
`boards.bml` database itself, and at which point in the process I should
be transforming things.

Give it time, we'll refine this into something nicer.
2018-02-01 19:20:37 +11:00
Tim Allen
aef8d5e962 Update to v106r1 release.
byuu says:

Changelog:

  - Z80: infinite DD/FD prefixes will no longer cause an emulator crash;
    but will still deadlock savestates
  - Z80: emulated R incrementing on M1 cycles
  - Z80: `LD a, [ir]` should update flags [hex_usr]
  - Z80: minor code cleanups
  - tomoko: added “Pause Emulation” toggle to Tools menu
      - you can still use the hotkey to pause emulation before starting
        a game if you really want to
      - this will be useful if and when I re-add trace logging to
        capture instructions from power-on
  - icarus: more PAL games added to the SNES database

I hope I've implemented R correctly. It should only increment twice on
DD,FD CB xx instructions. LDI/LDD/LDIR/LDDR should work as expected as
well. It increments once when interrupts are executed (and not maksed.)
The top bit is ignored in increments.
2017-12-27 08:11:03 +11:00
Tim Allen
a5af5eab3c Always hyphenate "hand-held".
There were previously three instances of "hand-held" and one of
"handheld", so let's go with the majority
2017-11-21 14:47:42 +11:00
Tim Allen
41efdba45a Document the "soft reset" functionality.
This was added in v105r01, but I forgot about it until now. :/
2017-11-21 14:47:11 +11:00
Tim Allen
b55783c322 Update to v106 release.
byuu says:

Changelog (since v105tr1):

Changelog:

  - added Emulation/AutoSaveMemory/Interval setting to specify number of
    seconds between auto-saves
  - added Game Notes tool
  - added 64 new SNES PAL games to the icarus preservation database

The Games Notes tool is a new feature that gives you a blank text box to
enter notes about a game that you're playing: so you can write down
things like level select codes for games with save RAM, combo moves, or
whatever other information you'd like quick and easy access to.

This is kind of an experiment. Ideally, we'd wanna allow more
personalized information, drawings, etc. But hey, let's try it out and
see what people think.
2017-11-19 23:05:02 +11:00
Tim Allen
3ec08cebbe Add credits from Talarubi's README.TXT to the docs. 2017-11-12 17:10:37 +11:00
Tim Allen
8d7d452534 We don't need to override compiler when building.
It seems on Windows, `compiler` has defaulted to `g++` for a while now,
so we didn't need to override it in the `make` invocation.

Since v105r01, `compiler` defaults to `g++` on Linux too, so we don't
need to override it there either.
2017-11-11 22:21:31 +11:00
Tim Allen
56cb9c01a5 Hack around some code-block formatting errors.
By default, mkdocs uses "highlight.js" to apply syntax-highlighting
to code blocks. Left to its own devices, highlight.js will guess the
language being used in the code block, apply the "hljs" CSS class
(so the code block will be given a nice border, a sensible font-size,
etc.) and apply the appropriate formatting markup. If it guesses wrongly,
you can give a language hint on the opening line of the block.

If you use a language that highlight.js does not recognise, or
the special name "nohighlight", highlight.js will leave the block
alone. Unfortunately, in mkdocs' default theme, that means it will be
formatted like *inline* code, with a border and background that wraps
behind each line of the block.

In order to make a code-block that looks like a code-block, you have
to carefully pick a language that highlight.js has heard of, but whose
syntax is sufficiently different from whatever's in the block that no
unwanted highlighting will occur.

This seems to be fixed in the readthedocs theme that comes with mkdocs
0.16.1, but ReadTheDocs doesn't actually seem to be using that version. :/
2017-11-11 18:22:08 +11:00
Tim Allen
e9d2d56df9 Update to v105r1 release.
byuu says:

Changelog:

  - higan: readded support for soft-reset to Famicom, Super Famicom,
    Mega Drive cores (work in progress)
      - handhelds lack soft reset obviously
      - the PC Engine also lacks a physical reset button
      - the Master System's reset button acts like a gamepad button, so
        can't show up in the menu
  - Mega Drive: power cycle wasn't initializing CPU (M68K) or APU (Z80)
    RAM
  - Super Famicom: fix SPC700 opcode 0x3b regression; fixes Majuu Ou
    [Jonas Quinn]
  - Super Famicom: fix SharpRTC save regression; fixes Dai Kaijuu
    Monogatari II's real-time clock [Talarubi]
  - Super Famicom: fix EpsonRTC save regression; fixes Tengai Makyou
    Zero's real-time clock [Talarubi]
  - Super Famicom: removed `*::init()` functions, as they were never used
  - Super Famicom: removed all but two `*::load()` functions, as they
    were not used
  - higan: added option to auto-save backup RAM every five seconds
    (enabled by default)
      - this is in case the emulator crashes, or there's a power outage;
        turn it off under advanced settings if you want
  - libco: updated license from public domain to ISC, for consistency
    with nall, ruby, hiro
  - nall: Linux compiler defaults to g++; override with g++-version if
    g++ is <= 4.8
      - FreeBSD compiler default is going to remain g++49 until my dev
        box OS ships with g++ >= 4.9

Errata: I have weird RAM initialization constants, thanks to hex_usr
and onethirdxcubed for both finding this:
http://wiki.nesdev.com/w/index.php?title=CPU_power_up_state&diff=11711&oldid=11184

I'll remove this in the next WIP.
2017-11-07 09:05:54 +11:00
Tim Allen
6d487925d0 Build documentation in a UTF-8 locale. 2017-10-27 17:16:29 +11:00
Tim Allen
7aad868adb Fix copy/pasto for libretro build. 2017-10-27 17:04:46 +11:00
Tim Allen
dd06dd0fed Include the licence and documentation with each WIP build. 2017-10-27 16:58:41 +11:00
Tim Allen
3c26736d4b Fix mojibake in README.txt 2017-10-27 14:45:52 +11:00
Talarubi
f53cb33eb9 Add icarus to LICENSE.txt 2017-10-25 18:22:10 -04:00
Talarubi
426de198b7 Initial attempt at a README.txt for release archives 2017-10-25 18:02:54 -04:00
Talarubi
9e06857e4d Update version and license
Added LICENSE.txt and GPLv3.txt. Also updated libco documentation.

After discussion with byuu, libco gets a more specific ISC license
to match nall, ruby and hiro. higan, as clarified in LICENSE.txt,
continues to be GPL version 3 only (no "or later" clause).
2017-10-24 23:37:22 -04:00
Talarubi
e28aa32324 Fixed: Typo in SPC700 instruction table
https://board.byuu.org/viewtopic.php?p=48325#p48325

Per Screwtape and Jonas Quinn, this fixes 魔獣王 (Majuu Ou)
hanging at the title.
2017-10-24 23:37:21 -04:00
Talarubi
a9571ff5b8 Fixed: Restore SPC7110 and S-RTC time properly
Loading and unloading the RTC is a little odd, since it's normally
always powered in the first place. What we want, and what the load()
functions really do, is to resync using the saved timestamps or
reset it. unload() proper doesn't do anything.

However, an interface refactoring after v098 reordered the above
operations, and this (along with a typo, shh!) was causing the already
synced time to be cleared.

I've added checks so that whenever rtc.ram can't be found, load() gets
called with empty arguments to initialise the defaults (like putting
in a fresh battery).
2017-10-24 23:16:22 -04:00
Tim Allen
3d21e9afe0 Mention that higan does not yet emulate the timing of the SGB2. 2017-10-23 13:24:14 +11:00
Tim Allen
1cb37fc974 Fix another stupid typo. :( 2017-10-08 18:51:47 +11:00
Tim Allen
f92fc276af Fix typo in libretro path. 2017-10-08 18:44:25 +11:00
Tim Allen
18afd41a80 Using "--no-commit" didn't work, let's try something else. 2017-10-08 18:17:48 +11:00
Tim Allen
3507c522f4 Don't commit the libretro test merge.
git requires a name and email to be configured before it can commit a change. We're not going to push the merge result anywhere, so we don't strictly speaking need it to commit.
2017-10-08 17:54:49 +11:00
Tim Allen
7a4bfca106 Fix git merge command 2017-10-08 17:29:14 +11:00
Tim Allen
0db886f91d Building the libretro core doesn't need UI libraries, but needs git.
The git dependency is only because we want to do some merge shenanigans for testing purposes.
2017-10-08 14:33:16 +11:00
Tim Allen
904d11a3f7 Next release, we need to update the libretro branch too. 2017-10-08 14:33:11 +11:00
Tim Allen
214b921388 Test that the libretro core builds with each change to higan. 2017-10-08 14:25:42 +11:00
Tim Allen
b8b5aef165 Update the release instructions after their first use. 2017-10-07 20:00:17 +11:00
1277 changed files with 66441 additions and 35306 deletions

5
.gitignore vendored
View File

@@ -1,3 +1,4 @@
higan/profile/WonderSwan.sys/internal.ram
higan/profile/WonderSwan Color.sys/internal.ram
higan/systems/WonderSwan.sys/internal.ram
higan/systems/WonderSwan Color.sys/internal.ram
higan/systems/Super Famicom.sys/configuration.bml
docs_build/

View File

@@ -1,39 +1,71 @@
# NOTE: This file is not part of the official higan source, it's been added
# to help build WIP binaries with minimal fuss.
image: debian:stable
image: ubuntu:latest
linux-x86_64-binaries:
higan-linux-x86_64-binaries:
script:
- apt-get update && apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl1.2-dev libxv-dev libao-dev libopenal-dev libudev-dev
- make -C icarus compiler=g++
- make -C higan compiler=g++
- apt-get update && apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl2-dev libxv-dev libao-dev libopenal-dev libudev-dev mkdocs
- make -C genius
- make -C icarus
- make -C higan target=higan
- LC_ALL=C.UTF-8 mkdocs build
- mkdir higan-nightly
- cp -a genius/out/genius higan-nightly/genius
- cp -a icarus/out/icarus higan-nightly/icarus
- cp -a icarus/Database higan-nightly/
- cp -a icarus/Firmware higan-nightly/
- cp -a higan/out/higan higan-nightly/higan
- cp -a higan/systems/* higan-nightly/
- cp -a shaders "higan-nightly/Video Shaders"
- cp -a higan/systems/ higan-nightly/
- cp -a shaders higan-nightly/
- cp -a docs_build higan-nightly/docs
- cp -a GPLv3.txt higan-nightly/
artifacts:
paths:
- higan-nightly/*
windows-x86_64-binaries:
# This is a normal Windows cross-compile process, except that
# nall::chrono tries to use clock_gettime on Windows even
# though it's a POSIX function, and for some weird reason mingw has
# clock_gettime() in the pthread library.
bsnes-linux-x86_64-binaries:
script:
- apt-get update && apt-get -y install build-essential mingw-w64
- sed -i -e 's/-lole32/& -static -lpthread/' nall/GNUmakefile
- make -C icarus platform=windows compiler="x86_64-w64-mingw32-g++ -static-libgcc -static-libstdc++" windres="x86_64-w64-mingw32-windres"
- make -C higan platform=windows compiler="x86_64-w64-mingw32-g++ -static-libgcc -static-libstdc++" windres="x86_64-w64-mingw32-windres"
- apt-get update && apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl2-dev libxv-dev libao-dev libopenal-dev libudev-dev
- make -C higan target=bsnes
- mkdir bsnes-nightly
- cp -a higan/out/bsnes bsnes-nightly/bsnes
- cp -a shaders bsnes-nightly/
- cp -a GPLv3.txt bsnes-nightly/
artifacts:
paths:
- bsnes-nightly/*
higan-windows-x86_64-binaries:
script:
- apt-get update && apt-get -y install build-essential mingw-w64 mkdocs
# genius does not currently build on Windows due to lack of a combo edit control in hiro
#- make -C genius platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
- make -C icarus platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
- make -C higan target=higan platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
- LC_ALL=C.UTF-8 mkdocs build
- mkdir higan-nightly
#- cp -a genius/out/genius higan-nightly/genius.exe
- cp -a icarus/out/icarus higan-nightly/icarus.exe
- cp -a icarus/Database higan-nightly/
- cp -a icarus/Firmware higan-nightly/
- cp -a higan/out/higan higan-nightly/higan.exe
- cp -a higan/systems/* higan-nightly/
- cp -a shaders "higan-nightly/Video Shaders"
- cp -a higan/systems/ higan-nightly/
- cp -a shaders higan-nightly/
- cp -a docs_build higan-nightly/docs
- cp -a GPLv3.txt higan-nightly/
artifacts:
paths:
- higan-nightly/*
bsnes-windows-x86_64-binaries:
script:
- apt-get update && apt-get -y install build-essential mingw-w64
- make -C higan target=bsnes platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
- mkdir bsnes-nightly
- cp -a higan/out/bsnes bsnes-nightly/bsnes.exe
- cp -a shaders bsnes-nightly/
- cp -a GPLv3.txt bsnes-nightly/
artifacts:
paths:
- bsnes-nightly/*

View File

@@ -2,8 +2,8 @@ Contributing to higan
=====================
If you would like to propose a change to higan,
you should create an account on the [official forums][f],
go to the "Projects" forum and the "higan" sub-forum,
you should create an account on the [unofficial forums][f],
go to the "Projects" forum,
and post your idea in a new topic there.
[f]: https://board.byuu.org/
[f]: https://helmet.kafuka.org/bboard/

674
GPLv3.txt Normal file
View File

@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

44
LICENSE.txt Normal file
View File

@@ -0,0 +1,44 @@
----------------------------------------------------------------------
higan - Suite of videogame console emulators
icarus - Game library importer for higan
Copyright © 2004-2017 byuu
https://byuu.org/
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, specifically version 3 of the License
and no other version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
----------------------------------------------------------------------
----------------------------------------------------------------------
hiro - User interface toolkit
libco - C cooperative threading library
nall - C++ template library
ruby - Hardware abstraction layer
Copyright © 2006-2017 byuu
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all
copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
----------------------------------------------------------------------

View File

@@ -27,11 +27,11 @@ Official higan resources
------------------------
- [Official homepage](https://byuu.org/emulation/higan/)
- [Official forum](https://board.byuu.org/viewforum.php?f=4)
Unofficial higan resources
--------------------------
- [Unofficial forum](https://helmet.kafuka.org/bboard/)
- Documentation for
[the current stable version][stadocs]
- [Source code repository](https://gitlab.com/higan/higan/)
@@ -43,6 +43,6 @@ Unofficial higan resources
[the latest WIP version][wipdocs]
[wipwin]: https://gitlab.com/higan/higan/-/jobs/artifacts/master/download?job=windows-x86_64-binaries
[wipwin]: https://gitlab.com/higan/higan/-/jobs/artifacts/master/download?job=higan-windows-x86_64-binaries
[stadocs]: https://higan.readthedocs.io/
[wipdocs]: https://higan.readthedocs.io/en/latest/

297
README.txt Normal file
View File

@@ -0,0 +1,297 @@

higan - "Now you're playing with fire!"
=======================================
higan is a multi-system emulator that began development on October 14th, 2004.
It currently plays games for the following systems:
* Nintendo Famicom (NES), Super Famicom (SNES)
* Nintendo Game Boy, Game Boy Color + Game Boy Advance
* Sega Master System, Game Gear + Mega Drive (Genesis)
* NEC PC Engine (TurboGrafx) + SuperGrafx
* Bandai WonderSwan + WonderSwan Color
Supported Systems
-----------------
* FreeBSD 10+
* Windows 7, 8, 10
* Linux 3.2+
* OS X 10.7 Lion or above
You'll need a fast CPU, with clock speed being vastly more important.
Dual-core is perfectly adequate, since higan doesn't make significant use
of more than one core.
For Intel, you're looking at a 3.5 GHz Haswell architecture or better,
whilst AMD users will want a similarly specced Ryzen build.
Controller Setup
----------------
First, you'll want to configure your controllers. Choose Settings -> Input and
pick the system you'd like to configure. If you have two players, or a special
game (eg. Mario Paint), you can pick the controller port and device here.
To assign inputs, double click the name and press the stick or button on your
controller. You can have multiple assignments, for example both keyboard and
joypad. The Erase button clears the assignments for one input; Reset clears
them all in one go.
(Normally you only need to do this once. But because of how USB works,
it's impossible to tell identical controllers apart. This means if you move
one to another port, it counts as a new device, and you'll have to reassign
the buttons or move it back.)
Loading Games
-------------
After this you can go to Library -> Load ROM File and select a game. higan
adds it to your library, and it should start immediately. (Game Boy Advance
titles need one more step; please see the FAQ below.)
To add games en masse, you can use Library -> Import ROM Files. This opens
icarus, where you can choose a folder of ROMs, then select the ones you want
to import.
In both cases, if you choose a system under the Library submenus, all games
added will show up in a file browser under the Emulation folder in your user
profile. The path can be changed under Settings -> Advanced if desired.
Controller Ports
----------------
If you're emulating a console, you need to plug the controllers in, since
there's no connection by default.
Usually this means selecting eg. Super Famicom -> Controller Port 1 -> Gamepad,
for example. However, some games require other peripherals like the SNES Mouse.
Troubleshooting & FAQ
---------------------
Q: What's the Library?
A: higan loads folders containing all the files needed to run the game.
Odds are you have PC games and music albums organised the same way.
This does mean that to play the games, you have to import them first.
If you're familiar with iTunes or Steam, you already know how this works!
Q: Importing vs. loading? What's the difference?
A: The "Library -> Load ROM File" menu is a shortcut. It adds the game to your
library, then opens it without the manual import process.
However, if you have lots of games to add at once, you'll want
"Import ROM Files" instead.
Q: Why's higan say I'm missing a file ("Game Boy Advance.sys/bios.rom")?
A: This is the ROM for the startup screen you see when you switch on the
Game Boy Advance. Games require it to run, but like other ROMs, it's
copyrighted and therefore not provided with higan.
Having acquired a copy, you'll have to drop it in the requested folder,
then rename it to bios.rom.
Q: Where are the games imported? Where did all my save files go?
A: Check the path under Settings -> Advanced. On Windows it'll probably be
something like C:\Users\<name>\Emulation, organised by system. The saves
are typically named save.ram.
Q: Where can I find the settings?
A: There's a few possible locations for settings.bml.
1) In the same folder as the higan executable.
2) In the older location if you previously installed higan:
C:\Users\<name>\AppData\Roaming\higan (Windows)
/home/<name>/.config/higan (BSD, Linux)
3) In the new location (created if the others aren't found):
C:\Users\<name>\AppData\Local\higan (Windows)
/home/<name>/.local/share/higan (BSD, Linux)
/Users/<name>/Library/Application Support/higan (Mac)
higan checks these in order, so you can make a portable install if you like.
(macOS normally hides the Library folder. To open it, switch to Finder,
hold the Option key and select Go -> Library from the menu.)
Q: I set up my gamepads, but they don't work!
A: Try configuring the ports found in the system menu (eg.
Super Famicom -> Controller Port 1 -> Gamepad). Like a real console,
fresh higan installs come without any controllers plugged in.
Q: I upgraded higan, why do I get a black screen? What's "Ignore Manifests?"
A: higan looks at a file called "manifest.bml" to get the information needed
to run each game. However, the format has changed over time, making older
manifests incompatible with newer higan releases.
If you tick "Settings -> Advanced -> Ignore Manifests," you might find this
resolves the problem. This can be useful for developers and testers.
However, it breaks a few titles that require manifests to work!
Should you find yourself in this situation, consider removing manifest.bml.
(By default, no manifests are created; higan looks at the files in the
game folder, and with the help of a database, tries to regenerate the
correct one each time you load the game.)
Q: I have "Ignore Manifests" ticked, but the game won't run?
A: A few games have especially quirky setups that require manifests for
the time being, so you'll need to untick this option:
* Far East of Eden: Tengai Makyou Zero (English translation only)
* Campus Challenge '92
* PowerFest '94
Q: Why's the audio lag, stutter, distort, or sound robotic?
A: If you have an Atom, certain Celeron models, or an older AMD processor
(or even an especially old Intel such as a Core 2 Duo)... then these aren't
fast enough, sorry. :(
Try going into the Settings -> Advanced menu, then pick a different audio
driver and restart higan. WASAPI can be fussy on some devices.
Select Settings -> Audio and experiment with the latency. Larger values
should be more reliable, with the downside of laggier game controls.
Occasionally software that hooks into the system or other apps, for example
mouse settings panels, can cause lag and other problems.
Because higan is CPU-intensive and single-threaded, it can interact badly
with video capture which is yet another burden on the system. If you're
trying to stream or broadcast, and you have Windows 7, consider disabling
DWM. Also, look up how to configure hardware encoding (eg. QuickSync).
Q: Can I get smoother video?
A: Try Settings -> Video -> Exclusive mode, then switch to fullscreen. This
currently requires the Direct3D video driver under Settings -> Advanced
in order to work.
(Exclusive fullscreen is pretty experimental at the moment.
There are cases where it fails badly, so save your work!)
Exclusive mode will normally yield what's known as "tearing." If this
bothers you, there's an alternative... albeit one with serious gotchas,
which is why it's hidden away.
Close higan, then open up settings.bml and look for the following:
Video
Driver:Direct3D
Synchronize:false
...
Change false to true, save the file, then start higan and untick
Settings -> Synchronize Audio.
Keep in mind that this setting can and will reduce sound quality, as GPUs
and sound cards in modern PCs generally are not synchronised with each
other. The second big consideration is that your refresh rate needs to
match the game.
PAL and NTSC titles run at 50 Hz and 60 Hz, respectively. This applies to
all console systems. Of the handhelds: Game Boy, Game Boy Color and
Game Boy Advance run at 60 Hz, while WonderSwan runs at 75 Hz.
This means you'll need a monitor that supports these frequencies, set to
the appropriate display mode. Not all of them do. If your refresh rate
doesn't match, games will run at the wrong speed.
Online Resources
----------------
Official homepage:
https://byuu.org/emulation/higan
Unofficial forum:
https://helmet.kafuka.org/bboard/
Unoffical source code repository + documentation:
https://gitlab.com/higan/higan
https://higan.readthedocs.io
Info on game folders and firmware:
https://byuu.org/emulation/higan/game-paks
https://byuu.org/emulation/higan/firmware
Donations:
https://patreon.com/byuu
Commercial use:
https://byuu.org/emulation/higan/licensing
Credits
-------
Original author:
byuu
We'd like to acknowledge many invaluable contributions made to higan
by the following individuals:
Andreas Naive Hendricks266 Overload
Ange Albertini hex_usr p4plus2
anomie jchadwick quequotion
AWJ Jonas Quinn RedDwarf
Bisqwit kode54 Richard Bannister
blargg krom Ryphecha
Łukasz Krawczyk Lioncash segher
Cydrak Lord Nightmare Sintendo
Danish lowkey SuperMikeMan
DMV27 MerryMage tetsuo55
Dr. Decapitator Matthew Callis TmEE
endrift mightymo TRAC
Fatbag Nach wareya
FitzRoy ncbncb zones
gekkio neviksti
GIGO OV2
It's been a long, wild ride... apologies to anyone we've missed!
For more information, please see:
https://board.byuu.org/viewtopic.php?f=4&t=1631&p=41575#p41575
License
-------
higan is provided under the GNU General Public License, version 3.
However, certain libraries may be used under the more permissive ISC license.
Please see LICENSE.txt for details.

View File

@@ -8,5 +8,5 @@ checklink \
--summary \
--broken \
--location=http://127.0.0.1:8000/ \
--exclude 'github.com|board.byuu.org' \
--exclude 'github.com|board.byuu.org|helmet.kafuka.org' \
http://127.0.0.1:8000/

View File

@@ -8,7 +8,7 @@ higan would create a game folder named `dkc3.sfc`,
and inside it store the game data as `program.rom`
and the save data as `save.ram`:
```text
```python
+- Super Famicom
|
+- dkc3.sfc
@@ -27,7 +27,7 @@ For example,
if another emulator loaded the game `dkc3.sfc`
it might store the save data in `dkc3.srm`:
```text
```python
+- Super Famicom
|
+- dkc3.sfc
@@ -53,7 +53,7 @@ higan can use a larger number of files per game.
For example,
higan's low-level emulation of Super Famicom co-processors
often requires [separate firmware files][firmware].
higan's [MSU-1 feature][msu1]
higan's [MSU1 feature][msu1]
supports up to 99 audio tracks per game,
and higan supports up to 133 save-states per game.
Thus,
@@ -70,7 +70,7 @@ like save-states and the cheat database
to be kept separate from the game's actual data,
by putting it in a sub-folder.
[msu1]: ../guides/import.md#msu-1-games
[msu1]: ../guides/import.md#msu1-games
[firmware]: ../guides/import.md#games-with-co-processor-firmware
For a more detailed motivation for game folders,
@@ -128,19 +128,27 @@ to all emulators that support them:
will create this file.
- `*.data.rom`, `*.program.rom`:
Files named like this are usually [co-processor firmware][firmware].
- `msu1.rom`:
Holds streamable data for [the MSU-1][msu1].
Files used by higan's [MSU1 extension][msu1]
are in the `msu1` sub-folder:
- `data.rom`:
Holds data that the MSU1 can stream.
- `track-*.pcm`:
Holds streamable audio for [the MSU-1][msu1].
Holds audio that the MSU1 can stream.
Files that are only useful to higan specifically
are placed in a `higan` sub-folder:
- `cheats.bml`:
All information present in
[the Cheat Editor](../interface/higan-tools.md#the-cheat-editor)
the [Cheat Editor](../interface/higan-tools.md#cheat-editor)
is stored here.
- `notes.txt`:
Everything entered in the [Game Notes] is stored here.
- `states/quick/slot-*.bst`:
All [Quick States](save-states.md#quick-states) are stored here.
- `states/managed/slot-*.bst`:
All [Manager States](save-states.md#manager-states) are stored here.
[Game Notes]: ../interface/higan-tools.md#game-notes

View File

@@ -4,8 +4,8 @@ is the folder where all the
When [icarus](../interface/icarus.md) imports a game,
it creates or updates
the corresponding game folder in the game library.
When you use the console sub-menu items
in [higan's Library menu](../interface/higan.md#the-library-menu),
When you use the items in
[higan's Systems menu](../interface/higan.md#the-systems-menu),
higan shows you the games for that console
that are already in the library.

View File

@@ -9,7 +9,7 @@ and to the console itself.
If you load a game into higan,
you can look at the game's manifest
by opening [the Tools menu](../interface/higan.md#the-tools-menu)
and choosing [Manifest Viewer](../interface/higan-tools.md#the-manifest-viewer).
and choosing [Manifest Viewer](../interface/higan-tools.md#manifest-viewer).
Why manifests?
--------------
@@ -17,7 +17,7 @@ Why manifests?
For most consoles,
a manifest isn't strictly necessary:
the raw game data provides enough clues
for emulators to guess the circuit board configuration,
for emulators to guess the correct circuit board configuration,
or at least
to guess a *reasonable* configuration.
However,
@@ -100,6 +100,13 @@ heuristics will always be needed as a fallback,
but at least if the heuristics are wrong
they can be overridden.
If you are a homebrew author
or have dumped a previously-unknown cartridge,
and want to write a manifest yourself,
you should read the [official manifest specification][manifest].
[manifest]: https://doc.byuu.org/higan/manifests/
Ignoring manifests
------------------

View File

@@ -119,7 +119,7 @@ Manager states
--------------
higan's
[State Manager](../interface/higan-tools.md#the-state-manager)
[State Manager](../interface/higan-tools.md#state-manager)
allows you to create over a hundred save states,
and add a helpful description to each one.

63
docs/credits.md Normal file
View File

@@ -0,0 +1,63 @@
higan's original author:
- byuu
We'd like to acknowledge
many invaluable contributions made to higan
by the following individuals:
- Andreas Naive
- Ange Albertini
- anomie
- AWJ
- Bisqwit
- blargg
- Łukasz Krawczyk
- Cydrak
- Danish
- DMV27
- Dr. Decapitator
- endrift
- Fatbag
- FitzRoy
- gekkio
- GIGO
- Hendricks266
- hex_usr
- ikari_01
- jchadwick
- Jonas Quinn
- kode54
- krom
- Lioncash
- Lord Nightmare
- lowkey
- MerryMage
- Matthew Callis
- mightymo
- Nach
- ncbncb
- neviksti
- OV2
- Overload
- p4plus2
- quequotion
- RedDwarf
- Richard Bannister
- Ryphecha
- segher
- Sintendo
- SuperMikeMan
- tetsuo55
- TmEE
- TRAC
- wareya
- zones
It's been a long, wild ride...
apologies to anyone we've missed!
For more information,
see the [credits thread](
https://helmet.kafuka.org/byuubackup2/viewtopic.php@f=4&t=1631.html)
on the archive of the official forums.

View File

@@ -5,17 +5,17 @@ playing audio,
and accepting input from game controllers.
Or rather,
there are many standards,
and different ones work best
and different ones work better
on different computers.
Therefore,
higan comes with "drivers"
for video, audio and input,
so you can find the one that works best for you.
so you can find the one that works best for your computer.
To see what drivers you're currently using,
or to choose different ones,
go to
[the Advanced tab](../interface/higan-settings.md#advanced)
of the Settings window.
go to the [Advanced tab] of the Settings window.
[Advanced tab]: ../interface/higan-settings.md#advanced
Here are the most notable drivers
for each platform

View File

@@ -1,7 +1,6 @@
Before it can load a game,
higan requires that all the game's data
be stored correctly in
[the Game Library](../concepts/game-library.md).
be stored correctly in the [Game Library].
For [regular games](#regular-games)
this is simple,
but some games require special treatment,
@@ -11,7 +10,7 @@ unusual hardware.
Regular games
-------------
higan's importing tool, icarus, can import games
higan's importing tool, [icarus], can import games
in the most commonly-used formats
for each supported console,
and also those same formats inside `.zip` files
@@ -20,26 +19,20 @@ More advanced compression formats
like RAR or 7-zip are not supported.
To import a game,
open [the Library menu](../interface/higan.md#the-library-menu),
open the [Systems menu],
choose "Load ROM File ..."
to open [a filesystem browser](../interface/common.md#the-filesystem-browser),
to open a [filesystem browser],
choose the ROM file of the game you want to play,
and it will be imported into the library and start playing.
and it will be imported into the library
and (if possible) start playing.
**Note:**
If you want to import many games,
run icarus directly,
or choose "Import ROM Files ..."
from the Library menu
(which just runs icarus anyway).
See [the icarus documentation](../interface/icarus.md) for details.
run icarus directly.
See the [icarus] documentation for details.
To play a game for a particular console from your library,
open the Library menu,
pick the console manufacturer sub-menu
(Nintendo for the Super Famicom,
Bandai for the WonderSwan,
etc.)
open the [Systems menu],
then choose the appropriate console menu item.
A filesystem browser will appear
listing all the games in the library
@@ -75,13 +68,24 @@ higan requires a copy of the co-processor firmware
as well as the actual game data.
Unfortunately,
like games themselves,
co-processor firmware cannot legally be distributed,
so you'll need to obtain
copies of the relevant firmware data
yourself.
most co-processor firmware cannot legally be distributed,
so you'll need to
obtain copies of the relevant firmware data
for yourself.
To import a game that requires co-processor firmware,
you must first combine the game data and the firmware into a single file.
the easiest approach is to drop the firmware files into
icarus' `Firmware` directory
before importing the game.
The directory should be beside the icarus executable,
or it can be `%LOCALAPPDATA%\icarus\Firmware` (on Windows)
or `~/.local/share/icarus/Firmware/` (on Linux).
If the easy approach doesn't work for a particular game,
it may be because icarus has incorrectly guessed
which firmware that game needs.
To ensure icarus uses specific firmware with a specific game,
you must combine the game data and the firmware into a single file.
For example,
let's say you want to import *Super Bases Loaded 2* for the Super Famicom,
which is stored in the file `sbl2.sfc`
@@ -105,7 +109,7 @@ cat dsp1.program.rom dsp1.data.rom >> sbl2.sfc
**Note:**
For co-processor chips with multiple firmware files,
you must put the "program" file before the "data" file.
always put the "program" file before the "data" file.
Wikipedia [lists which Super Famicom games use which co-processors][wpec],
although not all co-processors require separate firmware.
@@ -127,13 +131,13 @@ here's the firmware files you'll need:
</thead>
<tbody>
<tr>
<th scope="row">CX4</th>
<th scope="row">CX4<br><sup>See Note 1</sup></th>
<td><code>cx4.data.rom</code></td>
<td>3072</td>
<td><code>ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18</code></td>
</tr>
<tr>
<th scope="row" rowspan=2>DSP1/1A<br><sup>See Note 1</sup></th>
<th scope="row" rowspan=2>DSP1/1A<br><sup>See Note 2</sup></th>
<td><code>dsp1.data.rom</code></td>
<td>2048</td>
<td><code>0b5da6533e55852ee8fc397977ec5576c5b9f1fb2e05656d8f87123a121b076e</code></td>
@@ -144,7 +148,7 @@ here's the firmware files you'll need:
<td><code>269584b347a22953a2989494c850a7c1c027f4ca5add517a60e0c7d8833d0fac</code></td>
</tr>
<tr>
<th scope="row" rowspan=2>DSP1B<br><sup>See Note 2</sup></th>
<th scope="row" rowspan=2>DSP1B<br><sup>See Note 3</sup></th>
<td><code>dsp1b.data.rom</code></td>
<td>2048</td>
<td><code>8546cbac530830446bb8a277f6b139d4ad64d650bdbac7e4e150e2f095665049</code></td>
@@ -213,53 +217,54 @@ here's the firmware files you'll need:
<th scope="row" rowspan="2">ST018</th>
<td><code>st018.data.rom</code></td>
<td>32768</td>
<td><code>b5377d1bebe8adc507a024b6e2b9b8fdf4877e451da84fbad05dff4e4a70311e</code></td>
<td><code>c67032238b7182696cb80cf41b61bacda91adb2120b5370bea20c9dbf5cc79b8</code></td>
</tr>
<tr>
<td><code>st018.program.rom</code></td>
<td>131072</td>
<td><code>d90a5cda380e81cb9ba11a9da7539b173c49b31bedc7a3ac9c3c8b3f97e89e14</code></td>
<td><code>6cceff3c6945bb2672040066d218efcd2f31492f3f5c28916c8e53435c2c887e</code></td>
</tr>
</tbody>
</table>
**Note 1:**
The CX4 firmware is shipped with higan,
because it just contains mathematical tables
and not a copyrightable program.
**Note 2:**
The DSP1 and DSP1A are physically different,
but the firmware inside is identical.
**Note 2:**
**Note 3:**
The DSP1B is very similar to the DSP1A,
but fixes some bugs.
Note that icarus' heuristics cannot distinguish between
a game that uses the DSP1
and one that uses the DSP1B,
so if it cannot find your game in its manifest database,
so if it cannot find your game in its database,
it will assume it uses DSP1B.
Many games work just as well with either variant,
but *Pilotwings* requires the DSP1 firmware,
while *Ballz 3D* requires the DSP1B.
If you try to import a game
using the "Import ROM Files ..." option
in [the Library menu](../interface/higan.md#the-library-menu)
(or using icarus directly)
but it does not include the correct firmware data,
If you try to import a game with icarus,
but it cannot find the required firmware files,
a window will appear saying
"Import completed, but with 1 errors. View log?"
(or however many games were lacking the correct firmware).
(or however many games were lacking firmware).
If you press "Yes",
a new window will appear listing the games that couldn't be imported,
and what problem was detected:
> [sbl2.sfc] ROM image is missing DSP1 firmware data
> [sbl2.sfc] ROM image is missing data: dsp1.program.rom; dsp1.data.rom
If you try to import a game
using the "Load ROM File ..." option
in [the Library menu](../interface/higan.md#the-library-menu)
using the "Load ROM File ..." option in the [Systems menu]
but it does not include the correct firmware data,
nothing will happen,
and higan will just sit there
with "No cartridge loaded" in
with "Unloaded" in
[the status bar](../interface/higan.md#the-status-bar).
Once a game with co-processor firmware is imported,
@@ -268,7 +273,7 @@ you can play it just like any [regular game](#regular-games).
Satellaview games
-----------------
The [Satellaview][wpbsx]
The [Satellaview]
was a satellite modem peripheral
released for the Super Famicom in Japan.
As well as the actual modem
@@ -282,7 +287,7 @@ This control cartridge was called
which in English is
*BS-X The Story of The Town Whose Name Was Stolen*.
[wpbsx]: https://en.wikipedia.org/wiki/Satellaview
[Satellaview]: https://en.wikipedia.org/wiki/Satellaview
The control cartridge had a slot that accepted
re-writable "memory paks",
@@ -301,8 +306,16 @@ containing extra content for specific games.
Importing a game that has a slot for a memory pak
is just like [importing a regular game](#regular-games).
Importing a memory pak is like importing a regular game,
but the name of the memory pak file *must* end in `.bs`
To import a memory pak,
you should use [icarus].
You can use the "Load ROM File ..." menu item
in the [Systems menu],
but higan cannot actually load a memory pak directly,
so once you choose a file to load
it looks like nothing has happened.
When importing a memory pak,
the name of the memory pak file *must* end in `.bs`
(if it's in a `.zip` file,
that's OK,
but the name *inside* the `.zip` file
@@ -317,8 +330,7 @@ Rename the file and it should work beautifully.
Playing a game that has a slot for a memory pak
is just like playing a regular game,
but after you have selected which game you want to play
higan will open another
[filesystem browser](../interface/common.md#the-filesystem-browser)
higan will open another [filesystem browser]
to let you pick which previously-imported memory pak
you want to insert into the game.
If you press "Cancel" at this point,
@@ -352,7 +364,7 @@ see [the BS-X Project](https://bsxproj.superfamicom.org/).
Sufami Turbo games
------------------
The [Sufami Turbo][wpst]
The [Sufami Turbo]
was a special cartridge released
for the Super Famicom in Japan.
The Sufami Turbo on its own does nothing,
@@ -365,8 +377,16 @@ from a game in slot B.
Importing the Sufami Turbo cartridge
is just like [importing a regular game](#regular-games).
Importing a mini-cartridge is like importing a regular game,
but the name of the mini-cartridge file *must* end in `.st`
To import a mini-cartridge,
you should use [icarus].
You can use the "Load ROM File ..." menu item
in the [Systems menu],
but higan cannot actually load a mini-cartridge directly,
so once you choose a file to load
it looks like nothing has happened.
When importing a mini-cartridge,
the name of the file *must* end in `.st`
(if it's in a `.zip` file,
that's OK,
but the name *inside* the `.zip` file
@@ -380,8 +400,7 @@ Rename the file and it should work beautifully.
To play a Sufami Turbo game,
load the Sufami Turbo cartridge like any other game.
higan will open another
[filesystem browser](../interface/common.md#the-filesystem-browser)
higan will open another [filesystem browser]
to let you pick which previously-imported mini-cartridge
you want to insert into slot A.
If you press "Cancel" at this point,
@@ -397,7 +416,12 @@ to let you choose a mini-cartridge for slot B.
If you press "Cancel" at this point,
the Sufami Turbo cartridge will boot without anything in slot B.
[wpst]: https://en.wikipedia.org/wiki/Sufami_Turbo
If you play Sufami Turbo games regularly,
you may want to add the Sufami Turbo base cartridge
to the [Systems menu]
so you don't have to tell higan where it is every time.
[Sufami Turbo]: https://en.wikipedia.org/wiki/Sufami_Turbo
Super Game Boy games
--------------------
@@ -407,17 +431,29 @@ released for the Super Famicom
(and all its regional variants around the world)
that allowed Game Boy games to be played
via the Super Famicom's controllers and video output.
The Super Game Boy 2 was released in Japan,
and had some minor extra features
beyond the original Super Game Boy,
but importing and playing games
works the same way in higan.
The Super Game Boy does not emulate the Game Boy hardware,
it physically includes all the Game Boy components
so compatibility with Game Boy games is high.
However, the Super Game Boy drives the Game Boy hardware
from the Super Famicom's timing signals, which means
games play 2.4% faster than on a real Game Boy.
The Super Game Boy cartridge includes
the complete hardware of an original
(black-and-white)
Game Boy,
so it needs a boot ROM:
The Super Game Boy 2 was a Japan-only release
that fixed the timing problem of the original Super Game Boy,
and included a different set of default borders.
higan emulates the Super Game Boy 2 completely,
including the timing change.
Because the Super Game Boy cartridge includes
the original Game Boy hardware,
it needs a boot ROM.
icarus includes these files
and can reliably decide when to use them,
so importing either Super Game Boy cartridge
is just like [importing a regular game](#regular-games).
In case you need to check the Super Game Boy boot roms,
here are their details:
<table>
<thead>
@@ -444,22 +480,19 @@ so it needs a boot ROM:
</tbody>
</table>
To import the SGB base cartridge,
you must first combine the base cartridge data
and the boot ROM into a single file,
just like
[games with co-processor firmware](#games-with-co-processor-firmware).
Then you may import it like [a regular game](#regular-games).
To play a Game Boy game in Super Game Boy mode,
load the Super Game Boy cartridge like any other game.
higan will open another
[filesystem browser](../interface/common.md#the-filesystem-browser)
higan will open another [filesystem browser]
to let you pick which previously-imported Game Boy game
you want to insert into the Super Game Boy.
If you press "Cancel" at this point,
higan will crash, so don't do that.
If you regularly play Game Boy games
through the Super Game Boy,
you may want to add it to the [Systems menu]
so you don't have to tell higan where it is every time.
**Note:**
Only games for the original, black-and-white Game Boy
can be used with the Super Game Boy.
@@ -471,14 +504,14 @@ for details.
[blackcarts]: ../notes.md#playing-game-boy-color-games-in-game-boy-mode
MSU-1 games
-----------
MSU1 games
----------
The MSU-1 is a fictional expansion chip
The MSU1 is a fictional expansion chip
invented by higan's author byuu,
designed to allow the Super Famicom
to stream data and audio.
Although the MSU-1 is not specific
Although the MSU1 is not specific
to any particular storage medium,
it gives the Super Famicom similar capabilities
to CD-based add-ons
@@ -486,32 +519,32 @@ like the Mega Drive's Mega CD
and the PC Engine's CD-ROM²,
such as CD-quality music and full-motion video.
Although the MSU-1 was invented for higan,
Although the MSU1 was invented for higan,
it is now supported by other Super Famicom emulators too.
The [SD2SNES][sd2snes] programmable cartridge
even allows you to play MSU-1 games on a real console.
The [SD2SNES] programmable cartridge
even allows you to play MSU1 games on a real console.
There are a number of homebrew games
that make use of the MSU-1,
that make use of the MSU1,
and also mods for commercial Super Famicom games
that add higher-quality music and sometimes video.
One thing to be aware of
when importing an MSU-1 game
when importing an MSU1 game
is that early firmware versions of the SD2SNES
had a bug that caused MSU-1 music to play too quietly.
had a bug that caused MSU1 music to play too quietly.
Skipping over [the full details][msu1vol],
the short version is this:
- If offered the choice between "boosted" or non-boosted audio,
you want the non-boosted version.
- If an MSU-1 mod for a commercial game offers
- If an MSU1 mod for a commercial game offers
"emulator" and "hardware" versions of the patch file,
it means the audio tracks are already boosted.
- Some
[third](https://www.zeldix.net/t1265-#18320)
[parties](https://www.zeldix.net/t1339-#19818)
have created replacement, non-boosted audio tracks
for the most popular MSU-1 mods.
for the most popular MSU1 mods.
If the mod you want to play has a replacement pack,
use it with the "hardware" version of the patch.
- Even without access to non-boosted audio tracks,
@@ -522,11 +555,11 @@ the short version is this:
distorting and clipping,
in which case try the "emulator" patch.
To import an MSU-1 game:
To import an MSU1 game:
1. If you have a single, large file
with the `.msu1` extension,
that is a pack for use with [Mercurial Magic][mermag],
that is a pack for use with [Mercurial Magic],
which can automatically set up a game folder
in the correct format.
Go read Mercurial Magic's documentation
@@ -534,29 +567,31 @@ To import an MSU-1 game:
2. Otherwise,
import the Super Famicom ROM with icarus,
[like a regular game](#regular-games).
- If this is a homebrew game with MSU-1 support,
- If this is a homebrew game with MSU1 support,
there will probably be an ordinary ROM
whose name ends in `.sfc`,
which is the file you want to import.
- If this is a commercial game modded for MSU-1 support,
- If this is a commercial game modded for MSU1 support,
there will probably be a patch file
whose name ends in `.ips` or `.bps`.
Get a copy of the correct version of the commercial game,
apply the patch with a tool like [Flips][flips],
apply the patch with a tool like [Flips],
then import the patched file.
- If there's "hardware" and "emulator" versions of the patch,
see "One thing to be aware of..." above.
3. Find the game folder in
[the game library](../concepts/game-library.md)
3. Find the game folder in the [game library]
that icarus created when it imported the game.
4. Copy the MSU-1 data file into the game folder.
- This should be named `msu1.rom`
4. Inside the game folder,
create a new folder named `msu1`.
5. Copy the MSU1 data file into the new `msu1` folder.
- This should be named `data.rom`
- If there's no file by that name,
look for a file with a `.msu` extension
look for a file named `msu1.rom`,
or a file with a `.msu` extension,
and rename it to `msu1.rom`.
- If there's no file ending in `.msu` either,
create an empty file named `msu1.rom`.
5. Copy the audio tracks into the game folder.
6. Copy the audio tracks into the game folder.
- If you have to choose between two sets of audio files,
see "One thing to be aware of..." above.
- These should be named
@@ -576,20 +611,20 @@ To import an MSU-1 game:
this game probably just doesn't use the audio-playback feature.
Once the game folder is set up,
playing an MSU-1 game is just like
playing an MSU1 game is just like
[a regular game](#regular-games).
[sd2snes]: https://sd2snes.de/
[flips]: http://www.romhacking.net/utilities/1040/
[SD2SNES]: https://sd2snes.de/
[Flips]: http://www.romhacking.net/utilities/1040/
[msu1vol]: http://blog.qwertymodo.com/2017/07/the-msu-1-volume-fiasco-explained.html
[mermag]: https://github.com/hex-usr/Mercurial-Magic/
[Mercurial Magic]: https://github.com/qwertymodo/Mercurial-Magic
Patched games
-------------
The console emulation community
has a long and vibrant history of game modding,
or [ROM hacking][rhdn],
or [ROM hacking],
including fan-translations,
new levels for existing games,
and more.
@@ -598,7 +633,7 @@ would be copyright infringement,
the changes are typically distributed as "patches",
a file containing a list of modifications to make,
that can be automatically applied by a "patcher" tool
like [Flips][flips].
like [Flips].
higan does not support soft-patching,
so if you want to play a patched game in higan,
@@ -608,7 +643,7 @@ creating a new, patched copy of the game.
Then you can import and play the patched game just like
[a regular game](#regular-games).
[rhdn]: http://www.romhacking.net/
[ROM hacking]: http://www.romhacking.net/
Game Boy Advance games
----------------------
@@ -627,7 +662,7 @@ GBA games can be imported and played just like
Note that some GBA games
have trouble with
[in-game saves](../notes#in-game-saves-and-the-game-boy-advance).
[in-game saves](../notes.md#in-game-saves-and-the-game-boy-advance).
PowerFest '94
-------------
@@ -645,79 +680,17 @@ switch between them after a specific time,
extract a score,
and display the combined total at the end.
icarus cannot automatically import
dumps of the PowerFest '94 ROMs,
but if you have the files,
you can import them manually.
Previous versions of higan
could emulate the PowerFest '94 cartridge,
but changes to higan's manifest system in v107
prevent PowerFest '94 from working in that version.
Support will likely be re-added in a future version,
but in the mean time you can use higan v106
and follow [that version's import instructions][pf94v106].
You will need the following files:
[pf94v106]: https://higan.readthedocs.io/en/v106/guides/import/#powerfest-94
<table>
<thead>
<tr>
<th>Part</th>
<th>Filename</th>
<th>Size (bytes)</th>
<th>SHA256</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Scoring</th>
<td><code>program.rom</code></td>
<td>262144</td>
<td><code>2fc9dca305ce3fb2f1a476567de500d50c174fbfbabd32b1b91c3ea6a731b4a1</code></td>
</tr>
<tr>
<th scope="row">Super Mario Bros. - The Lost Levels</th>
<td><code>slot-1.rom</code></td>
<td>524288</td>
<td><code>7fd86113c5f95f794d65807bb75ab91c93c914670c27fc813ffa2ca20a48705e</code></td>
</tr>
<tr>
<th scope="row">Super Mario Kart</th>
<td><code>slot-2.rom</code></td>
<td>524288</td>
<td><code>19eb77affbf8dd068f5d79a3cf80a2084fd73237cd1ae4e47192b4422449e64a</code></td>
</tr>
<tr>
<th scope="row">Ken Griffey Jr. Presents Major League Baseball</th>
<td><code>slot-3.rom</code></td>
<td>1048576</td>
<td><code>d47bc9f9a6289c4f2e7f6bf74095f6ed36b1043a761e3e729ac9af2fc39ae062</code></td>
</tr>
</tbody>
</table>
You will also need
the usual `dsp1.program.rom` and `dsp1.data.rom`
[co-processor firmware](#games-with-co-processor-firmware) files.
**Note:** the versions of
*Super Mario Kart*
and *Ken Griffey Jr...*
in *PowerFest '94*
are not the same as the stand-alone versions of those games.
To "import" *PowerFest '94*,
collect all the files mentioned above, then:
1. Inside [the game library](../concepts/game-library.md),
create the `Super Famicom` folder
(if it does not already exist).
2. Inside the `Super Famicom` folder,
create a `PowerFest '94.sfc` folder
(the `.sfc` extension is important,
but you can choose a different base name if you want).
3. Copy the various ROM files into the `PowerFest '94.sfc` folder.
To play *PowerFest '94*,
open the Library menu,
pick the Nintendo sub-menu,
then choose the Super Famicom sub-menu item
to open a filesystem browser listing
all the Super Famicom games in the library.
Select *PowerFest '94* from the list
and click the Open button,
or just double-click the game,
and it will begin playing.
[filesystem browser]: ../interface/common.md#the-filesystem-browser
[Game Library]: ../concepts/game-library.md
[icarus]: ../interface/icarus.md
[Systems menu]: ../interface/higan.md#the-systems-menu

View File

@@ -2,17 +2,16 @@ Most of the consoles higan emulates
were designed for low resolution NTSC televisions,
and their video output is chunky and blocky
by today's standards.
Video shaders customise how a console's video output
Video shaders customise how the emulated console's video output
is drawn to the computer screen,
and can clean up and smooth out the original video,
reproduce the scanlines and blurring of the original display,
or any other visual effect.
The available video shaders are listed in
the "Video Shaders" sub-menu of
[the Settings menu](../interface/higan.md#the-settings-menu).
the "Shader" sub-menu of the [Settings menu].
Which shaders are available depends on
the [video driver](drivers.md#video) higan is configured to use.
the [video driver] higan is configured to use.
Most drivers only support these shaders:
- **None**
@@ -20,7 +19,8 @@ Most drivers only support these shaders:
the colour of the single nearest console pixel,
sometimes called "nearest neighbour" scaling.
This produces unnaturally crisp and blocky images.
- If you use [aspect correction or non-integral scaling][ac],
- If you enable Scale, Stretch, or Aspect Correction modes
in the Output sub-menu of the [Settings menu],
neighbouring console pixels may be drawn
with a different number of computer pixels due to rounding errors,
causing a distracting rippling effect.
@@ -31,8 +31,6 @@ Most drivers only support these shaders:
sometimes called "bilinear" scaling.
This produces unnaturally blurry images.
[ac]: ../interface/higan-settings.md#video
In addition to those,
the OpenGL driver also supports custom shaders.
@@ -52,12 +50,13 @@ Where to get custom shaders
- higan includes some simple example shaders.
If your copy of higan did not come with shaders,
you can get them from
[the unofficial higan repository](https://gitlab.com/higan/higan/tree/master/shaders).
- [quark-shaders](https://github.com/hizzlekizzle/quark-shaders)
contains many high-quality shaders for use with higan.
you can get them from the [unofficial higan repository].
- [quark-shaders] contains many high-quality shaders for use with higan.
- You can write your own.
[unofficial higan repository]: https://gitlab.com/higan/higan/tree/master/shaders
[quark-shaders]: https://github.com/hizzlekizzle/quark-shaders
How to install custom shaders
-----------------------------
@@ -68,11 +67,11 @@ it should contain a file named `manifest.bml`,
and probably some `*.fs` or `*.vs` files.
Place the shader folder inside
the `Video Shaders` folder
the `shaders` folder
of your higan installation.
If you don't have a `Video Shaders` folder,
create it beside the `*.sys` folders
like `Game Boy Advance.sys` and `Super Famicom.sys`.
If you don't have a `shaders` folder,
create it beside the `systems` folder
and `settings.bml`.
- On Windows,
this is probably the folder containing `higan.exe`
@@ -80,10 +79,9 @@ like `Game Boy Advance.sys` and `Super Famicom.sys`.
this is probably `~/.local/share/higan`
Launch higan,
open the Settings menu,
and choose "Advanced ..."
to open [the Advanced tab](../interface/higan-settings.md#advanced)
of the Settings window.
open the [Settings menu],
and choose "Advanced ..." to open
the [Advanced tab] of the Settings window.
Under "Driver Selection",
make sure "Video" is set to "OpenGL".
If it wasn't already set that way,
@@ -91,7 +89,7 @@ you'll need to restart higan
for the change to take effect.
Open the Settings menu again,
choose the "Video Shader" sub-menu,
choose the "Shader" sub-menu,
and now the shaders you installed
should be listed at the bottom of the menu.
@@ -213,3 +211,7 @@ The PC Engine does not support an interlaced mode,
but its horizontal resolution is much more flexible
than the Super Famicom or Mega Drive,
and so it has the same problems with shaders as those consoles.
[Settings menu]: ../interface/higan.md#the-settings-menu
[video driver]: drivers.md#video
[Advanced tab]: ../interface/higan-settings.md#advanced

View File

@@ -1,7 +1,7 @@
higan, the multi-system emulator
================================
higan emulates a number of classic video-game consoles of the 1980s and 1990s,
higan emulates a number of 2D video-game consoles,
allowing you to play classic games on a modern general-purpose computer.
To get started with higan right away,
@@ -11,7 +11,7 @@ see the [Quick Start](qs.md) section of the documentation.
About higan
-----------
As of v104,
As of v107,
higan has top-tier support for the following consoles:
- Nintendo Super Famicom/Super Nintendo Entertainment System,
@@ -33,6 +33,7 @@ It also includes some level of support for these consoles:
- NEC SuperGrafx
- Bandai WonderSwan
- Bandai WonderSwan Color
- Pocket Challenge v2
**Note:** Some consoles were released under different names
in different geographic regions.
@@ -63,17 +64,17 @@ by the time you read this,
and it may contain errors or omissions.
If you find something that's wrong,
or you have a suggestion,
post a message on the official higan forum.
post a message on the unofficial forum.
Official higan resources
------------------------
- [Official homepage](https://byuu.org/emulation/higan/)
- [Official forum](https://board.byuu.org/viewforum.php?f=4)
Unofficial higan resources
--------------------------
- [Unofficial forum](https://helmet.kafuka.org/bboard/)
- [Source code repository](https://gitlab.com/higan/higan/)
archives official higan releases
and WIP snapshots
@@ -84,12 +85,12 @@ Unofficial higan resources
or smarter algorithms for scaling up to modern PC resolutions.
See [Using video shaders][shaders] below for details.
- [Mercurial Magic](https://github.com/hex-usr/Mercurial-Magic/)
is a tool for converting MSU-1 games and mods into a format
is a tool for converting MSU1 games and mods into a format
higan can use.
See [Importing MSU-1 games][msu1] for details.
See [Importing MSU1 games][msu1] for details.
[shaders]: guides/shaders.md
[msu1]: guides/import.md#msu-1-games
[msu1]: guides/import.md#msu1-games
There are also other projects
based on current or older versions of higan,
@@ -112,7 +113,7 @@ that you might want to check out.
is a fork of bsnes v094
adapted to work as a
[libretro](https://www.libretro.com/) emulation core.
- [nSide](https://github.com/hex-usr/nSide)
- [nSide](https://gitlab.com/hex-usr/nSide)
is a fork of higan that greatly enhances
its NES emulation support,
and adds minor features to the other cores too.

View File

@@ -23,7 +23,7 @@ If you have a real GBA and a flash-cart,
the Internet contains many tools
that will extract the BIOS image so it can be copied
to your desktop computer.
The correct GBA BIOS file is exactly 16384 bytes long,
The correct GBA BIOS file is exactly 16,384 bytes long,
and has the SHA256 hash
`fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570`.

View File

@@ -12,7 +12,7 @@ using the Git source-code management tool,
or by clicking the download button on the right-hand side of the web-page
and choosing an archive format.
You will also need GCC 4.9 or higher,
You will also need GCC 7 or higher,
including the C and C++ compiler,
GNU Make,
and development files
@@ -24,7 +24,7 @@ for the following libraries:
- Mesa
- gtksourceview 2.x
- Cairo
- SDL 1.2
- SDL 2.0
- libXv
- libAO
- OpenAL
@@ -35,7 +35,7 @@ On a Debian-derived Linux distribution
you can install everything you need with a command like:
sudo apt-get install build-essential libgtk2.0-dev libpulse-dev \
mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl1.2-dev \
mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl2-dev \
libxv-dev libao-dev libopenal-dev libudev-dev
Once you have all the dependencies installed,
@@ -53,9 +53,9 @@ being installed system-wide.
3. Type `cd ~/higan-src`
(or wherever you put the higan source)
and press Enter
4. Type `make -C higan compiler=g++` and press Enter
4. Type `make -C higan target=higan` and press Enter
to build the main higan executable
5. Type `make -C icarus compiler=g++` and press Enter
5. Type `make -C icarus` and press Enter
to build the icarus import tool
Installing a compiled build on Linux
@@ -68,7 +68,7 @@ as described in the previous section:
2. Type `cd ~/higan-src`
(or wherever you put the higan source)
and press Enter
3. Type `make -C higan install` and press Enter
3. Type `make -C higan target=higan install` and press Enter
to install higan and its supporting files
4. Type `make -C icarus install` and press Enter
to install icarus and its game database
@@ -123,7 +123,7 @@ as installed by the above instructions:
2. Type `cd ~/higan-src`
(or wherever you put the higan source)
and press Enter
3. Type `make -C higan uninstall` and press Enter
3. Type `make -C higan target=higan uninstall` and press Enter
4. Type `make -C icarus uninstall` and press Enter
To remove higan's configuration,

View File

@@ -50,38 +50,58 @@ using the Git source-code management tool,
or by clicking the download button on the right-hand side of the web-page
and choosing an archive format.
You will need a C++ compiler to compile higan.
We recommend installing [TDM64-GCC][tdm],
preferably the latest version
but anything newer than 4.9 should be fine.
You will need a C++ compiler that supports C++17 to compile higan.
We recommend installing the latest version of [MinGW-W64].
higan does not support building with clang++
(Clang is still not quite there yet for Windows)
nor Microsoft Visual C++
(last we checked, it didn't support all the C++ features higan uses).
**Note:** Make sure you get TDM64-GCC,
not TDM-GCC.
When compiled in x86 (32-bit) mode,
higan may crash at startup
because gcc targeting x86 does not support
Windows' structured exception handling (SEH).
Also,
historically in x86 mode
gcc has miscompiled a part of the NES emulation core.
See the higan forum
[for](https://board.byuu.org/viewtopic.php?p=41977#p41977)
[details](https://board.byuu.org/viewtopic.php?p=42253#p42253).
[MinGW-W64]: https://mingw-w64.org/
Once you've installed the compiler,
open a command-prompt window,
MinGW-W64 is available in a number of variants,
and the installer should ask you which you want.
- **Version:**
Version 8.1.0 is known to work,
later versions may work too.
- **Architecture:**
You *must* choose "x86_64", not "i686".
When built with an i686 compiler,
higan may crash at startup
because gcc targeting x86 does not support
Windows' structured exception handling (SEH).
Also,
historically in x86 mode
gcc has miscompiled a part of the NES emulation core.
See the archive of the official forum
[for](https://helmet.kafuka.org/byuubackup2/viewtopic.php@f=4&t=1636&start=20.html#p41977)
[details](https://helmet.kafuka.org/byuubackup2/viewtopic.php@f=4&t=1636&start=30.html#p42253).
- **Threads:**
Both options should work,
but higan is developed with the "posix" model.
- **Exception:**
You *must* choose "seh",
or higan may crash at startup.
If "seh" is not an option,
make sure "Architecture" is set to "x86_64".
- **Build Revision:**
Choose the largest number, whatever it is.
When the compiler is installed,
it adds a "Run terminal" shortcut to the Start menu
which opens a command-prompt
with all the compiler tools available.
To verify that the compiler is installed correctly,
launch the "Run Terminal" shortcut,
type `g++ --version`
then press Enter
to check it's installed correctly.
then press Enter.
You should see a message like
```text
g++ 1.2.3 20010101
Copyright (C) 2001 Free Software Foundation, Inc.
g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```
@@ -91,11 +111,11 @@ and the corresponding dates.
If you see an error message like
"'g++' is not recognized as an internal or external command,
operable program or batch file",
you may need to add the compiler's "bin" folder
to your computer's `%PATH%`.
make sure you're using the "Run terminal" shortcut,
or otherwise have MinGW-W64's "bin" directory in your `%PATH%`.
See the compiler's documentation for help with that.
Once mingw is installed and available from the command prompt:
Once the compiler is installed:
1. Put the higan source code in some convenient location,
like `C:\higan-src`
@@ -103,13 +123,11 @@ Once mingw is installed and available from the command prompt:
3. Type `cd C:\higan-src`
(or wherever you put the higan source)
and press Enter
4. Type `mingw32-make -C icarus compiler=g++` and press Enter
4. Type `mingw32-make -C icarus` and press Enter
to build the icarus import tool
5. Type `mingw32-make -C higan compiler=g++` and press Enter
5. Type `mingw32-make -C higan target=higan` and press Enter
to build the main higan executable
[tdm]: http://tdm-gcc.tdragon.net/download
Installing a compiled build on Windows
--------------------------------------
@@ -120,24 +138,20 @@ Installing a compiled build on Windows
into the new folder
3. Copy `C:\higan-src\icarus\Database` and its contents
into the new folder
4. Copy `C:\higan-src\higan\out\higan.exe`
4. Copy `C:\higan-src\icarus\Firmware` and its contents
into the new folder
5. Copy all the `*.sys` folders
in `C:\higan-src\higan\systems`
5. Copy `C:\higan-src\higan\out\higan.exe`
into the new folder
6. If the higan source includes a `shaders` folder,
make another new folder named `Video Shaders`
inside the new folder,
and copy all the `*.shader` folders
from `C:\higan-src\shaders\`
into the `Video Shaders` folder.
6. Copy `C:\higan-src\higan\systems`
into the new folder
7. If the higan source includes a `shaders` folder,
copy it into the new folder too.
The new folder should now contain
`icarus.exe`,
`higan.exe`,
a folder named `Database`,
and half a dozen folders named after the systems higan emulates
with `.sys` at the end.
and folders named `Database`, `Firmware`, `systems`,
and possibly `shaders`.
This is what you would get by downloading an official build,
as described under
[Installing an official release on Windows][instwin]

1
docs/interface/down.png Symbolic link
View File

@@ -0,0 +1 @@
../../hiro/resource/icon/go/down.png

View File

@@ -57,7 +57,8 @@ higan will prompt for one.
`SUBGAME2` is ignored.
When `GAME` refers to
the [Super Game Boy](../guides/import.md#super-game-boy-games),
the [Super Game Boy](../guides/import.md#super-game-boy-games)
or Super Game Boy 2,
`SUBGAME1` should be
the path to a game folder or ROM file
representing a Game Boy game to insert into the slot.

View File

@@ -6,7 +6,79 @@ and contains less-frequently-modified settings.
Most of these can be safely ignored,
or set once and never changed again.
This window has a tab for each main category of options:
This window has a tab for each main category of options.
Systems
=======
This tab configures the contents of
[the Systems menu](higan.md#the-systems-menu),
so you can make it easier to load the games you care about
and hide things that get in the way.
Each item in the list represents
a single item in the Systems menu.
If the box at the left is ticked,
that item will be included in the menu,
otherwise it will be hidden—but higan will remember its configuration
in case you want to show it again.
At the bottom left are
![up-arrow](up.png) and ![down-arrow](down.png) buttons.
These move the selected item
upward or downward in the list.
The **Append** button in the lower right
adds a new item to the end of the list.
It opens the [System Properties](#system-properties) dialog,
so you can enter the details of the new item.
If you don't want the new item to be at the end,
you can use the up and down buttons
in the lower left
to move it to its intended location.
The **Modify** button in the lower right
opens the [System Properties](#system-properties) dialog
for the selected item,
so you can make changes.
The **Remove** button in the lower right
removes the selected item from the list entirely.
Unlike hiding the item,
this forgets whatever configuration the item had.
System Properties
-----------------
This dialog appears when clicking "Append" or "Modify"
in the [Systems](#systems) tab.
It allows you to configure a new ("Append") or existing ("Modify") entry
in the [Systems menu](higan.md#the-systems-menu).
- **System** controls which console will be emulated
when this menu-item is chosen.
- **Load** controls what game will be loaded
into the emulated console
when this menu-item is chosen.
- If left blank,
higan will open [a filesystem browser](common.md#the-filesystem-browser)
allowing you to pick a previously-imported game from
the [game library](../concepts/game-library.md).
- If you choose a particular game,
higan will immediately load it
when the menu-item is chosen.
If the game requires additional data
(for example, the Super Game Boy requires a Game Boy cartridge)
higan will prompt for it.
- **Alias** controls the name of this item,
as displayed in the Systems menu.
- **Append** (present in "Append" mode) closes the dialog
and adds a new item with this configuration
to the list.
- **Modify** (present in "Modify" mode) closes the dialog
and updates the configuration of
the item being modified.
- **Cancel** closes the dialog without making any changes.
Video
=====
@@ -20,9 +92,9 @@ settings adjust the colour and brightness
of the emulated console's video output:
- **Saturation** adjusts the vibrancy of colours displayed,
where 0% makes things pure grey,
where 0% makes things black-and-white,
100% is normal,
and 200% is garishly brightly coloured.
and 200% is garishly exaggerated colour.
- **Gamma** adjusts how bright mid-range colours are
compared to the brightest colours,
where 100% is normal,
@@ -30,20 +102,23 @@ of the emulated console's video output:
This is in addition to
any adjustment applied by
the "Colors" option
in the "Video Emulation" sub-menu
in the "Emulation" sub-menu
of the [Settings menu](higan.md#the-settings-menu).
- **Luminance** adjusts the overall brightness,
where 100% is normal,
and 0% is totally black.
**Overscan Mask**
removes parts of
the video output that would have been hidden
by the bezel around the edge of
a standard-definition television screen.
Some games (particularly on the Famicom)
displayed random glitchy output in this area,
which can be distracting.
**Overscan Area**
controls what parts of the video output are hidden
when "Show Overscan Area" is disabled
in the "Output" sub-menu of
the [Settings menu](higan.md#the-settings-menu).
On a standard-definition television,
the outermost edges of the emulated console's video output
would have been hidden by the bezel,
so some games (particularly on the Famicom)
allowed random glitchy output to appear there,
assuming it wouldn't be visible.
- **Horizontal**
removes pixels from the left and right of the video output.
@@ -58,41 +133,7 @@ whether the Super Famicom is in
lo-res (256px) or hi-res (512px)
mode.
**Windowed Mode**
settings apply when higan is running
in a normal window.
- **Aspect Correction**
stretches the image to match the aspect ratio
produced by the original console hardware,
but can cause a "ripple" effect
during horizontal scrolling
due to rounding errors.
[Video shaders](../guides/shaders.md)
can reduce this effect.
- **Integral Scaling**
makes higan draw the emulated video output
at a whole-number multiple of the original size,
rather than completely filling the available space.
This means that every game pixel
uses the same number of computer pixels,
and avoids graphics looking chunky and uneven.
Note that Aspect Correction
is applied after integral scaling,
so some unevenness may be visible
even with this option enabled.
- **Adaptive Sizing**
automatically resizes the higan window
to fit snugly around the emulated video output
whenever it changes size
(because the user loaded a game for a different console,
chose a different option from
the [Video Scale sub-menu](higan.md#the-settings-menu),
toggled Aspect Correction, etc.)
When disabled,
higan generally respects manual resizing.
**Fullscreen Mode**
**Fullscreen**
settings apply
when higan is running fullscreen,
because it was started with the `--fullscreen`
@@ -100,10 +141,6 @@ because it was started with the `--fullscreen`
or because the user pressed
the Toggle Fullscreen [hotkey](higan-settings.md#hotkeys).
- **Aspect Correction**
behaves the same way as in Windowed mode above.
- **Integral Scaling**
behaves the same way as in Windowed mode above.
- **Exclusive Mode**
requests exclusive access
to the computer's video output
@@ -113,7 +150,7 @@ the Toggle Fullscreen [hotkey](higan-settings.md#hotkeys).
from drawing anything,
and may also temporarily disable any kind of compositing,
reducing video latency.
As of v104,
As of v107,
only the Direct3D video driver is capable of exclusive mode;
with other drivers this option does nothing.
@@ -164,52 +201,52 @@ before it is sent to your computer's speakers.
where 0% means only the left speaker produces sound,
50% means both speakers produce sound equally,
and 100% means only the right speaker produces sound.
- **Reverb** adds a slight reverberation effect
to the emulated console's audio output,
as though you were playing the game in a tunnel or small room.
Input
=====
This tab controls which PC inputs
are mapped to which emulated controllers.
This tab controls
how higan handles input for the emulated consoles.
**When focus is lost**
controls what happens when a game is loaded,
but higan is not the current foreground window.
- **Pause Emulation** automatically pauses emulation.
- **Block Input** allows emulation to keep running,
but higan will ignore all configured button presses.
If you're using the keyboard to emulate a controller,
this prevents typing in other applications
from messing with higan,
but music will keep playing.
- **Allow Input** allows emulation to continue as normal.
This allows somebody to play higan with a controller
in one window,
while somebody else types into another application
in another window.
The rest of this tab configures
the mapping from PC inputs to emulated controllers.
The exact PC inputs that can be mapped
depend on [the input driver](../guides/drivers.md#input).
General input settings:
To choose which of the possible controllers to configure:
- **Pause Emulation** automatically pauses emulation
when the main higan window
is not the current foreground window.
- **Allow Input** can be ticked
when "Pause Emulation" is *not* ticked,
and allows configured inputs to keep affecting higan
even when higan is running in the background.
This is particularly relevant if
you configure your PC keyboard to control higan:
if you tick this box,
and switch to a different application
leaving higan running in the background,
typing in that other application may affect
the emulated game running in higan
even though you can't see it!
- The first drop-down list controls
which console's ports appear in the second list.
- The second drop-down list controls
which port's compatible controllers appear in the third list.
- The third drop-down list controls
which controller's inputs are shown
in the mapping list below.
Choosing which of the possible controllers to configure:
Note that some consoles only allow particular controllers
to be used in a particular port.
For example,
the Super Scope controller for the Super Famicom
only works in Controller Port 2.
- The console selector chooses which console's inputs
to display in the mapping list below.
- The port selector chooses which port of the selected console
to display in the mapping list below.
- The controller selector chooses which controller
associated with the given console and port
to display in the mapping list below.
Note that some consoles only allow particular controllers
to be used in a particular port.
For example,
the Super Scope controller for the Super Famicom
only works in Controller Port 2.
Configuring the selected controller:
To configure the selected controller:
- The mapping list includes
every button and axis on the selected controller,
@@ -260,6 +297,13 @@ then click one of the
or "Mouse Y-axis"
buttons in the bottom-left of the window.
**Note:**
To use an controller axis mapped to a mouse axis,
higan will need to be in fullscreen mode,
or you'll need to press
the key mapped to "Toggle Mouse Capture"
on the [Hotkeys tab](#hotkeys).
If you start mapping a button or axis,
but decide you don't want to,
you can press Escape
@@ -267,10 +311,25 @@ to exit the "Press a key or button to map..." mode
without actually mapping anything.
**Note:**
Consoles in the Game Boy family include
The Game Boy and Game Boy Color consoles
have a "Cartridge" port with controllers
that are not really controllers:
- The "MBC5" controller is automatically used for
games whose cartridge includes the MBC5 memory-mapper
and a rumble motor,
like *Pokémon Pinball*.
See [Rumble Compatibility for Game Boy (Color)][gbcrumble]
for details.
- The "MBC7" controller is automatically used for
games whose cartridge includes the MBC7 memory-mapper
and an accelerometer,
like *Kirby Tilt 'n' Tumble*.
**Note:**
The Game Boy Advance console includes
a Rumble "input" which is really more of an output.
See [Rumble Compatibility for Game Boy (Color)][gbcrumble]
and [Rumble Compatibility for Game Boy Advance][gbarumble]
See [Rumble Compatibility for Game Boy Advance][gbarumble]
for details.
[gbcrumble]: ../notes.md#rumble-compatibility-for-game-boy-color
@@ -288,12 +347,12 @@ Hotkeys
This tab is like "Inputs" above,
except it contains controls for higan itself
instead of the emulated console.
instead of for the emulated console.
- **Toggle Fullscreen** puts higan into fullscreen mode,
where the menu and status bar are hidden,
and the emulated console's video output
is enlarged to cover the entire screen.
can cover the entire screen.
Toggling fullscreen also automatically captures the mouse.
- **Toggle Mouse Capture** hides the usual mouse-cursor,
and captures the mouse so it cannot leave the higan window.
@@ -308,13 +367,22 @@ instead of the emulated console.
- **Increment Quick State** selects the next [Quick State][qstates] slot.
The status bar will briefly display the new current slot number.
- **Pause Emulation** pauses the emulated console
until the Pause Emulation hotkey is pressed a second time.
until the Pause Emulation hotkey is pressed a second time,
or "Pause Emulation" is chosen from
[the Tools menu](higan.md#the-tools-menu)..
- **Fast Forward** disables audio and video synchronisation
for as long as it's held down,
so emulation proceeds as quickly as possible.
If your PC struggles to hit "real time"
(60fps for most emulated consoles),
this likely won't have any effect.
- **Soft Reset** restarts the emulated console's CPU
while leaving the console's memory untouched,
just like the "Soft Reset" menu item
in [the console menu](higan.md#the-console-menu).
This hotkey does nothing
when the "Soft Reset" item
does not appear in the console menu.
- **Power Cycle** turns the emulated console off and back on
(a "hard reset"),
just like the "Power Cycle" menu item
@@ -359,8 +427,7 @@ for help choosing which drivers you should use.
configures how higan interacts
with the [Game Library](../concepts/game-library.md).
- **Location** selects where higan
looks for games to load.
- **Location** tells higan where to look for games to load.
See [Moving the Game Library](../concepts/game-library.md#moving-the-game-library)
for more information.
- **Ignore Manifests** makes higan ignore
@@ -371,3 +438,16 @@ with the [Game Library](../concepts/game-library.md).
to guess a manifest on the fly.
See [Ignoring manifests](../concepts/manifests.md#ignoring-manifests)
for details.
**Other**
- **Auto-Save Memory Periodically** makes higan write
[in-game saves](../concepts/save-states.md#save-states-versus-in-game-saves)
to disk during gameplay,
instead of only when higan exits.
This may cause stuttering,
but means that you haven't lost everything
if higan crashes,
or your computer loses power.
- Note that this does not include
[game notes](higan-tools.md#game-notes)

View File

@@ -3,10 +3,10 @@ appears when you choose
one of the items at the bottom of
[the Tools menu](higan.md#the-tools-menu).
The window has a tab for each tool:
The window has a tab for each tool.
The Cheat Editor
----------------
Cheat Editor
============
For some consoles,
higan supports applying temporary changes to the code of a running game.
@@ -89,8 +89,8 @@ in Super Mario World,
you can lock the time to 999 with these codes:
`7e0f31=09+7e0f32=09+7e0f33=09`.
The State Manager
-----------------
State Manager
=============
The State Manager allows you to create,
load,
@@ -125,8 +125,8 @@ and click "Erase" in the bottom-right corner.
To clear all the slots at once,
click "Reset" in the bottom-right corner.
The Manifest Viewer
-------------------
Manifest Viewer
===============
As described in
[Game Manifests](../concepts/manifests.md),
@@ -135,3 +135,14 @@ describe how the various parts of a game cartridge
are wired up together.
The Manifest Viewer lets you examine
the configuration higan is using for the loaded game.
Game Notes
==========
The Game Notes tab
is a place where you can write whatever you want
about the running game.
This information is automatically stored inside
the [game folder](../concepts/game-folders.md)
and loaded back into this tab
every time the game is loaded.

View File

@@ -5,27 +5,31 @@ a status-bar across the bottom,
and a large area in the middle that shows
the running game's video output.
The Library menu
The Systems menu
----------------
Manufacturer sub-menus
allow you to play
games you've already imported
into higan's
[game library](../concepts/game-library.md).
This menu lists the systems higan emulates.
Choosing any system from this menu allows you to play
games for that system that you've already imported
into higan's [game library](../concepts/game-library.md).
See [Importing and playing games](../guides/import.md).
You can customise this menu
in [higan's Systems settings](higan-settings.md#systems)
to hide systems you don't care about,
or add a specific cartridge for any supported system.
This makes it more convenient
to play games that involve mini-cartridges:
for example, you can
add the Sufami Turbo to the list
and load *SD Ultra Battle*
in two clicks instead of three.
**Load ROM File ...**
opens a [filesystem browser](common.md#the-filesystem-browser)
allowing you to choose a single ROM file.
It will be imported and immediately start playing.
See [Importing and playing games](../guides/import.md).
**Import ROM Files ...**
launches the icarus importing tool,
allowing you to bulk-import many ROM files at once.
See [the icarus documentation](icarus.md).
The console menu
---------------
@@ -35,8 +39,7 @@ The console menu does not appear
until a game is loaded.
Also,
it's not named "console",
it's named for the kind of console
the loaded game runs on.
it's named for the console that runs the loaded game.
For example,
when playing a Game Boy game,
you will have a "Game Boy" menu.
@@ -46,7 +49,6 @@ to the particular console being emulated.
All consoles will have some of the following items,
but few consoles have all of them.
**Controller Port 1**
allows you
to connect different emulated controllers
@@ -76,7 +78,21 @@ This menu appears for the Famicom,
even though the Famicom did not support alternate controllers,
because the Famicom emulation core also emulates the NES,
which did.
**Controller**
is like "Controller Port 1"
for consoles that only have one controller port.
**Hardware**
appears for consoles with buttons on the main unit,
like the Game Boy,
or Master System.
It only allows the built-in controls to be used.
**Cartridge**
appears for the Game Boy and Game Boy Colour.
The options inside it do nothing.
**Expansion Port**
allows you
to connect different emulated devices
@@ -91,6 +107,30 @@ This option allows the same program
to control the emulated SNES,
for development or testing.
**Extension Port**
is the name the Sega Mega Drive used for its expansion port.
**Soft Reset**
restarts the emulated console's CPU
while leaving the console's memory untouched,
like pressing the "reset" button
on a physical console.
This menu item does not appear
for consoles that did not have a "reset" button,
like hand-helds.
It also does not appear for the Sega Master System,
since that console's reset button is wired up like a controller
rather than directly attached to the CPU.
To reset the Master System,
bind a keyboard or joypad button
to the "Reset" function
on the "Controls" controller
in the "Hardware" port
of the Sega Master System
in [higan's Input settings](higan-settings.md#input).
**Power Cycle**
restarts the loaded game
as though the emulated console were switched off and on again.
@@ -99,7 +139,7 @@ as though the emulated console were switched off and on again.
stops the current game,
as though the emulated console were switched off.
You can load a new game
from [the Library menu](#the-library-menu).
from [the Systems menu](#the-systems-menu).
[21fx]: https://github.com/defparam/21FX
@@ -109,13 +149,83 @@ The Settings menu
The Settings menu allows you to configure things
that aren't specific to any particular console.
**Video Scale** determines the size
**Size**
determines the size
of the emulated console's video output
when higan is running in windowed mode
(as opposed to fullscreen).
The menu-items that indicate particular sizes
are only approximate, since
aspect correction can be applied,
different consoles have different native image sizes,
and some consoles can change the size of their output image dynamically.
**Video Emulation** applies various effects
- **1x (240p)**
resizes the higan window
so that each pixel of the emulated console's video output
is drawn as a single pixel on the computer screen.
- **2x (480p)**
resizes the higan window
so that each pixel of the emulated console's video output
is drawn as a 2×2 block of pixels on the computer screen.
- **3x (720p)**
resizes the higan window
so that each pixel of the emulated console's video output
is drawn as a 3×3 block of pixels on the computer screen.
- **Shrink Window To Size**
resizes the higan window to fit the emulated console's video output
at its current scale,
so there's no black padding between the image and the window border
(some padding may remain
if "Show Overscan Area" is enabled
in the Output menu).
- **Center Window**
moves the higan window to the centre of the computer screen.
**Output**
controls how higan draws the emulated console's video output
into the space available,
in both windowed and fullscreen modes.
- **Center**
draws the emulated video
at the largest integer multiple of the native size that will fit,
centered in the space available.
This gives the most crisp output,
but often has black borders.
- **Scale**
draws the emulated video
at the largest size that will fit,
and which preserves the image's aspect ratio.
This strikes a balance between
displaying the video output as it was intended,
and eliminating black borders.
- **Stretch**
draws the emulated video
to cover the entire available output area,
even if that distorts the image.
This completely eliminates black borders,
but can look very weird.
- **Adaptive Sizing**
allows higan to resize its window
when the emulated console changes the resolution
of its video output.
This can avoid black borders,
but the window resizing itself might be even more distracting.
- **Aspect Correction**
horizontally stretches the emulated video output
to match the aspect ratio produced by the original console.
It can make the output look more "lumpy",
but is a more accurate representation
of the original console's output.
- **Show Overscan Area**
controls whether the area defined by
the "Overscan Area" sliders in
the [Video settings](higan-settings.md#video)
is clipped from the emulated video output
or shown.
**Emulation** applies various effects
to the emulated console's video output
to reproduce some behaviours
that aren't technically part of the console itself:
@@ -140,8 +250,8 @@ that aren't technically part of the console itself:
used by the Super Famicom,
the dim, washed out colours of the original Game Boy Advance,
and the pea-green display of the original Game Boy.
**Video Shader** controls
**Shader** controls
how the low-resolution video output of the emulated console
is scaled up to suit modern high-resolution displays.
[Using video shaders](../guides/shaders.md)
@@ -166,6 +276,9 @@ at the bottom of the window.
This option has no effect in fullscreen mode.
See [The status bar](#the-status-bar) for more information.
**Systems ...**
opens [higan's Systems settings](higan-settings.md#systems).
**Video ...**
opens [higan's Video settings](higan-settings.md#video).
@@ -199,16 +312,26 @@ restores the emulated console to
a state previously saved to one of the quick state slots.
See [Save States](../concepts/save-states.md) for more information.
**Cheat Editor**
opens [the Cheat Editor tab](higan-tools.md#the-cheat-editor)
**Pause Emulation**
pauses the emulated console
until this menu-item is selected again.
This can also be triggered by
the [pause hotkey](higan-settings.md#hotkeys).
**Cheat Editor ...**
opens the [Cheat Editor tab](higan-tools.md#cheat-editor)
of the Tools window.
**State Manager**
opens [the State Manager tab](higan-tools.md#the-state-manager)
**State Manager ...**
opens the [State Manager tab](higan-tools.md#state-manager)
of the Tools window.
**Manifest Viewer**
opens [the Manifest Viewer tab](higan-tools.md#the-manifest-viewer)
**Manifest Viewer ...**
opens the [Manifest Viewer tab](higan-tools.md#manifest-viewer)
of the Tools window.
**Game Notes ...**
opens [the Game Notes tab](higan-tools.md#game-notes)
of the Tools window.
The Help menu
@@ -237,7 +360,7 @@ at the bottom of the main higan window,
while "Show Status Bar" is ticked in [the Settings menu](#the-settings-menu).
Before any game is loaded,
the status bar displays "No cartridge loaded".
the status bar displays "Unloaded".
When a game is loaded and running,
the status bar displays the current emulation speed
@@ -255,11 +378,13 @@ or you may have pressed the "turbo" [hotkey](higan-settings.md#hotkeys).
The status bar displays "Paused"
if you have pressed the "pause" [hotkey](higan-settings.md#hotkeys),
selected "Pause Emulation" from [the Tools menu](#the-tools-menu),
or if "When focus is lost: Pause Emulation" is ticked
in [higan's Input settings](higan-settings.md#input)
and the main higan window is not the foreground window.
To resume emulation,
make sure the main higan window is in the foreground,
select "Pause Emulation" from the Tools menu again,
and/or press the "pause" hotkey.
The status bar briefly displays "Selected quick state slot X"
@@ -279,7 +404,7 @@ sub-menu that has not had a save-state saved to it,
or when you press the "Load Quick State" hotkey
while the current Quick State slot has not had a save-state saved to it,
The status bar briefly displays "Power cycled"
The status bar briefly displays "System has been power cycled"
when you choose "Power Cycle" from [the console menu](#the-console-menu),
or press the "Power Cycle" hotkey.

View File

@@ -1,10 +1,7 @@
When launching icarus,
directly or by picking "Import ROM Files ..."
from higan's [Library menu](higan.md#the-library-menu),
the main icarus window appears.
This window allows you to bulk-import ROM files
into [higan's game library][gamelib],
and also to access icarus' settings.
icarus is a separate tool
bundled with higan
that allows you to bulk-import ROM files
into higan's [game library].
Bulk importing ROM files
------------------------
@@ -18,7 +15,7 @@ with customisations:
consoles higan emulates,
plus `.zip` files since ROM dumps are often compressed.
- Each matching file has a check-box next to it.
- You can tick the check-box next to every file at once
- You can tick the check-box next to every listed file at once
by pressing "Select All" in the bottom-left.
- You can un-tick all the check-boxes
by pressing "Unselect All" in the bottom-left.
@@ -27,7 +24,7 @@ Pressing "Import ..." in the bottom-right
will close the filesystem browser
then try to import all the files
whose check-boxes are ticked
into [the Game Library][gamelib].
into the [game library].
icarus displays a progress dialog during the import process,
and a result window if any errors occurred.
@@ -47,12 +44,9 @@ The icarus Settings dialog contains the following settings:
where icarus puts the games it imports.
See [Moving the Game Library][movgamelib]
for details.
- **Create Manifests** causes icarus
to include
[a manifest file](../concepts/manifests.md)
inside
[the game folder](../concepts/game-folders.md)
for each imported game.
- **Create Manifests** causes icarus to
include a [manifest] file
inside the [game folder] for each imported game.
See [Ignoring manifests](../concepts/manifests.md#ignoring-manifests)
for details.
- **Use Database** causes icarus to use manifest information
@@ -65,5 +59,42 @@ The icarus Settings dialog contains the following settings:
higan uses icarus to generate a manifest when a game is loaded,
not just at import-time.
[gamelib]: ../concepts/game-library.md
Command line
------------
icarus can be launched in any of the following ways:
> icarus
>
> icarus \-\-import *FILE*
>
> icarus \-\-manifest *GAME*
When run without arguments,
icarus runs interactively
as described under [Bulk importing ROM files](#bulk-importing-rom-files) above.
When run with the `--import` flag,
`FILE` should be the path to a ROM file
for one of the consoles higan supports,
or a `.zip` file containing such a ROM file.
icarus will import it into the [game library]
just as it would if running interactively,
and the full path to the ressulting game folder
is printed to icarus' standard output.
If the game cannot be imported correctly
due to missing firmware,
icarus prints no output.
When run with the `--manifest` flag,
`GAME` should be the path to a [game folder],
such as a game previously imported into the [game library].
icarus will examine the game,
come up with a [manifest] describing the game's memory layout,
and print it to standard output.
[game library]: ../concepts/game-library.md
[movgamelib]: ../concepts/game-library.md#moving-the-game-library
[game folder]: ../concepts/game-folders.md
[manifest]: ../concepts/manifests.md

View File

@@ -29,8 +29,8 @@ you may be comparing it
to a Mega Drive calibrated to a different scale
(or to an emulator tweaked to match such a Mega Drive).
[vol]: https://board.byuu.org/viewtopic.php?p=42482#p42482
[va6]: https://board.byuu.org/viewtopic.php?p=42195#p42195
[vol]: https://helmet.kafuka.org/byuubackup2/viewtopic.php@f=4&t=1235&start=140.html#p42482
[va6]: https://helmet.kafuka.org/byuubackup2/viewtopic.php@f=4&t=1235&start=130.html#p42195
Playing Game Boy Color games in Game Boy mode
---------------------------------------------
@@ -128,19 +128,23 @@ Rumble compatibility for Game Boy (Color)
The Game Boy and Game Boy Color did not natively support
any kind of rumble or force-feedback system,
but some game cartridges (such as Pokémon Pinball)
but some game cartridges (such as *Pokémon Pinball*)
included a rumble motor within the cartridge itself.
Such cartridges generally used the "MBC5" memory mapper chip.
Because higan does not currently support
game-specific controller features,
to experience the rumble effect in higan
you'll need to configure the console:
To experience the rumble effect in higan,
you'll need to configure the MBC5 "controller"
connected to the "cartridge" port:
- Open
[higan's Input settings](interface/higan-settings.md#input)
- In the list of consoles,
select Game Boy, or Game Boy Color
depending on which console you want to use to play the game
- In the list of ports,
select "Cartridge"
- In the list of controllers,
select "MBC5"
- In the list of inputs,
double-click "Rumble"
or select it and press Enter

View File

@@ -51,7 +51,7 @@ Load a game
-----------
From
[the Library menu](interface/higan.md#the-library-menu),
[the Systems menu](interface/higan.md#the-systems-menu),
choose "Load ROM File ..."
to open [a filesystem browser](interface/common.md#the-filesystem-browser),
and choose the game you want to play.
@@ -63,7 +63,7 @@ In the future,
if you want to play this game again,
you can choose "Load ROM File ..." as you did before,
or you can choose the appropriate console name
from the Library menu,
from the Systems menu,
which will list all the games for that console
in the Game Library.
@@ -91,7 +91,7 @@ start out with nothing plugged in,
so if the game you're playing needs a gamepad connected,
you'll have to connect it!
This doesn't apply to handheld consoles
This doesn't apply to hand-held consoles
like the Game Boy and WonderSwan,
since the "controller" is always connected.
This *does* apply to the Famicom,

View File

@@ -3,8 +3,13 @@ Release checklist
1. Commit the new release
2. Tag the commit
3. `git push`
4. `git push --tags`
5. Go to [the docs admin][rtd] and enable builds for the new tag.
[rtd]: https://readthedocs.org/dashboard/higan/versions/
3. `git push --tags origin master` to push the commit and tag at the
same time.
4. Go to [the docs admin][rtd] and verify that it's building the new
version as 'stable' and under its tag name.
5. Check out the `libretro` branch.
6. Merge changes from master.
7. Copy `target-bsnes/resource/resource.?pp` to the `target-libretro` folder.
7. `git push` to make the new changes available.
[rtd]: https://readthedocs.org/projects/higan/builds/

BIN
firmware/cx4.data.rom Normal file

Binary file not shown.

BIN
firmware/sgb1.boot.rom Normal file

Binary file not shown.

BIN
firmware/sgb2.boot.rom Normal file

Binary file not shown.

59
genius/GNUmakefile Normal file
View File

@@ -0,0 +1,59 @@
name := genius
build := stable
flags += -I..
nall.path := ../nall
include $(nall.path)/GNUmakefile
hiro.path := ../hiro
hiro.resource := data/$(name).rc
include $(hiro.path)/GNUmakefile
objects := obj/genius.o
obj/genius.o: genius.cpp
all: $(hiro.objects) $(objects)
$(info Linking out/$(name) ...)
+@$(compiler) -o out/$(name) $(hiro.objects) $(objects) $(hiro.options) $(options)
ifeq ($(platform),macos)
rm -rf out/$(name).app
mkdir -p out/$(name).app/Contents/MacOS/
mkdir -p out/$(name).app/Contents/Resources/
mv out/$(name) out/$(name).app/Contents/MacOS/$(name)
cp data/$(name).plist out/$(name).app/Contents/Info.plist
sips -s format icns data/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns
endif
verbose: hiro.verbose nall.verbose all;
clean:
ifeq ($(platform),macos)
rm -rf out/$(name).app
endif
$(call delete,obj/*)
$(call delete,out/*)
install: all
ifeq ($(platform),macos)
cp -R out/$(name).app /Applications/$(name).app
else ifneq ($(filter $(platform),linux bsd),)
mkdir -p $(prefix)/bin/
mkdir -p $(prefix)/share/applications/
mkdir -p $(prefix)/share/icons/
mkdir -p $(prefix)/share/$(name)/
cp out/$(name) $(prefix)/bin/$(name)
cp data/$(name).desktop $(prefix)/share/applications/$(name).desktop
cp data/$(name).png $(prefix)/share/icons/$(name).png
endif
uninstall:
ifeq ($(platform),macos)
rm -rf /Applications/$(name).app
else ifneq ($(filter $(platform),linux bsd),)
rm -f $(prefix)/bin/$(name)
rm -f $(prefix)/share/applications/$(name).desktop
rm -f $(prefix)/share/icons/$(name).png
endif
-include obj/*.d

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="genius" 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>
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>false</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

View File

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

BIN
genius/data/genius.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

18
genius/data/genius.plist Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>org.byuu.genius</string>
<key>CFBundleDisplayName</key>
<string>genius</string>
<key>CFBundleExecutable</key>
<string>genius</string>
<key>CFBundleIconFile</key>
<string>genius.icns</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>

BIN
genius/data/genius.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

2
genius/data/genius.rc Normal file
View File

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

80
genius/data/genius.svg Normal file
View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="256mm"
height="256mm"
viewBox="0 0 256 256"
version="1.1"
id="svg8"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="icarus.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.5"
inkscape:cx="62.34093"
inkscape:cy="560"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1028"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-41)">
<circle
id="path10"
cx="128.0"
cy="169.0"
r="120.0"
style="stroke-width:0.25;fill:#b8b8ff;fill-opacity:1" />
<g
aria-label="氷"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:260.07336426px;line-height:1.25;font-family:KaiTi;-inkscape-font-specification:KaiTi;letter-spacing:0px;word-spacing:0px;fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="text818">
<path
style="fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 130.80961,146.24049 q 5.07956,5.07956 14.22276,15.23868 14.22277,-13.20685 25.39779,-27.42962 12.19094,-15.23867 11.17503,-24.38187 0,-10.15912 10.15912,-4.06365 10.15911,6.09547 14.22276,12.19094 4.06364,5.07956 -2.03182,7.11138 -6.09547,1.01591 -22.35006,14.22276 -15.23867,12.19094 -32.50917,26.4137 13.20685,11.17503 23.36597,20.31823 11.17502,9.14321 24.38187,18.28641 14.22277,8.1273 27.42962,14.22276 14.22276,5.07956 21.33414,7.11139 7.11138,2.03182 -3.04773,5.07955 -9.14321,2.03183 -23.36597,3.04774 -14.22276,0 -21.33414,-2.03182 -6.09547,-3.04774 -11.17503,-8.1273 -4.06365,-5.07956 -24.38188,-27.42961 -19.30232,-23.36597 -31.49326,-39.62055 1.01591,41.65237 2.03182,64.00243 2.03183,22.35005 0,34.54099 -2.03182,12.19094 -8.12729,19.30232 -5.07956,7.11138 -8.12729,4.06365 -2.03182,-2.03183 -7.11138,-11.17503 -5.07956,-8.12729 -16.254587,-15.23867 -11.175027,-8.1273 1.015912,-5.07956 12.190935,2.03182 16.254585,2.03182 4.06365,-1.01591 6.09547,-7.11138 2.03182,-7.11138 2.03182,-46.73193 0,-40.63646 -1.01591,-77.20928 -1.01591,-37.58873 -6.09547,-45.716022 -5.07956,-9.143205 4.06365,-7.111382 10.15911,1.015912 16.25458,5.079558 7.11138,3.047735 4.06365,9.143205 -3.04774,5.079557 -4.06365,15.238673 -1.01591,10.159118 -1.01591,51.811488 z"
id="path822" />
<path
style="fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 88.141325,149.28823 q 5.079558,1.01591 14.222765,7.11138 9.1432,6.09547 4.06364,10.15911 -5.07955,4.06365 -12.190935,18.28641 -6.09547,14.22276 -14.222763,25.39779 -8.127292,10.15912 -19.30232,19.30232 -11.175027,8.12729 -21.334143,12.19094 -9.143204,3.04774 -16.254585,5.07956 -6.095469,1.01591 5.079558,-6.09547 11.175027,-7.11138 21.334143,-17.2705 11.175027,-10.15911 18.286408,-21.33414 8.127293,-12.19094 11.175028,-20.31823 3.047735,-9.14321 3.047735,-14.22276 1.015911,-5.07956 -3.047735,-5.07956 -4.063646,0 -15.238674,4.06364 -10.159116,4.06365 -15.238674,6.09547 -4.063646,2.03183 -13.20685,-3.04773 -8.127293,-6.09547 2.031823,-6.09547 11.175027,-1.01591 24.381878,-5.07956 14.222762,-5.07956 17.270497,-7.11138 4.063646,-3.04773 9.143204,-2.03182 z"
id="path820" />
<path
style="fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 63.759447,101.54038 q 9.143204,1.01591 18.286409,5.07956 9.143204,4.06365 11.175027,11.17503 2.031823,7.11138 -1.015912,11.17503 -3.047734,4.06364 -15.238673,-4.06365 -11.175028,-9.1432 -16.254586,-16.25459 -5.079557,-8.12729 3.047735,-7.11138 z"
id="path815" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

629
genius/genius.cpp Normal file
View File

@@ -0,0 +1,629 @@
#include <nall/nall.hpp>
using namespace nall;
#include <hiro/hiro.hpp>
using namespace hiro;
#include "genius.hpp"
unique_pointer<ListWindow> listWindow;
unique_pointer<GameWindow> gameWindow;
unique_pointer<MemoryWindow> memoryWindow;
unique_pointer<OscillatorWindow> oscillatorWindow;
//
ListWindow::ListWindow() {
listWindow = this;
fileMenu.setText("File");
newAction.setText("New").onActivate([&] { newDatabase(); });
openAction.setText("Open ...").onActivate([&] {
if(auto location = BrowserDialog().setParent(*this).setFilters({"*.bml"}).openFile()) {
loadDatabase(location);
}
});
saveAction.setText("Save").onActivate([&] {
if(!location) return saveAsAction.doActivate();
saveDatabase(location);
});
saveAsAction.setText("Save As ...").onActivate([&] {
if(auto location = BrowserDialog().setParent(*this).setFilters({"*.bml"}).saveFile()) {
saveDatabase(location);
}
});
quitAction.setText("Quit").onActivate([&] { quit(); });
helpMenu.setText("Help");
aboutAction.setText("About ...").onActivate([&] {
MessageDialog().setParent(*this).setTitle("About").setText({
"genius\n",
"Author: byuu\n",
"Website: https://byuu.org/"
}).information();
});
layout.setPadding(5);
gameList.setHeadered();
gameList.onActivate([&] { modifyButton.doActivate(); });
gameList.onChange([&] { updateWindow(); });
appendButton.setText("Append").onActivate([&] {
setEnabled(false);
gameWindow->show();
});
modifyButton.setText("Modify").onActivate([&] {
if(auto item = gameList.selected()) {
setEnabled(false);
gameWindow->show(games[item.offset()]);
}
});
removeButton.setText("Remove").onActivate([&] { removeGame(); });
onClose([&] { quit(); });
setSize({820, 600});
reloadList();
updateWindow();
setCentered();
}
auto ListWindow::quit() -> void {
if(!modified || MessageDialog().setParent(*this).setText({
"Are you sure you want to quit without saving your changes?"
}).question() == "Yes") {
Application::quit();
}
}
auto ListWindow::reloadList() -> void {
gameList.reset();
gameList.append(TableViewColumn().setText("Name").setExpandable());
gameList.append(TableViewColumn().setText("Region"));
gameList.append(TableViewColumn().setText("Revision"));
gameList.append(TableViewColumn().setText("Board"));
for(auto& game : games) {
TableViewItem item{&gameList};
item.append(TableViewCell().setText(game.name));
item.append(TableViewCell().setText(game.region));
item.append(TableViewCell().setText(game.revision));
item.append(TableViewCell().setText(game.board));
}
Application::processEvents();
gameList.resizeColumns();
}
auto ListWindow::updateWindow() -> void {
modifyButton.setEnabled((bool)gameList.selected());
removeButton.setEnabled((bool)gameList.selected());
string name = Location::base(location);
if(!name) name = "(Untitled)";
setTitle({modified ? "*" : "", name, " [", games.size(), "] - genius"});
}
auto ListWindow::newDatabase() -> void {
games.reset();
modified = false;
location = "";
reloadList();
updateWindow();
}
auto ListWindow::loadDatabase(string location) -> void {
auto document = BML::unserialize(string::read(location));
games.reset();
for(auto node : document.find("game")) {
Game game;
game.sha256 = node["sha256"].text();
game.label = node["label"].text();
game.name = node["name"].text();
game.region = node["region"].text();
game.revision = node["revision"].text();
game.board = node["board"].text();
for(auto object : node["board"]) {
Component component;
if(object.name() == "memory") {
component.type = Component::Type::Memory;
component.memory.type = object["type"].text();
component.memory.size = object["size"].text();
component.memory.content = object["content"].text();
component.memory.manufacturer = object["manufacturer"].text();
component.memory.architecture = object["architecture"].text();
component.memory.identifier = object["identifier"].text();
component.memory.Volatile = (bool)object["volatile"];
}
if(object.name() == "oscillator") {
component.type = Component::Type::Oscillator;
component.oscillator.frequency = object["frequency"].text();
}
game.components.append(component);
}
game.note = node["note"].text();
games.append(game);
}
modified = false;
this->location = location;
reloadList();
updateWindow();
}
auto ListWindow::saveDatabase(string location) -> void {
auto fp = file::open(location, file::mode::write);
if(!fp) return MessageDialog().setParent(*this).setText({
"Error: failed to write file.\n\n",
"Name: ", location
}).error(), void();
auto copy = games;
copy.sort([](auto x, auto y) {
return string::icompare(
{x.name, "\n", x.region, "\n", x.revision},
{y.name, "\n", y.region, "\n", y.revision}
) < 0;
});
fp.print("database\n");
fp.print(" revision: ", chrono::local::date(), "\n\n");
for(auto& game : copy) {
fp.print("game\n");
fp.print(" sha256: ", game.sha256, "\n");
if(game.label)
fp.print(" label: ", game.label, "\n");
fp.print(" name: ", game.name, "\n");
fp.print(" region: ", game.region, "\n");
fp.print(" revision: ", game.revision, "\n");
if(game.board)
fp.print(" board: ", game.board, "\n");
else if(game.components)
fp.print(" board\n");
for(auto& component : game.components) {
if(component.type == Component::Type::Memory) {
fp.print(" memory\n");
fp.print(" type: ", component.memory.type, "\n");
fp.print(" size: ", component.memory.size, "\n");
fp.print(" content: ", component.memory.content, "\n");
if(component.memory.manufacturer)
fp.print(" manufacturer: ", component.memory.manufacturer, "\n");
if(component.memory.architecture)
fp.print(" architecture: ", component.memory.architecture, "\n");
if(component.memory.identifier)
fp.print(" identifier: ", component.memory.identifier, "\n");
if(component.memory.Volatile)
fp.print(" volatile\n");
}
if(component.type == Component::Type::Oscillator) {
fp.print(" oscillator\n");
fp.print(" frequency: ", component.oscillator.frequency, "\n");
}
}
if(game.note)
fp.print(" note: ", game.note, "\n");
fp.print("\n");
}
modified = false;
this->location = location;
updateWindow();
}
auto ListWindow::appendGame(Game game) -> void {
modified = true;
auto offset = games.size();
games.append(game);
reloadList();
gameList.item(offset).setSelected().setFocused();
updateWindow();
}
auto ListWindow::modifyGame(Game game) -> void {
if(auto item = gameList.selected()) {
modified = true;
auto offset = item.offset();
games[offset] = game;
reloadList();
gameList.item(offset).setSelected().setFocused();
updateWindow();
}
}
auto ListWindow::removeGame() -> void {
if(auto item = gameList.selected()) {
if(MessageDialog().setParent(*this).setText({
"Are you sure you want to permanently remove this game?\n\n",
"Name: ", item.cell(0).text()
}).question() == "Yes") {
modified = true;
games.remove(item.offset());
reloadList();
updateWindow();
}
}
}
//
GameWindow::GameWindow() {
gameWindow = this;
layout.setPadding(5);
hashLabel.setText("SHA256:").setAlignment(1.0);
hashEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); });
regionLabel.setText("Region:").setAlignment(1.0);
regionEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); });
revisionLabel.setText("Revision:");
revisionEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); });
boardLabel.setText("Board:");
boardEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); });
nameLabel.setText("Name:").setAlignment(1.0);
nameEdit.onChange([&] { modified = true, updateWindow(); });
labelLabel.setText("Label:").setAlignment(1.0);
labelEdit.onChange([&] { modified = true, updateWindow(); });
noteLabel.setText("Note:").setAlignment(1.0);
noteEdit.onChange([&] { modified = true, updateWindow(); });
componentLabel.setText("Tree:").setAlignment({1.0, 0.0});
componentTree.onActivate([&] { modifyComponentButton.doActivate(); });
componentTree.onChange([&] { updateWindow(); });
appendMemoryButton.setText("Memory").onActivate([&] {
setEnabled(false);
memoryWindow->show();
});
appendOscillatorButton.setText("Oscillator").onActivate([&] {
setEnabled(false);
oscillatorWindow->show();
});
modifyComponentButton.setText("Modify").onActivate([&] {
if(auto item = componentTree.selected()) {
setEnabled(false);
auto path = item.path().split("/");
auto offset = path(0).natural();
Component component = game.components[offset];
if(component.type == Component::Type::Memory) {
memoryWindow->show(component.memory);
}
if(component.type == Component::Type::Oscillator) {
oscillatorWindow->show(component.oscillator);
}
}
});
removeComponentButton.setText("Remove").onActivate([&] { removeComponent(); });
acceptButton.setText("Accept").onActivate([&] { accept(); });
cancelButton.setText("Cancel").onActivate([&] { cancel(); });
onClose([&] { cancel(); });
setSize({640, 480});
setDismissable();
}
auto GameWindow::show(Game game) -> void {
this->game = game;
modified = false;
create = !game.sha256;
hashEdit.setText(game.sha256);
regionEdit.setText(game.region);
revisionEdit.setText(game.revision);
boardEdit.setText(game.board);
nameEdit.setText(game.name);
labelEdit.setText(game.label);
noteEdit.setText(game.note);
acceptButton.setText(create ? "Create" : "Apply");
reloadList();
updateWindow();
setCentered(*listWindow);
setVisible();
if(create) {
hashEdit.setFocused();
} else {
cancelButton.setFocused();
}
}
auto GameWindow::accept() -> void {
game.sha256 = hashEdit.text().strip();
game.region = regionEdit.text().strip();
game.revision = revisionEdit.text().strip();
game.board = boardEdit.text().strip();
game.name = nameEdit.text().strip();
game.label = labelEdit.text().strip();
game.note = noteEdit.text().strip();
if(create) {
listWindow->appendGame(game);
} else {
listWindow->modifyGame(game);
}
memoryWindow->setVisible(false);
setVisible(false);
listWindow->setEnabled();
listWindow->setFocused();
}
auto GameWindow::cancel() -> void {
if(!modified || MessageDialog().setParent(*this).setText({
"Are you sure you want to discard your changes to this game?"
}).question() == "Yes") {
memoryWindow->setVisible(false);
setVisible(false);
listWindow->setEnabled();
listWindow->setFocused();
}
}
auto GameWindow::reloadList() -> void {
componentTree.reset();
uint counter = 1;
for(auto& component : game.components) {
TreeViewItem item;
string index = {"[", counter++, "] "};
if(component.type == Component::Type::Memory) {
item.setText({index, "Memory"});
item.append(TreeViewItem().setText({"Type: ", component.memory.type}));
item.append(TreeViewItem().setText({"Size: ", component.memory.size}));
item.append(TreeViewItem().setText({"Content: ", component.memory.content}));
if(component.memory.manufacturer)
item.append(TreeViewItem().setText({"Manufacturer: ", component.memory.manufacturer}));
if(component.memory.architecture)
item.append(TreeViewItem().setText({"Architecture: ", component.memory.architecture}));
if(component.memory.identifier)
item.append(TreeViewItem().setText({"Identifier: ", component.memory.identifier}));
if(component.memory.Volatile)
item.append(TreeViewItem().setText({"Volatile"}));
}
if(component.type == Component::Type::Oscillator) {
item.setText({index, "Oscillator"});
item.append(TreeViewItem().setText({"Frequency: ", component.oscillator.frequency}));
}
componentTree.append(item);
}
Application::processEvents();
for(auto& item : componentTree.items()) item.setExpanded();
}
auto GameWindow::updateWindow() -> void {
bool valid = true;
bool hashValid = hashEdit.text().strip().size() == 64;
hashEdit.setEditable(!hashValid).setBackgroundColor(
!create || hashValid ? Color{192, 255, 192}
: (valid = false, Color{255, 224, 224}));
regionEdit.setBackgroundColor(regionEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
revisionEdit.setBackgroundColor(revisionEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
boardEdit.setBackgroundColor(boardEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
nameEdit.setBackgroundColor(nameEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
labelEdit.setBackgroundColor(labelEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
noteEdit.setBackgroundColor(noteEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
modifyComponentButton.setEnabled((bool)componentTree.selected());
removeComponentButton.setEnabled((bool)componentTree.selected());
acceptButton.setEnabled(valid);
setTitle({modified ? "*" : "", create ? "Add New Game" : "Modify Game Details"});
if(create && hashValid && hashEdit.focused()) regionEdit.setFocused();
}
auto GameWindow::appendComponent(Component component) -> void {
modified = true;
auto offset = game.components.size();
game.components.append(component);
reloadList();
componentTree.item(offset).setSelected().setFocused();
updateWindow();
}
auto GameWindow::modifyComponent(Component component) -> void {
if(auto item = componentTree.selected()) {
modified = true;
auto path = item.path().split("/");
auto offset = path(0).natural();
game.components[offset] = component;
reloadList();
componentTree.item(offset).setSelected().setFocused();
updateWindow();
}
}
auto GameWindow::removeComponent() -> void {
if(auto item = componentTree.selected()) {
if(MessageDialog().setParent(*this).setText({
"Are you sure you want to permanently remove this component?"
}).question() == "Yes") {
modified = true;
auto path = item.path().split("/");
auto offset = path(0).natural();
game.components.remove(offset);
reloadList();
updateWindow();
}
}
}
//
MemoryWindow::MemoryWindow() {
memoryWindow = this;
layout.setPadding(5);
typeLabel.setText("Type:").setAlignment(1.0);
typeEdit.append(ComboEditItem().setText("ROM"));
typeEdit.append(ComboEditItem().setText("EEPROM"));
typeEdit.append(ComboEditItem().setText("Flash"));
typeEdit.append(ComboEditItem().setText("RAM"));
typeEdit.append(ComboEditItem().setText("RTC"));
typeEdit.onChange([&] { modified = true, updateWindow(); });
sizeLabel.setText("Size:").setAlignment(1.0);
sizeEdit.onChange([&] { modified = true, updateWindow(); });
contentLabel.setText("Content:").setAlignment(1.0);
contentEdit.append(ComboEditItem().setText("Program"));
contentEdit.append(ComboEditItem().setText("Data"));
contentEdit.append(ComboEditItem().setText("Character"));
contentEdit.append(ComboEditItem().setText("Save"));
contentEdit.append(ComboEditItem().setText("Time"));
contentEdit.onChange([&] { modified = true, updateWindow(); });
manufacturerLabel.setText("Manufacturer:").setAlignment(1.0);
manufacturerEdit.onChange([&] { modified = true, updateWindow(); });
architectureLabel.setText("Architecture:").setAlignment(1.0);
architectureEdit.onChange([&] { modified = true, updateWindow(); });
identifierLabel.setText("Identifier:").setAlignment(1.0);
identifierEdit.onChange([&] { modified = true, updateWindow(); });
volatileOption.setText("Volatile").onToggle([&] { modified = true, updateWindow(); });
acceptButton.setText("Accept").onActivate([&] { accept(); });
cancelButton.setText("Cancel").onActivate([&] { cancel(); });
onClose([&] { cancel(); });
setSize({320, layout.minimumSize().height()});
setDismissable();
}
auto MemoryWindow::show(Memory memory) -> void {
this->memory = memory;
modified = false;
create = !memory.type;
typeEdit.setText(memory.type);
sizeEdit.setText(memory.size);
contentEdit.setText(memory.content);
manufacturerEdit.setText(memory.manufacturer);
architectureEdit.setText(memory.architecture);
identifierEdit.setText(memory.identifier);
volatileOption.setChecked(memory.Volatile);
updateWindow();
setCentered(*gameWindow);
setVisible();
typeEdit.setFocused();
}
auto MemoryWindow::accept() -> void {
memory.type = typeEdit.text().strip();
memory.size = sizeEdit.text().strip();
memory.content = contentEdit.text().strip();
memory.manufacturer = manufacturerEdit.text().strip();
memory.architecture = architectureEdit.text().strip();
memory.identifier = identifierEdit.text().strip();
memory.Volatile = volatileOption.checked() && (memory.type == "RAM" || memory.type == "RTC");
Component component{Component::Type::Memory};
component.memory = memory;
if(create) {
gameWindow->appendComponent(component);
} else {
gameWindow->modifyComponent(component);
}
setVisible(false);
gameWindow->setEnabled();
gameWindow->setFocused();
}
auto MemoryWindow::cancel() -> void {
if(!modified || MessageDialog().setParent(*this).setText({
"Are you sure you want to discard your changes to this memory?"
}).question() == "Yes") {
setVisible(false);
gameWindow->setEnabled();
gameWindow->setFocused();
}
}
auto MemoryWindow::updateWindow() -> void {
bool valid = true;
typeEdit.setBackgroundColor(typeEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
sizeEdit.setBackgroundColor(sizeEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
contentEdit.setBackgroundColor(contentEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
manufacturerEdit.setBackgroundColor(manufacturerEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
architectureEdit.setBackgroundColor(architectureEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
identifierEdit.setBackgroundColor(identifierEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
volatileOption.setEnabled(typeEdit.text().strip() == "RAM" || typeEdit.text().strip() == "RTC");
acceptButton.setEnabled(valid);
setTitle({modified ? "*" : "", create ? "Add New Memory" : "Modify Memory Details"});
}
//
OscillatorWindow::OscillatorWindow() {
oscillatorWindow = this;
layout.setPadding(5);
frequencyLabel.setText("Frequency:").setAlignment(1.0);
frequencyEdit.onChange([&] { modified = true, updateWindow(); });
acceptButton.setText("Accept").onActivate([&] { accept(); });
cancelButton.setText("Cancel").onActivate([&] { cancel(); });
onClose([&] { cancel(); });
setSize({320, layout.minimumSize().height()});
setDismissable();
}
auto OscillatorWindow::show(Oscillator oscillator) -> void {
this->oscillator = oscillator;
modified = false;
create = !oscillator.frequency;
frequencyEdit.setText(oscillator.frequency);
updateWindow();
setCentered(*gameWindow);
setVisible();
frequencyEdit.setFocused();
}
auto OscillatorWindow::accept() -> void {
oscillator.frequency = frequencyEdit.text().strip();
Component component{Component::Type::Oscillator};
component.oscillator = oscillator;
if(create) {
gameWindow->appendComponent(component);
} else {
gameWindow->modifyComponent(component);
}
setVisible(false);
gameWindow->setEnabled();
gameWindow->setFocused();
}
auto OscillatorWindow::cancel() -> void {
if(!modified || MessageDialog().setParent(*this).setText({
"Are you sure you want to discard your changes to this property?"
}).question() == "Yes") {
setVisible(false);
gameWindow->setEnabled();
gameWindow->setFocused();
}
}
auto OscillatorWindow::updateWindow() -> void {
bool valid = true;
frequencyEdit.setBackgroundColor(frequencyEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
acceptButton.setEnabled(valid);
setTitle({modified ? "*" : "", create ? "Add New Property" : "Modify Property Details"});
}
//
auto hiro::initialize() -> void {
Application::setName("genius");
}
#include <nall/main.hpp>
auto nall::main(Arguments) -> void {
new ListWindow;
new GameWindow;
new MemoryWindow;
new OscillatorWindow;
listWindow->setVisible();
Application::run();
}

178
genius/genius.hpp Normal file
View File

@@ -0,0 +1,178 @@
struct Memory {
string type;
string size;
string content;
string manufacturer;
string architecture;
string identifier;
boolean Volatile;
};
struct Oscillator {
string frequency;
};
//variant meta-class
struct Component {
enum class Type : uint {
Memory,
Oscillator,
} type;
Memory memory;
Oscillator oscillator;
};
struct Game {
string sha256;
string region;
string revision;
string board;
string name;
string label;
string note;
vector<Component> components;
};
struct ListWindow : Window {
ListWindow();
auto quit() -> void;
auto reloadList() -> void;
auto updateWindow() -> void;
auto newDatabase() -> void;
auto loadDatabase(string) -> void;
auto saveDatabase(string) -> void;
auto appendGame(Game) -> void;
auto modifyGame(Game) -> void;
auto removeGame() -> void;
private:
bool modified = false;
vector<Game> games;
string location;
MenuBar menuBar{this};
Menu fileMenu{&menuBar};
MenuItem newAction{&fileMenu};
MenuItem openAction{&fileMenu};
MenuItem saveAction{&fileMenu};
MenuItem saveAsAction{&fileMenu};
MenuSeparator quitSeparator{&fileMenu};
MenuItem quitAction{&fileMenu};
Menu helpMenu{&menuBar};
MenuItem aboutAction{&helpMenu};
HorizontalLayout layout{this};
TableView gameList{&layout, Size{~0, ~0}};
VerticalLayout controlLayout{&layout, Size{80, ~0}};
Button appendButton{&controlLayout, Size{~0, 0}};
Button modifyButton{&controlLayout, Size{~0, 0}};
Button removeButton{&controlLayout, Size{~0, 0}};
};
struct GameWindow : Window {
GameWindow();
auto show(Game = {}) -> void;
auto accept() -> void;
auto cancel() -> void;
auto reloadList() -> void;
auto updateWindow() -> void;
auto appendComponent(Component) -> void;
auto modifyComponent(Component) -> void;
auto removeComponent() -> void;
private:
bool modified = false;
bool create = true;
Game game;
VerticalLayout layout{this};
HorizontalLayout hashLayout{&layout, Size{~0, 0}};
Label hashLabel{&hashLayout, Size{50, 0}};
LineEdit hashEdit{&hashLayout, Size{~0, 0}};
HorizontalLayout infoLayout{&layout, Size{~0, 0}};
Label regionLabel{&infoLayout, Size{50, 0}};
LineEdit regionEdit{&infoLayout, Size{~0, 0}};
Label revisionLabel{&infoLayout, Size{0, 0}};
LineEdit revisionEdit{&infoLayout, Size{~0, 0}};
Label boardLabel{&infoLayout, Size{0, 0}};
LineEdit boardEdit{&infoLayout, Size{~0, 0}, 0};
HorizontalLayout nameLayout{&layout, Size{~0, 0}};
Label nameLabel{&nameLayout, Size{50, 0}};
LineEdit nameEdit{&nameLayout, Size{~0, 0}};
HorizontalLayout labelLayout{&layout, Size{~0, 0}};
Label labelLabel{&labelLayout, Size{50, 0}};
LineEdit labelEdit{&labelLayout, Size{~0, 0}};
HorizontalLayout noteLayout{&layout, Size{~0, 0}};
Label noteLabel{&noteLayout, Size{50, 0}};
LineEdit noteEdit{&noteLayout, Size{~0, 0}};
HorizontalLayout lowerLayout{&layout, Size{~0, ~0}};
Label componentLabel{&lowerLayout, Size{50, ~0}};
TreeView componentTree{&lowerLayout, Size{~0, ~0}};
VerticalLayout controlLayout{&lowerLayout, Size{0, ~0}};
Button appendMemoryButton{&controlLayout, Size{80, 0}};
Button appendOscillatorButton{&controlLayout, Size{80, 0}};
Button modifyComponentButton{&controlLayout, Size{80, 0}};
Button removeComponentButton{&controlLayout, Size{80, 0}};
Widget controlSpacer{&controlLayout, Size{0, ~0}};
Button acceptButton{&controlLayout, Size{80, 0}};
Button cancelButton{&controlLayout, Size{80, 0}};
};
struct MemoryWindow : Window {
MemoryWindow();
auto show(Memory = {}) -> void;
auto accept() -> void;
auto cancel() -> void;
auto updateWindow() -> void;
private:
bool modified = false;
bool create = true;
Memory memory;
VerticalLayout layout{this};
HorizontalLayout infoLayout{&layout, Size{~0, 0}};
Label typeLabel{&infoLayout, Size{80, 0}};
ComboEdit typeEdit{&infoLayout, Size{~0, 0}};
Label sizeLabel{&infoLayout, Size{0, 0}};
LineEdit sizeEdit{&infoLayout, Size{~0, 0}};
HorizontalLayout contentLayout{&layout, Size{~0, 0}};
Label contentLabel{&contentLayout, Size{80, 0}};
ComboEdit contentEdit{&contentLayout, Size{~0, 0}};
HorizontalLayout manufacturerLayout{&layout, Size{~0, 0}};
Label manufacturerLabel{&manufacturerLayout, Size{80, 0}};
LineEdit manufacturerEdit{&manufacturerLayout, Size{~0, 0}};
HorizontalLayout architectureLayout{&layout, Size{~0, 0}};
Label architectureLabel{&architectureLayout, Size{80, 0}};
LineEdit architectureEdit{&architectureLayout, Size{~0, 0}};
HorizontalLayout identifierLayout{&layout, Size{~0, 0}};
Label identifierLabel{&identifierLayout, Size{80, 0}};
LineEdit identifierEdit{&identifierLayout, Size{~0, 0}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Widget controlSpacer{&controlLayout, Size{~0, 0}};
CheckLabel volatileOption{&controlLayout, Size{0, 0}};
Button acceptButton{&controlLayout, Size{80, 0}};
Button cancelButton{&controlLayout, Size{80, 0}};
};
struct OscillatorWindow : Window {
OscillatorWindow();
auto show(Oscillator = {}) -> void;
auto accept() -> void;
auto cancel() -> void;
auto updateWindow() -> void;
private:
bool modified = false;
bool create = true;
Oscillator oscillator;
VerticalLayout layout{this};
HorizontalLayout frequencyLayout{&layout, Size{~0, 0}};
Label frequencyLabel{&frequencyLayout, Size{60, 0}};
LineEdit frequencyEdit{&frequencyLayout, Size{~0, 0}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Widget controlSpacer{&controlLayout, Size{~0, 0}};
Button acceptButton{&controlLayout, Size{80, 0}};
Button cancelButton{&controlLayout, Size{80, 0}};
};

2
genius/obj/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.o
*.d

1
genius/out/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
genius

View File

@@ -1,16 +1,15 @@
build := optimize
include ../nall/GNUmakefile
target := bsnes
binary := application
target := tomoko
objects := libco emulator audio video resource
build := performance
openmp := true
flags += -I. -I..
nall.path := ../nall
include $(nall.path)/GNUmakefile
ifeq ($(platform),windows)
link += $(if $(call streq,$(console),true),-mconsole,-mwindows)
ifeq ($(binary),application)
link += -mthreads -lpthread -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
link += -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
link += -Wl,-enable-auto-import
link += -Wl,-enable-runtime-pseudo-reloc
else ifeq ($(binary),library)
@@ -23,8 +22,6 @@ else ifeq ($(platform),macos)
link += -dynamiclib
endif
else ifneq ($(filter $(platform),linux bsd),)
flags += -fopenmp
link += -fopenmp
ifeq ($(binary),application)
flags += -march=native
link += -Wl,-export-dynamic
@@ -37,29 +34,66 @@ else
$(error "unsupported platform")
endif
compile = \
$(strip \
$(if $(filter %.c,$<), \
$(compiler) $(cflags) $(flags) $1 -c $< -o $@, \
$(if $(filter %.cpp,$<), \
$(compiler) $(cppflags) $(flags) $1 -c $< -o $@ \
) \
) \
)
objects := libco emulator
%.o: $<; $(call compile)
obj/libco.o: ../libco/libco.c
obj/emulator.o: emulator/emulator.cpp
all: build;
ifeq ($(target),higan)
cores := fc sfc ms md pce msx gb gba ws ngp
endif
obj/libco.o: ../libco/libco.c $(call rwildcard,../libco)
obj/emulator.o: emulator/emulator.cpp $(call rwildcard,emulator)
obj/audio.o: audio/audio.cpp $(call rwildcard,audio)
obj/video.o: video/video.cpp $(call rwildcard,video)
obj/resource.o: resource/resource.cpp $(call rwildcard,resource)
ifeq ($(target),bsnes)
cores := sfc gb
endif
ifneq ($(filter $(cores),fc),)
include fc/GNUmakefile
endif
ifneq ($(filter $(cores),sfc),)
include sfc/GNUmakefile
endif
ifneq ($(filter $(cores),ms),)
include ms/GNUmakefile
endif
ifneq ($(filter $(cores),md),)
include md/GNUmakefile
endif
ifneq ($(filter $(cores),pce),)
include pce/GNUmakefile
endif
ifneq ($(filter $(cores),msx),)
include msx/GNUmakefile
endif
ifneq ($(filter $(cores),gb),)
include gb/GNUmakefile
endif
ifneq ($(filter $(cores),gba),)
include gba/GNUmakefile
endif
ifneq ($(filter $(cores),ws),)
include ws/GNUmakefile
endif
ifneq ($(filter $(cores),ngp),)
include ngp/GNUmakefile
endif
include processor/GNUmakefile
flags += $(foreach c,$(call strupper,$(cores)),-DCORE_$c)
ui := target-$(target)
include $(ui)/GNUmakefile
-include obj/*.d
clean:
-@$(call delete,out/*)
-@$(call delete,obj/*)
$(call delete,obj/*)
$(call delete,out/*)

View File

@@ -1,70 +0,0 @@
auto Stream::reset(uint channels_, double inputFrequency, double outputFrequency) -> void {
this->inputFrequency = inputFrequency;
this->outputFrequency = outputFrequency;
channels.reset();
channels.resize(channels_);
for(auto& channel : channels) {
channel.filters.reset();
channel.resampler.reset(inputFrequency, outputFrequency);
}
}
auto Stream::setFrequency(double inputFrequency, maybe<double> outputFrequency) -> void {
this->inputFrequency = inputFrequency;
if(outputFrequency) this->outputFrequency = outputFrequency();
for(auto& channel : channels) {
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
}
}
auto Stream::addFilter(Filter::Order order, Filter::Type type, double cutoffFrequency, uint passes) -> void {
for(auto& channel : channels) {
for(auto pass : range(passes)) {
Filter filter{order};
if(order == Filter::Order::First) {
DSP::IIR::OnePole::Type _type;
if(type == Filter::Type::LowPass) _type = DSP::IIR::OnePole::Type::LowPass;
if(type == Filter::Type::HighPass) _type = DSP::IIR::OnePole::Type::HighPass;
filter.onePole.reset(_type, cutoffFrequency, inputFrequency);
}
if(order == Filter::Order::Second) {
DSP::IIR::Biquad::Type _type;
if(type == Filter::Type::LowPass) _type = DSP::IIR::Biquad::Type::LowPass;
if(type == Filter::Type::HighPass) _type = DSP::IIR::Biquad::Type::HighPass;
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.biquad.reset(_type, cutoffFrequency, inputFrequency, q);
}
channel.filters.append(filter);
}
}
}
auto Stream::pending() const -> bool {
return channels && channels[0].resampler.pending();
}
auto Stream::read(double samples[]) -> uint {
for(auto c : range(channels)) samples[c] = channels[c].resampler.read();
return channels.size();
}
auto Stream::write(const double samples[]) -> void {
for(auto c : range(channels)) {
double sample = samples[c] + 1e-25; //constant offset used to suppress denormals
for(auto& filter : channels[c].filters) {
switch(filter.order) {
case Filter::Order::First: sample = filter.onePole.process(sample); break;
case Filter::Order::Second: sample = filter.biquad.process(sample); break;
}
}
channels[c].resampler.write(sample);
}
audio.process();
}

View File

@@ -1,34 +1,16 @@
#include <emulator/emulator.hpp>
namespace Emulator {
#include "stream.cpp"
Audio audio;
auto Audio::reset(maybe<uint> channels_, maybe<double> frequency_) -> void {
interface = nullptr;
if(channels_) channels = channels_();
if(frequency_) frequency = frequency_();
streams.reset();
reverb.reset();
reverb.resize(channels);
for(auto c : range(channels)) {
reverb[c].resize(7);
reverb[c][0].resize(1229);
reverb[c][1].resize(1559);
reverb[c][2].resize(1907);
reverb[c][3].resize(4057);
reverb[c][4].resize(8117);
reverb[c][5].resize(8311);
reverb[c][6].resize(9931);
}
Audio::~Audio() {
reset(nullptr);
}
auto Audio::setInterface(Interface* interface) -> void {
auto Audio::reset(Interface* interface) -> void {
this->interface = interface;
streams.reset();
channels = 0;
}
auto Audio::setFrequency(double frequency) -> void {
@@ -46,11 +28,8 @@ auto Audio::setBalance(double balance) -> void {
this->balance = balance;
}
auto Audio::setReverb(bool enabled) -> void {
this->reverbEnable = enabled;
}
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
this->channels = max(this->channels, channels);
shared_pointer<Stream> stream = new Stream;
stream->reset(channels, frequency, this->frequency);
streams.append(stream);
@@ -58,7 +37,7 @@ auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stre
}
auto Audio::process() -> void {
while(true) {
while(streams) {
for(auto& stream : streams) {
if(!stream->pending()) return;
}
@@ -67,7 +46,7 @@ auto Audio::process() -> void {
for(auto& sample : samples) sample = 0.0;
for(auto& stream : streams) {
double buffer[16];
double buffer[channels];
uint length = stream->read(buffer), offset = 0;
for(auto& sample : samples) {
@@ -78,13 +57,6 @@ auto Audio::process() -> void {
for(auto c : range(channels)) {
samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
if(reverbEnable) {
samples[c] *= 0.125;
for(auto n : range(7)) samples[c] += 0.125 * reverb[c][n].last();
for(auto n : range(7)) reverb[c][n].write(samples[c]);
samples[c] *= 8.000;
}
}
if(channels == 2) {
@@ -92,7 +64,7 @@ auto Audio::process() -> void {
if(balance > 0.0) samples[0] *= 1.0 - balance;
}
platform->audioSample(samples, channels);
platform->audioFrame(samples, channels);
}
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include <nall/dsp/iir/dc-removal.hpp>
#include <nall/dsp/iir/one-pole.hpp>
#include <nall/dsp/iir/biquad.hpp>
#include <nall/dsp/resampler/cubic.hpp>
@@ -12,13 +13,12 @@ struct Filter;
struct Stream;
struct Audio {
auto reset(maybe<uint> channels = nothing, maybe<double> frequency = nothing) -> void;
auto setInterface(Interface* interface) -> void;
~Audio();
auto reset(Interface* interface) -> void;
auto setFrequency(double frequency) -> void;
auto setVolume(double volume) -> void;
auto setBalance(double balance) -> void;
auto setReverb(bool enabled) -> void;
auto createStream(uint channels, double frequency) -> shared_pointer<Stream>;
@@ -29,24 +29,22 @@ private:
vector<shared_pointer<Stream>> streams;
uint channels = 0;
double frequency = 0.0;
double frequency = 48000.0;
double volume = 1.0;
double balance = 0.0;
bool reverbEnable = false;
vector<vector<queue<double>>> reverb;
friend class Stream;
};
struct Filter {
enum class Order : uint { First, Second };
enum class Type : uint { LowPass, HighPass };
enum class Mode : uint { DCRemoval, OnePole, Biquad } mode;
enum class Type : uint { None, LowPass, HighPass } type;
enum class Order : uint { None, First, Second } order;
Order order;
DSP::IIR::OnePole onePole; //first-order
DSP::IIR::Biquad biquad; //second-order
DSP::IIR::DCRemoval dcRemoval;
DSP::IIR::OnePole onePole;
DSP::IIR::Biquad biquad;
};
struct Stream {
@@ -54,7 +52,9 @@ struct Stream {
auto setFrequency(double inputFrequency, maybe<double> outputFrequency = nothing) -> void;
auto addFilter(Filter::Order order, Filter::Type type, double cutoffFrequency, uint passes = 1) -> void;
auto addDCRemovalFilter() -> void;
auto addLowPassFilter(double cutoffFrequency, Filter::Order order, uint passes = 1) -> void;
auto addHighPassFilter(double cutoffFrequency, Filter::Order order, uint passes = 1) -> void;
auto pending() const -> bool;
auto read(double samples[]) -> uint;
@@ -68,6 +68,7 @@ struct Stream {
private:
struct Channel {
vector<Filter> filters;
vector<DSP::IIR::Biquad> nyquist;
DSP::Resampler::Cubic resampler;
};
vector<Channel> channels;

View File

@@ -0,0 +1,106 @@
auto Stream::reset(uint channelCount, double inputFrequency, double outputFrequency) -> void {
channels.reset();
channels.resize(channelCount);
for(auto& channel : channels) {
channel.filters.reset();
}
setFrequency(inputFrequency, outputFrequency);
}
auto Stream::setFrequency(double inputFrequency, maybe<double> outputFrequency) -> void {
this->inputFrequency = inputFrequency;
if(outputFrequency) this->outputFrequency = outputFrequency();
for(auto& channel : channels) {
channel.nyquist.reset();
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
}
if(this->inputFrequency >= this->outputFrequency * 2) {
//add a low-pass filter to prevent aliasing during resampling
double cutoffFrequency = min(25000.0, this->outputFrequency / 2.0 - 2000.0);
for(auto& channel : channels) {
uint passes = 3;
for(uint pass : range(passes)) {
DSP::IIR::Biquad filter;
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, this->inputFrequency, q);
channel.nyquist.append(filter);
}
}
}
}
auto Stream::addDCRemovalFilter() -> void {
return; //todo: test to ensure this is desirable before enabling
for(auto& channel : channels) {
Filter filter{Filter::Mode::DCRemoval, Filter::Type::None, Filter::Order::None};
channel.filters.append(filter);
}
}
auto Stream::addLowPassFilter(double cutoffFrequency, Filter::Order order, uint passes) -> void {
for(auto& channel : channels) {
for(uint pass : range(passes)) {
if(order == Filter::Order::First) {
Filter filter{Filter::Mode::OnePole, Filter::Type::LowPass, Filter::Order::First};
filter.onePole.reset(DSP::IIR::OnePole::Type::LowPass, cutoffFrequency, inputFrequency);
channel.filters.append(filter);
}
if(order == Filter::Order::Second) {
Filter filter{Filter::Mode::Biquad, Filter::Type::LowPass, Filter::Order::Second};
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.biquad.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, inputFrequency, q);
channel.filters.append(filter);
}
}
}
}
auto Stream::addHighPassFilter(double cutoffFrequency, Filter::Order order, uint passes) -> void {
for(auto& channel : channels) {
for(uint pass : range(passes)) {
if(order == Filter::Order::First) {
Filter filter{Filter::Mode::OnePole, Filter::Type::HighPass, Filter::Order::First};
filter.onePole.reset(DSP::IIR::OnePole::Type::HighPass, cutoffFrequency, inputFrequency);
channel.filters.append(filter);
}
if(order == Filter::Order::Second) {
Filter filter{Filter::Mode::Biquad, Filter::Type::HighPass, Filter::Order::Second};
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.biquad.reset(DSP::IIR::Biquad::Type::HighPass, cutoffFrequency, inputFrequency, q);
channel.filters.append(filter);
}
}
}
}
auto Stream::pending() const -> bool {
return channels && channels[0].resampler.pending();
}
auto Stream::read(double samples[]) -> uint {
for(uint c : range(channels.size())) samples[c] = channels[c].resampler.read();
return channels.size();
}
auto Stream::write(const double samples[]) -> void {
for(auto c : range(channels.size())) {
double sample = samples[c] + 1e-25; //constant offset used to suppress denormals
for(auto& filter : channels[c].filters) {
switch(filter.mode) {
case Filter::Mode::DCRemoval: sample = filter.dcRemoval.process(sample); break;
case Filter::Mode::OnePole: sample = filter.onePole.process(sample); break;
case Filter::Mode::Biquad: sample = filter.biquad.process(sample); break;
}
}
for(auto& filter : channels[c].nyquist) {
sample = filter.process(sample);
}
channels[c].resampler.write(sample);
}
audio.process();
}

View File

@@ -17,11 +17,11 @@ struct Cheat {
codes.reset();
}
auto append(uint addr, uint data, maybe<uint> comp = nothing) -> void {
auto append(uint addr, uint data, maybe<uint> comp = {}) -> void {
codes.append({addr, data, comp});
}
auto assign(const string_vector& list) -> void {
auto assign(const vector<string>& list) -> void {
reset();
for(auto& entry : list) {
for(auto code : entry.split("+")) {

View File

@@ -1,5 +1,9 @@
#include <emulator/emulator.hpp>
#include <emulator/audio/audio.cpp>
#include <emulator/video/video.cpp>
#include <emulator/resource/resource.cpp>
namespace Emulator {
Platform* platform = nullptr;

View File

@@ -1,24 +1,43 @@
#pragma once
#include <nall/nall.hpp>
#include <nall/platform.hpp>
#include <nall/adaptive-array.hpp>
#include <nall/any.hpp>
#include <nall/bit-field.hpp>
#include <nall/chrono.hpp>
#include <nall/dl.hpp>
#include <nall/endian.hpp>
#include <nall/image.hpp>
#include <nall/literals.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/shared-pointer.hpp>
#include <nall/string.hpp>
#include <nall/traits.hpp>
#include <nall/unique-pointer.hpp>
#include <nall/vector.hpp>
#include <nall/vfs.hpp>
#include <nall/hash/crc32.hpp>
#include <nall/hash/sha256.hpp>
using namespace nall;
#include "types.hpp"
#include <libco/libco.h>
#include <audio/audio.hpp>
#include <video/video.hpp>
#include <resource/resource.hpp>
#include <emulator/types.hpp>
#include <emulator/memory/readable.hpp>
#include <emulator/memory/writable.hpp>
#include <emulator/audio/audio.hpp>
#include <emulator/video/video.hpp>
#include <emulator/resource/resource.hpp>
namespace Emulator {
static const string Name = "higan";
static const string Version = "105";
static const string Version = "107";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";
//incremented only when serialization format changes
static const string SerializerVersion = "104";
static const string SerializerVersion = "107";
namespace Constants {
namespace Colorburst {
@@ -38,3 +57,4 @@ namespace Emulator {
#include "platform.hpp"
#include "interface.hpp"
#include "game.hpp"

110
higan/emulator/game.hpp Normal file
View File

@@ -0,0 +1,110 @@
#pragma once
namespace Emulator {
struct Game {
struct Memory;
struct Oscillator;
inline auto load(string_view) -> void;
inline auto memory(Markup::Node) -> maybe<Memory>;
inline auto oscillator(natural = 0) -> maybe<Oscillator>;
struct Memory {
Memory() = default;
inline Memory(Markup::Node);
explicit operator bool() const { return (bool)type; }
inline auto name() const -> string;
string type;
natural size;
string content;
string manufacturer;
string architecture;
string identifier;
boolean nonVolatile;
};
struct Oscillator {
Oscillator() = default;
inline Oscillator(Markup::Node);
explicit operator bool() const { return frequency; }
natural frequency;
};
Markup::Node document;
string sha256;
string label;
string name;
string region;
string revision;
string board;
vector<Memory> memoryList;
vector<Oscillator> oscillatorList;
};
auto Game::load(string_view text) -> void {
document = BML::unserialize(text);
sha256 = document["game/sha256"].text();
label = document["game/label"].text();
name = document["game/name"].text();
region = document["game/region"].text();
revision = document["game/revision"].text();
board = document["game/board"].text();
for(auto node : document.find("game/board/memory")) {
memoryList.append(Memory{node});
}
for(auto node : document.find("game/board/oscillator")) {
oscillatorList.append(Oscillator{node});
}
}
auto Game::memory(Markup::Node node) -> maybe<Memory> {
if(!node) return nothing;
for(auto& memory : memoryList) {
auto type = node["type"].text();
auto size = node["size"].natural();
auto content = node["content"].text();
auto manufacturer = node["manufacturer"].text();
auto architecture = node["architecture"].text();
auto identifier = node["identifier"].text();
if(type && type != memory.type) continue;
if(size && size != memory.size) continue;
if(content && content != memory.content) continue;
if(manufacturer && manufacturer != memory.manufacturer) continue;
if(architecture && architecture != memory.architecture) continue;
if(identifier && identifier != memory.identifier) continue;
return memory;
}
return nothing;
}
auto Game::oscillator(natural index) -> maybe<Oscillator> {
if(index < oscillatorList.size()) return oscillatorList[index];
return nothing;
}
Game::Memory::Memory(Markup::Node node) {
type = node["type"].text();
size = node["size"].natural();
content = node["content"].text();
manufacturer = node["manufacturer"].text();
architecture = node["architecture"].text();
identifier = node["identifier"].text();
nonVolatile = !(bool)node["volatile"];
}
auto Game::Memory::name() const -> string {
if(architecture) return string{architecture, ".", content, ".", type}.downcase();
return string{content, ".", type}.downcase();
}
Game::Oscillator::Oscillator(Markup::Node node) {
frequency = node["frequency"].natural();
}
}

View File

@@ -6,79 +6,96 @@ struct Interface {
struct Information {
string manufacturer;
string name;
bool overscan;
} information;
struct Medium {
uint id;
string name;
string type; //extension
};
vector<Medium> media;
struct Device {
uint id;
string name;
struct Input {
uint type; //0 = digital, 1 = analog (relative), 2 = rumble
string name;
};
vector<Input> inputs;
string extension;
bool resettable = false;
};
struct Port {
uint id;
struct Display {
struct Type { enum : uint {
CRT,
LCD,
};};
uint id = 0;
string name;
vector<Device> devices;
};
vector<Port> ports;
//information
virtual auto manifest() -> string = 0;
virtual auto title() -> string = 0;
struct VideoInformation {
uint type = 0;
uint colors = 0;
uint width = 0;
uint height = 0;
uint internalWidth = 0;
uint internalHeight = 0;
double aspectCorrection = 0;
double refreshRate = 0;
};
virtual auto videoInformation() -> VideoInformation = 0;
virtual auto videoColors() -> uint32 = 0;
virtual auto videoColor(uint32 color) -> uint64 = 0;
//media interface
struct Port {
uint id;
string name;
};
struct Device {
uint id;
string name;
};
struct Input {
struct Type { enum : uint {
Hat,
Button,
Trigger,
Control,
Axis,
Rumble,
};};
uint type;
string name;
};
//information
virtual auto information() -> Information { return {}; }
virtual auto display() -> Display { return {}; }
virtual auto color(uint32 color) -> uint64 { return 0; }
//game interface
virtual auto loaded() -> bool { return false; }
virtual auto sha256() -> string { return ""; }
virtual auto load(uint id) -> bool { return false; }
virtual auto hashes() -> vector<string> { return {}; }
virtual auto manifests() -> vector<string> { return {}; }
virtual auto titles() -> vector<string> { return {}; }
virtual auto load() -> bool { return false; }
virtual auto save() -> void {}
virtual auto unload() -> void {}
//system interface
virtual auto ports() -> vector<Port> { return {}; }
virtual auto devices(uint port) -> vector<Device> { return {}; }
virtual auto inputs(uint device) -> vector<Input> { return {}; }
virtual auto connected(uint port) -> uint { return 0; }
virtual auto connect(uint port, uint device) -> void {}
virtual auto power() -> void {}
virtual auto reset() -> void {}
virtual auto run() -> void {}
//time functions
virtual auto rtc() -> bool { return false; }
virtual auto rtcSynchronize() -> void {}
virtual auto synchronize(uint64 timestamp = 0) -> void {}
//state functions
virtual auto serialize() -> serializer = 0;
virtual auto unserialize(serializer&) -> bool = 0;
virtual auto serialize() -> serializer { return {}; }
virtual auto unserialize(serializer&) -> bool { return false; }
//cheat functions
virtual auto cheatSet(const string_vector& = {}) -> void {}
virtual auto cheats(const vector<string>& = {}) -> void {}
//configuration
virtual auto configuration() -> string { return {}; }
virtual auto configuration(string name) -> string { return {}; }
virtual auto configure(string configuration = "") -> bool { return false; }
virtual auto configure(string name, string value) -> bool { return false; }
//settings
virtual auto cap(const string& name) -> bool { return false; }
virtual auto get(const string& name) -> any { return {}; }
virtual auto set(const string& name, const any& value) -> bool { return false; }
//shared functions
auto videoColor(uint16 r, uint16 g, uint16 b) -> uint32;
};
}

View File

@@ -0,0 +1,30 @@
#pragma once
namespace Emulator::Memory {
inline auto mirror(uint address, uint size) -> uint {
if(size == 0) return 0;
uint base = 0;
uint mask = 1 << 31;
while(address >= size) {
while(!(address & mask)) mask >>= 1;
address -= mask;
if(size > mask) {
size -= mask;
base += mask;
}
mask >>= 1;
}
return base + address;
}
inline auto reduce(uint address, uint mask) -> uint {
while(mask) {
uint bits = (mask & -mask) - 1;
address = address >> 1 & ~bits | address & bits;
mask = (mask & mask - 1) >> 1;
}
return address;
}
}

View File

@@ -0,0 +1,63 @@
#pragma once
#include <emulator/memory/memory.hpp>
namespace Emulator::Memory {
template<typename T>
struct Readable {
~Readable() { reset(); }
inline auto reset() -> void {
delete[] self.data;
self.data = nullptr;
self.size = 0;
self.mask = 0;
}
inline auto allocate(uint size, T fill = ~0ull) -> void {
if(!size) return reset();
delete[] self.data;
self.size = size;
self.mask = bit::round(self.size) - 1;
self.data = new T[self.mask + 1];
memory::fill<T>(self.data, self.mask + 1, fill);
}
inline auto load(vfs::shared::file fp) -> void {
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
for(uint address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
inline auto save(vfs::shared::file fp) -> void {
fp->write(self.data, self.size * sizeof(T));
}
explicit operator bool() const { return (bool)self.data; }
inline auto data() const -> const T* { return self.data; }
inline auto size() const -> uint { return self.size; }
inline auto mask() const -> uint { return self.mask; }
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
inline auto write(uint address, T data) const -> void {}
auto serialize(serializer& s) -> void {
const uint size = self.size;
s.integer(self.size);
s.integer(self.mask);
if(self.size != size) allocate(self.size);
s.array(self.data, self.size);
}
private:
struct {
T* data = nullptr;
uint size = 0;
uint mask = 0;
} self;
};
}

View File

@@ -0,0 +1,65 @@
#pragma once
#include <emulator/memory/memory.hpp>
namespace Emulator::Memory {
template<typename T>
struct Writable {
~Writable() { reset(); }
inline auto reset() -> void {
delete[] self.data;
self.data = nullptr;
self.size = 0;
self.mask = 0;
}
inline auto allocate(uint size, T fill = ~0ull) -> void {
if(!size) return reset();
delete[] self.data;
self.size = size;
self.mask = bit::round(self.size) - 1;
self.data = new T[self.mask + 1];
memory::fill<T>(self.data, self.mask + 1, fill);
}
inline auto load(vfs::shared::file fp) -> void {
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
for(uint address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
inline auto save(vfs::shared::file fp) -> void {
fp->write(self.data, self.size * sizeof(T));
}
explicit operator bool() const { return (bool)self.data; }
inline auto data() -> T* { return self.data; }
inline auto data() const -> const T* { return self.data; }
inline auto size() const -> uint { return self.size; }
inline auto mask() const -> uint { return self.mask; }
inline auto operator[](uint address) -> T& { return self.data[address & self.mask]; }
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
inline auto write(uint address, T data) -> void { self.data[address & self.mask] = data; }
auto serialize(serializer& s) -> void {
const uint size = self.size;
s.integer(self.size);
s.integer(self.mask);
if(self.size != size) allocate(self.size);
s.array(self.data, self.size);
}
private:
struct {
T* data = nullptr;
uint size = 0;
uint mask = 0;
} self;
};
}

View File

@@ -4,27 +4,24 @@ namespace Emulator {
struct Platform {
struct Load {
Load() : _pathID(nothing) {}
Load(uint pathID, string option = "") : _pathID(pathID), _option(option) {}
Load() = default;
Load(uint pathID, string option = "") : valid(true), pathID(pathID), option(option) {}
explicit operator bool() const { return valid; }
explicit operator bool() const { return (bool)_pathID; }
auto pathID() const -> uint { return _pathID(); }
auto option() const -> string { return _option; }
private:
maybe<uint> _pathID;
string _option;
bool valid = false;
uint pathID = 0;
string option;
};
virtual auto path(uint id) -> string { return ""; }
virtual auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> vfs::shared::file { return {}; }
virtual auto load(uint id, string name, string type, string_vector options = {}) -> Load { return {}; }
virtual auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void {}
virtual auto audioSample(const double* samples, uint channels) -> void {}
virtual auto load(uint id, string name, string type, vector<string> options = {}) -> Load { return {}; }
virtual auto videoFrame(const uint32* data, uint pitch, uint width, uint height) -> void {}
virtual auto audioFrame(const double* samples, uint channels) -> void {}
virtual auto inputPoll(uint port, uint device, uint input) -> int16 { return 0; }
virtual auto inputRumble(uint port, uint device, uint input, bool enable) -> void {}
virtual auto dipSettings(Markup::Node node) -> uint { return 0; }
virtual auto notify(string text) -> void { print(text, "\n"); }
virtual auto notify(string text) -> void {}
};
extern Platform* platform;

View File

@@ -1,6 +1,4 @@
namespace name=Resource
namespace name=Logo
binary name=higan file=logo/higan.png
namespace name=Sprite
binary name=CrosshairRed file=sprite/crosshair-red.png
binary name=CrosshairGreen file=sprite/crosshair-green.png

View File

@@ -0,0 +1,45 @@
#include "resource.hpp"
namespace Resource {
namespace Sprite {
const unsigned char CrosshairRed[342] = {
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
196,1,149,43,14,27,0,0,0,248,73,68,65,84,88,133,205,87,65,14,196,32,8,132,102,255,255,101,246,176,177,139,
148,81,80,27,229,212,70,102,6,212,0,50,229,77,26,107,156,37,139,2,228,241,209,39,11,113,71,156,68,139,106,128,
56,255,198,175,203,223,114,16,79,68,253,138,90,99,141,113,112,80,231,131,196,11,83,52,19,43,196,53,135,147,7,38,
150,104,244,212,32,86,235,228,236,20,6,200,207,191,117,215,70,12,242,94,139,133,166,236,173,236,67,252,111,139,67,157,
237,71,48,27,192,244,142,93,228,23,148,144,184,228,131,96,254,3,164,4,176,213,108,37,52,5,208,53,47,227,81,28,
49,153,102,163,88,96,149,68,150,193,21,223,59,128,68,43,69,13,103,4,199,246,8,34,151,240,209,249,38,112,251,47,
97,177,209,74,152,246,95,93,9,211,51,160,181,99,142,128,104,115,55,124,59,136,115,7,146,237,51,33,2,71,166,226,
94,23,13,77,214,104,44,103,174,163,143,86,189,244,187,224,232,151,81,21,132,39,210,33,91,246,54,132,193,44,226,219,
107,95,57,136,120,253,172,254,16,23,0,0,0,0,73,69,78,68,174,66,96,130,
};
const unsigned char CrosshairGreen[329] = {
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
196,1,149,43,14,27,0,0,0,235,73,68,65,84,88,133,213,87,65,18,195,32,8,196,78,31,230,211,253,153,61,180,
52,18,145,1,193,97,178,39,141,44,139,24,69,11,216,209,133,177,98,117,166,37,92,162,77,176,170,118,223,26,163,78,
68,71,145,198,244,169,157,57,35,84,248,43,222,255,109,154,254,113,140,114,102,222,18,239,165,120,251,181,42,0,232,103,
114,217,85,226,163,27,124,232,163,87,142,115,153,82,137,71,98,233,247,21,44,228,194,169,217,171,252,159,22,95,234,164,
47,129,55,128,144,140,237,166,63,132,151,190,4,247,147,16,103,35,157,90,220,140,119,121,80,224,94,108,0,164,227,119,
182,221,229,13,182,82,193,225,176,42,56,59,188,105,9,52,5,3,109,58,243,205,202,203,255,9,17,251,91,202,169,227,
205,128,235,198,19,17,64,40,82,171,225,233,32,158,113,33,65,164,222,9,105,16,50,81,55,238,88,210,212,119,1,0,
238,241,241,126,143,125,62,216,173,151,209,35,222,134,235,96,98,252,229,226,3,112,72,179,236,202,138,114,18,0,0,0,
0,73,69,78,68,174,66,96,130,
};
const unsigned char CrosshairBlue[332] = {
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
196,1,149,43,14,27,0,0,0,238,73,68,65,84,88,133,213,87,91,18,195,32,8,196,78,15,232,81,189,161,253,9,
25,52,98,121,57,76,246,43,137,44,11,24,69,11,232,209,55,99,69,235,76,74,184,69,107,229,245,91,27,220,137,124,
75,140,58,21,165,34,181,246,199,251,100,167,174,200,32,124,137,119,124,134,177,252,116,108,224,44,120,44,190,156,56,102,
163,204,228,182,107,173,80,31,93,225,67,30,189,112,124,85,41,145,120,36,88,191,159,96,33,23,78,101,47,242,127,90,
156,213,73,159,2,111,0,33,21,179,150,63,132,151,62,5,243,78,136,217,236,118,173,85,198,86,30,20,152,154,13,192,
118,251,125,216,90,121,212,118,215,112,86,224,26,142,133,247,152,2,73,195,64,155,190,248,166,229,229,255,132,8,243,146,
242,234,120,43,224,58,241,68,4,16,138,212,110,120,58,136,119,28,72,16,169,103,194,33,136,63,68,209,184,103,74,83,
239,5,0,215,26,167,231,123,124,103,130,53,221,140,94,113,55,100,131,9,242,151,139,31,79,50,234,237,105,206,30,22,
0,0,0,0,73,69,78,68,174,66,96,130,
};
}
}

View File

@@ -0,0 +1,7 @@
namespace Resource {
namespace Sprite {
extern const unsigned char CrosshairRed[342];
extern const unsigned char CrosshairGreen[329];
extern const unsigned char CrosshairBlue[332];
}
}

View File

Before

Width:  |  Height:  |  Size: 332 B

After

Width:  |  Height:  |  Size: 332 B

View File

Before

Width:  |  Height:  |  Size: 329 B

After

Width:  |  Height:  |  Size: 329 B

View File

Before

Width:  |  Height:  |  Size: 342 B

After

Width:  |  Height:  |  Size: 342 B

View File

@@ -15,6 +15,10 @@ struct Thread {
inline auto scalar() const { return _scalar; }
inline auto clock() const { return _clock; }
auto setHandle(cothread_t handle) -> void {
_handle = handle;
}
auto setFrequency(double frequency) -> void {
_frequency = frequency + 0.5;
_scalar = Second / _frequency;

View File

@@ -1,16 +1,15 @@
#include <emulator/emulator.hpp>
namespace Emulator {
#include "sprite.cpp"
Video video;
Video::~Video() {
reset();
reset(nullptr);
}
auto Video::reset() -> void {
interface = nullptr;
auto Video::reset(Interface* interface) -> void {
this->interface = interface;
sprites.reset();
delete buffer;
buffer = nullptr;
@@ -25,18 +24,14 @@ auto Video::reset() -> void {
effects.rotateLeft = false;
}
auto Video::setInterface(Interface* interface) -> void {
this->interface = interface;
}
auto Video::setPalette() -> void {
if(!interface) return;
delete palette;
colors = interface->videoColors();
colors = interface->display().colors;
palette = new uint32[colors];
for(auto index : range(colors)) {
uint64 color = interface->videoColor(index);
uint64 color = interface->color(index);
uint16 b = color.bits( 0,15);
uint16 g = color.bits(16,31);
uint16 r = color.bits(32,47);
@@ -63,11 +58,17 @@ auto Video::setPalette() -> void {
b = uclamp<16>(b * luminance);
}
//convert color from 16-bits/channel to 8-bits/channel; force alpha to 1.0
palette[index] = a.byte(1) << 24 | r.byte(1) << 16 | g.byte(1) << 8 | b.byte(1) << 0;
switch(depth) {
case 24: palette[index] = r >> 8 << 16 | g >> 8 << 8 | b >> 8 << 0; break;
case 30: palette[index] = r >> 6 << 20 | g >> 6 << 10 | b >> 6 << 0; break;
}
}
}
auto Video::setDepth(uint depth) -> void {
this->depth = depth;
}
auto Video::setSaturation(double saturation) -> void {
this->saturation = saturation;
}
@@ -101,7 +102,7 @@ auto Video::createSprite(uint width, uint height) -> shared_pointer<Sprite> {
}
auto Video::removeSprite(shared_pointer<Sprite> sprite) -> bool {
for(uint n : range(sprites)) {
for(uint n : range(sprites.size())) {
if(sprite == sprites[n]) {
sprites.remove(n);
return true;
@@ -133,21 +134,23 @@ auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void
*target++ = color;
}
} else {
uint32 mask = depth == 30 ? 0x40100401 : 0x01010101;
for(uint x : range(width)) {
auto a = *target;
auto b = palette[*source++];
*target++ = (a + b - ((a ^ b) & 0x01010101)) >> 1;
*target++ = (a + b - ((a ^ b) & mask)) >> 1;
}
}
}
if(effects.colorBleed) {
uint32 mask = depth == 30 ? 0x40100401 : 0x01010101;
for(uint y : range(height)) {
auto target = output + y * width;
for(uint x : range(width)) {
auto a = target[x];
auto b = target[x + (x != width - 1)];
target[x] = (a + b - ((a ^ b) & 0x01010101)) >> 1;
target[x] = (a + b - ((a ^ b) & mask)) >> 1;
}
}
}
@@ -167,6 +170,7 @@ auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void
for(auto& sprite : sprites) {
if(!sprite->visible) continue;
uint32 opaqueAlpha = depth == 30 ? 0xc0000000 : 0xff000000;
for(int y : range(sprite->height)) {
for(int x : range(sprite->width)) {
int pixelY = sprite->y + y;
@@ -176,12 +180,12 @@ auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void
if(pixelX < 0 || pixelX >= width) continue;
auto pixel = sprite->pixels[y * sprite->width + x];
if(pixel) output[pixelY * width + pixelX] = 0xff000000 | pixel;
if(pixel) output[pixelY * width + pixelX] = opaqueAlpha | pixel;
}
}
}
platform->videoRefresh(output, width * sizeof(uint32), width, height);
platform->videoFrame(output, width * sizeof(uint32), width, height);
}
}

View File

@@ -14,11 +14,10 @@ struct Video {
};
~Video();
auto reset() -> void;
auto setInterface(Interface* interface) -> void;
auto reset(Interface* interface) -> void;
auto setPalette() -> void;
auto setDepth(uint depth) -> void;
auto setSaturation(double saturation) -> void;
auto setGamma(double gamma) -> void;
auto setLuminance(double luminance) -> void;
@@ -42,6 +41,7 @@ private:
uint height = 0;
uint colors = 0;
uint depth = 24;
double saturation = 1.0;
double gamma = 1.0;
double luminance = 1.0;

View File

@@ -3,11 +3,11 @@ processors += mos6502
objects += fc-interface fc-system fc-controller
objects += fc-memory fc-cartridge fc-cpu fc-apu fc-ppu
obj/fc-interface.o: fc/interface/interface.cpp $(call rwildcard,fc/interface/)
obj/fc-system.o: fc/system/system.cpp $(call rwildcard,fc/system/)
obj/fc-controller.o: fc/controller/controller.cpp $(call rwildcard,fc/controller/)
obj/fc-memory.o: fc/memory/memory.cpp $(call rwildcard,fc/memory/)
obj/fc-cartridge.o: fc/cartridge/cartridge.cpp $(call rwildcard,fc/cartridge/)
obj/fc-cpu.o: fc/cpu/cpu.cpp $(call rwildcard,fc/cpu/)
obj/fc-apu.o: fc/apu/apu.cpp $(call rwildcard,fc/apu/)
obj/fc-ppu.o: fc/ppu/ppu.cpp $(call rwildcard,fc/ppu/)
obj/fc-interface.o: fc/interface/interface.cpp
obj/fc-system.o: fc/system/system.cpp
obj/fc-controller.o: fc/controller/controller.cpp
obj/fc-memory.o: fc/memory/memory.cpp
obj/fc-cartridge.o: fc/cartridge/cartridge.cpp
obj/fc-cpu.o: fc/cpu/cpu.cpp
obj/fc-apu.o: fc/apu/apu.cpp
obj/fc-ppu.o: fc/ppu/ppu.cpp

View File

@@ -71,13 +71,13 @@ auto APU::setSample(int16 sample) -> void {
cartridgeSample = sample;
}
auto APU::power() -> void {
auto APU::power(bool reset) -> void {
create(APU::Enter, system.frequency());
stream = Emulator::audio.createStream(1, frequency() / rate());
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 90.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 440.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::LowPass, 14000.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
stream->addHighPassFilter( 90.0, Emulator::Filter::Order::First);
stream->addHighPassFilter( 440.0, Emulator::Filter::Order::First);
stream->addLowPassFilter (14000.0, Emulator::Filter::Order::First);
stream->addDCRemovalFilter();
pulse[0].power();
pulse[1].power();

View File

@@ -12,7 +12,7 @@ struct APU : Thread {
auto setIRQ() -> void;
auto setSample(int16 sample) -> void;
auto power() -> void;
auto power(bool reset) -> void;
auto readIO(uint16 addr) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void;

View File

@@ -22,6 +22,7 @@ struct BandaiFCG : Board {
case 2: return 0x0000 | (addr & 0x03ff);
case 3: return 0x0400 | (addr & 0x03ff);
}
unreachable;
}
auto readPRG(uint addr) -> uint8 {

View File

@@ -21,63 +21,57 @@
Board::Board(Markup::Node& document) {
cartridge.board = this;
auto board = document["board"];
information.type = document["game/board"].text();
information.type = board["id"].text();
information.battery = (bool)board["prg/ram/name"];
auto prom = board["prg/rom"];
auto pram = board["prg/ram"];
auto crom = board["chr/rom"];
auto cram = board["chr/ram"];
prgrom.size = prom["size"].natural();
prgram.size = pram["size"].natural();
chrrom.size = crom["size"].natural();
chrram.size = cram["size"].natural();
if(prgrom.size) prgrom.data = new uint8_t[prgrom.size]();
if(prgram.size) prgram.data = new uint8_t[prgram.size]();
if(chrrom.size) chrrom.data = new uint8_t[chrrom.size]();
if(chrram.size) chrram.data = new uint8_t[chrram.size]();
if(prgrom.name = prom["name"].text()) {
if(auto fp = platform->open(cartridge.pathID(), prgrom.name, File::Read, File::Required)) {
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=ROM,content=Program)"]}) {
if(prgrom.size = memory.size) prgrom.data = new uint8_t[prgrom.size]();
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read, File::Required)) {
fp->read(prgrom.data, min(prgrom.size, fp->size()));
}
}
if(prgram.name = pram["name"].text()) {
if(auto fp = platform->open(cartridge.pathID(), prgram.name, File::Read)) {
fp->read(prgram.data, min(prgram.size, fp->size()));
}
}
if(chrrom.name = crom["name"].text()) {
if(auto fp = platform->open(cartridge.pathID(), chrrom.name, File::Read, File::Required)) {
fp->read(chrrom.data, min(chrrom.size, fp->size()));
}
}
if(chrram.name = cram["name"].text()) {
if(auto fp = platform->open(cartridge.pathID(), chrram.name, File::Read)) {
fp->read(chrram.data, min(chrram.size, fp->size()));
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
if(prgram.size = memory.size) prgram.data = new uint8_t[prgram.size](), prgram.writable = true;
if(memory.nonVolatile) {
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read)) {
fp->read(prgram.data, min(prgram.size, fp->size()));
}
}
}
prgram.writable = true;
chrram.writable = true;
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=ROM,content=Character)"]}) {
if(chrrom.size = memory.size) chrrom.data = new uint8_t[chrrom.size]();
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read, File::Required)) {
fp->read(chrrom.data, min(chrrom.size, fp->size()));
}
}
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Character)"]}) {
if(chrram.size = memory.size) chrram.data = new uint8_t[chrram.size](), chrram.writable = true;
if(memory.nonVolatile) {
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read)) {
fp->read(chrram.data, min(chrram.size, fp->size()));
}
}
}
}
auto Board::save() -> void {
auto document = BML::unserialize(cartridge.manifest());
if(auto name = document["board/prg/ram/name"].text()) {
if(auto fp = platform->open(cartridge.pathID(), name, File::Write)) {
fp->write(prgram.data, prgram.size);
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
if(memory.nonVolatile) {
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Write)) {
fp->write(prgram.data, prgram.size);
}
}
}
if(auto name = document["board/chr/ram/name"].text()) {
if(auto fp = platform->open(cartridge.pathID(), name, File::Write)) {
fp->write(chrram.data, chrram.size);
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Character)"]}) {
if(memory.nonVolatile) {
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Write)) {
fp->write(chrram.data, chrram.size);
}
}
}
}
@@ -138,9 +132,9 @@ auto Board::serialize(serializer& s) -> void {
auto Board::load(string manifest) -> Board* {
auto document = BML::unserialize(manifest);
cartridge.information.title = document["information/title"].text();
cartridge.information.title = document["game/label"].text();
string type = document["board/id"].text();
string type = document["game/board"].text();
if(type == "BANDAI-FCG" ) return new BandaiFCG(document);

View File

@@ -39,7 +39,6 @@ struct Board {
struct Information {
string type;
bool battery;
} information;
Memory prgrom;

View File

@@ -151,6 +151,7 @@ struct Sunsoft5B : Board {
case 2: return 0x0000 | (addr & 0x03ff); //first
case 3: return 0x0400 | (addr & 0x03ff); //second
}
unreachable;
}
auto readCHR(uint addr) -> uint8 {

View File

@@ -17,8 +17,8 @@ auto Cartridge::main() -> void {
auto Cartridge::load() -> bool {
if(auto loaded = platform->load(ID::Famicom, "Famicom", "fc", {"NTSC-J", "NTSC-U", "PAL"})) {
information.pathID = loaded.pathID();
information.region = loaded.option();
information.pathID = loaded.pathID;
information.region = loaded.option;
} else return false;
if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {

View File

@@ -10,7 +10,7 @@ struct Cartridge : Thread {
auto pathID() const -> uint { return information.pathID; }
auto region() const -> string { return information.region; }
auto sha256() const -> string { return information.sha256; }
auto hash() const -> string { return information.sha256; }
auto manifest() const -> string { return information.manifest; }
auto title() const -> string { return information.title; }

View File

@@ -34,6 +34,7 @@ struct MMC1 : Chip {
case 2: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
case 3: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
}
unreachable;
}
auto writeIO(uint addr, uint8 data) -> void {

View File

@@ -35,6 +35,7 @@ struct MMC3 : Chip {
case 3:
return (0x3f << 13) | (addr & 0x1fff);
}
unreachable;
}
auto addrCHR(uint addr) const -> uint {
@@ -53,11 +54,13 @@ struct MMC3 : Chip {
if(addr <= 0x17ff) return (chrBank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x1fff) return (chrBank[1] << 10) | (addr & 0x07ff);
}
return 0;
}
auto addrCIRAM(uint addr) const -> uint {
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
unreachable;
}
auto readRAM(uint addr) -> uint8 {

View File

@@ -83,6 +83,8 @@ struct MMC5 : Chip {
case 0x5205: return (multiplier * multiplicand) >> 0;
case 0x5206: return (multiplier * multiplicand) >> 8;
}
return 0x00;
}
auto writePRG(uint addr, uint8 data) -> void {
@@ -215,6 +217,8 @@ struct MMC5 : Chip {
auto bank = chrSpriteBank[(addr / 0x0400)];
return (bank * 0x0400) + (addr & 0x03ff);
}
unreachable;
}
auto chrBGAddr(uint addr) -> uint {
@@ -239,6 +243,8 @@ struct MMC5 : Chip {
auto bank = chrBGBank[(addr / 0x0400)];
return (bank * 0x0400) + (addr & 0x03ff);
}
unreachable;
}
auto chrVSAddr(uint addr) -> uint {
@@ -274,6 +280,7 @@ struct MMC5 : Chip {
case 2: return exramMode < 2 ? exram[addr & 0x03ff] : (uint8)0x00;
case 3: return (hcounter & 2) == 0 ? fillmodeTile : fillmodeColor;
}
unreachable;
}
auto readCHR(uint addr) -> uint8 {

View File

@@ -35,6 +35,7 @@ struct MMC6 : Chip {
case 3:
return (0x3f << 13) | (addr & 0x1fff);
}
unreachable;
}
auto addrCHR(uint addr) const -> uint {
@@ -53,11 +54,13 @@ struct MMC6 : Chip {
if(addr <= 0x17ff) return (chrBank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x1fff) return (chrBank[1] << 10) | (addr & 0x07ff);
}
return 0;
}
auto addrCIRAM(uint addr) const -> uint {
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
unreachable;
}
auto readRAM(uint addr) -> uint8 {

View File

@@ -115,6 +115,7 @@ struct VRC6 : Chip {
if((addr & 0xc000) == 0x8000) return (prgBank[0] << 14) | (addr & 0x3fff);
if((addr & 0xe000) == 0xc000) return (prgBank[1] << 13) | (addr & 0x1fff);
if((addr & 0xe000) == 0xe000) return ( 0xff << 13) | (addr & 0x1fff);
return 0x00;
}
auto addrCHR(uint addr) const -> uint {
@@ -129,6 +130,7 @@ struct VRC6 : Chip {
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
}
unreachable;
}
auto readRAM(uint addr) -> uint8 {

View File

@@ -96,6 +96,7 @@ struct VRC7 : Chip {
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
}
unreachable;
}
auto power() -> void {

View File

@@ -24,22 +24,21 @@ auto CPU::step(uint clocks) -> void {
for(auto peripheral : peripherals) synchronize(*peripheral);
}
auto CPU::power() -> void {
auto CPU::power(bool reset) -> void {
MOS6502::BCD = 0;
MOS6502::power();
create(CPU::Enter, system.frequency());
for(auto addr : range(0x0800)) ram[addr] = 0xff;
ram[0x0008] = 0xf7;
ram[0x0009] = 0xef;
ram[0x000a] = 0xdf;
ram[0x000f] = 0xbf;
if(!reset) for(auto& data : ram) data = 0xff;
ram[0x008] = 0xf7; //todo: what is this about?
ram[0x009] = 0xef;
ram[0x00a] = 0xdf;
ram[0x00f] = 0xbf;
r.pc.byte(0) = bus.read(0xfffc);
r.pc.byte(1) = bus.read(0xfffd);
memory::fill(&io, sizeof(IO));
io.rdyLine = 1;
io = {};
}
}

View File

@@ -6,7 +6,7 @@ struct CPU : Processor::MOS6502, Thread {
auto main() -> void;
auto step(uint clocks) -> void;
auto power() -> void;
auto power(bool reset) -> void;
//memory.cpp
auto readRAM(uint11 addr) -> uint8;
@@ -37,20 +37,20 @@ struct CPU : Processor::MOS6502, Thread {
//protected:
vector<Thread*> peripherals;
uint8 ram[0x0800];
uint8 ram[0x800];
struct IO {
bool interruptPending;
bool nmiPending;
bool nmiLine;
bool irqLine;
bool apuLine;
bool interruptPending = 0;
bool nmiPending = 0;
bool nmiLine = 0;
bool irqLine = 0;
bool apuLine = 0;
bool rdyLine;
bool rdyAddrValid;
bool rdyLine = 1;
bool rdyAddrValid = 0;
uint16 rdyAddrValue;
bool oamdmaPending;
bool oamdmaPending = 0;
uint8 oamdmaPage;
} io;
};

View File

@@ -4,69 +4,35 @@ namespace Famicom {
Settings settings;
Interface::Interface() {
auto Interface::information() -> Information {
Information information;
information.manufacturer = "Nintendo";
information.name = "Famicom";
information.overscan = true;
media.append({ID::Famicom, "Famicom", "fc"});
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};
Port controllerPort2{ID::Port::Controller2, "Controller Port 2"};
{ Device device{ID::Device::None, "None"};
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
{ Device device{ID::Device::Gamepad, "Gamepad"};
device.inputs.append({0, "Up" });
device.inputs.append({0, "Down" });
device.inputs.append({0, "Left" });
device.inputs.append({0, "Right" });
device.inputs.append({0, "B" });
device.inputs.append({0, "A" });
device.inputs.append({0, "Select"});
device.inputs.append({0, "Start" });
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
ports.append(move(controllerPort1));
ports.append(move(controllerPort2));
information.extension = "fc";
information.resettable = true;
return information;
}
auto Interface::manifest() -> string {
return cartridge.manifest();
auto Interface::display() -> Display {
Display display;
display.type = Display::Type::CRT;
display.colors = 1 << 9;
display.width = 256;
display.height = 240;
display.internalWidth = 256;
display.internalHeight = 240;
display.aspectCorrection = 8.0 / 7.0;
return display;
}
auto Interface::title() -> string {
return cartridge.title();
}
auto Interface::videoInformation() -> VideoInformation {
VideoInformation vi;
vi.width = 256;
vi.height = 240;
vi.internalWidth = 256;
vi.internalHeight = 240;
vi.aspectCorrection = 8.0 / 7.0;
vi.refreshRate = system.frequency() / (ppu.vlines() * ppu.rate() * 341.0);
return vi;
}
auto Interface::videoColors() -> uint32 {
return 1 << 9;
}
auto Interface::videoColor(uint32 n) -> uint64 {
auto Interface::color(uint32 n) -> uint64 {
double saturation = 2.0;
double hue = 0.0;
double contrast = 1.0;
double brightness = 1.0;
double gamma = settings.colorEmulation ? 1.8 : 2.2;
int color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
int color = (n & 0x0f), level = color < 0xe ? int(n >> 4 & 3) : 1;
static const double black = 0.518, white = 1.962, attenuation = 0.746;
static const double levels[8] = {
@@ -114,11 +80,19 @@ auto Interface::loaded() -> bool {
return system.loaded();
}
auto Interface::sha256() -> string {
return cartridge.sha256();
auto Interface::hashes() -> vector<string> {
return {cartridge.hash()};
}
auto Interface::load(uint id) -> bool {
auto Interface::manifests() -> vector<string> {
return {cartridge.manifest()};
}
auto Interface::titles() -> vector<string> {
return {cartridge.title()};
}
auto Interface::load() -> bool {
return system.load(this);
}
@@ -131,13 +105,68 @@ auto Interface::unload() -> void {
system.unload();
}
auto Interface::ports() -> vector<Port> { return {
{ID::Port::Controller1, "Controller Port 1"},
{ID::Port::Controller2, "Controller Port 2"},
{ID::Port::Expansion, "Expansion Port" }};
}
auto Interface::devices(uint port) -> vector<Device> {
if(port == ID::Port::Controller1) return {
{ID::Device::None, "None" },
{ID::Device::Gamepad, "Gamepad"}
};
if(port == ID::Port::Controller2) return {
{ID::Device::None, "None" },
{ID::Device::Gamepad, "Gamepad"}
};
if(port == ID::Port::Expansion) return {
{ID::Device::None, "None"}
};
return {};
}
auto Interface::inputs(uint device) -> vector<Input> {
using Type = Input::Type;
if(device == ID::Device::None) return {
};
if(device == ID::Device::Gamepad) return {
{Type::Hat, "Up" },
{Type::Hat, "Down" },
{Type::Hat, "Left" },
{Type::Hat, "Right" },
{Type::Button, "B" },
{Type::Button, "A" },
{Type::Control, "Select"},
{Type::Control, "Start" }
};
return {};
}
auto Interface::connected(uint port) -> uint {
if(port == ID::Port::Controller1) return settings.controllerPort1;
if(port == ID::Port::Controller2) return settings.controllerPort2;
if(port == ID::Port::Expansion) return settings.expansionPort;
return 0;
}
auto Interface::connect(uint port, uint device) -> void {
if(port == ID::Port::Controller1) controllerPort1.connect(settings.controllerPort1 = device);
if(port == ID::Port::Controller2) controllerPort2.connect(settings.controllerPort2 = device);
}
auto Interface::power() -> void {
system.power();
system.power(/* reset = */ false);
}
auto Interface::reset() -> void {
system.power(/* reset = */ true);
}
auto Interface::run() -> void {
@@ -153,7 +182,7 @@ auto Interface::unserialize(serializer& s) -> bool {
return system.unserialize(s);
}
auto Interface::cheatSet(const string_vector& list) -> void {
auto Interface::cheats(const vector<string>& list) -> void {
cheat.assign(list);
}
@@ -172,7 +201,7 @@ auto Interface::get(const string& name) -> any {
auto Interface::set(const string& name, const any& value) -> bool {
if(name == "Color Emulation" && value.is<bool>()) {
settings.colorEmulation = value.get<bool>();
system.configureVideoPalette();
Emulator::video.setPalette();
return true;
}
if(name == "Scanline Emulation" && value.is<bool>()) return settings.scanlineEmulation = value.get<bool>(), true;

View File

@@ -1,3 +1,5 @@
#if defined(CORE_FC)
namespace Famicom {
struct ID {
@@ -19,31 +21,33 @@ struct ID {
};
struct Interface : Emulator::Interface {
using Emulator::Interface::load;
auto information() -> Information override;
Interface();
auto manifest() -> string override;
auto title() -> string override;
auto videoInformation() -> VideoInformation override;
auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;
auto sha256() -> string override;
auto load(uint id) -> bool override;
auto hashes() -> vector<string> override;
auto manifests() -> vector<string> override;
auto titles() -> vector<string> override;
auto load() -> bool override;
auto save() -> void override;
auto unload() -> void override;
auto ports() -> vector<Port> override;
auto devices(uint port) -> vector<Device> override;
auto inputs(uint device) -> vector<Input> override;
auto connected(uint port) -> uint override;
auto connect(uint port, uint device) -> void override;
auto power() -> void override;
auto reset() -> void override;
auto run() -> void override;
auto serialize() -> serializer override;
auto unserialize(serializer&) -> bool override;
auto cheatSet(const string_vector&) -> void override;
auto cheats(const vector<string>&) -> void override;
auto cap(const string& name) -> bool override;
auto get(const string& name) -> any override;
@@ -54,11 +58,13 @@ struct Settings {
bool colorEmulation = true;
bool scanlineEmulation = true;
uint controllerPort1 = 0;
uint controllerPort2 = 0;
uint expansionPort = 0;
uint controllerPort1 = ID::Device::Gamepad;
uint controllerPort2 = ID::Device::Gamepad;
uint expansionPort = ID::Device::None;
};
extern Settings settings;
}
#endif

View File

@@ -54,18 +54,19 @@ auto PPU::refresh() -> void {
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
}
auto PPU::power() -> void {
auto PPU::power(bool reset) -> void {
create(PPU::Enter, system.frequency());
memory::fill(&io, sizeof(IO));
memory::fill(&latch, sizeof(Latches));
io.vramIncrement = 1;
io = {};
latch = {};
for(auto& n : ciram ) n = 0;
for(auto& n : cgram ) n = 0;
for(auto& n : oam ) n = 0;
if(!reset) {
for(auto& data : ciram ) data = 0;
for(auto& data : cgram ) data = 0;
for(auto& data : oam ) data = 0;
}
for(auto& n : buffer) n = 0;
for(auto& data : buffer) data = 0;
}
}

View File

@@ -11,7 +11,7 @@ struct PPU : Thread {
auto frame() -> void;
auto refresh() -> void;
auto power() -> void;
auto power(bool reset) -> void;
//memory.cpp
auto readCIRAM(uint11 addr) -> uint8;
@@ -39,13 +39,14 @@ struct PPU : Thread {
uint8 mdr;
uint1 field;
uint lx;
uint ly;
uint lx = 0;
uint ly = 0;
uint8 busData;
union {
uint value;
union Union {
auto& operator=(const Union& u) { value = u.value; return *this; }
uint value = 0;
NaturalBitField<uint, 0, 4> tileX;
NaturalBitField<uint, 5, 9> tileY;
NaturalBitField<uint,10,11> nametable;
@@ -59,28 +60,28 @@ struct PPU : Thread {
NaturalBitField<uint,16,18> fineX;
} v, t;
bool nmiHold;
bool nmiFlag;
bool nmiHold = 0;
bool nmiFlag = 0;
//$2000
uint vramIncrement;
uint spriteAddress;
uint bgAddress;
uint spriteHeight;
bool masterSelect;
bool nmiEnable;
uint vramIncrement = 1;
uint spriteAddress = 0;
uint bgAddress = 0;
uint spriteHeight = 0;
bool masterSelect = 0;
bool nmiEnable = 0;
//$2001
bool grayscale;
bool bgEdgeEnable;
bool spriteEdgeEnable;
bool bgEnable;
bool spriteEnable;
bool grayscale = 0;
bool bgEdgeEnable = 0;
bool spriteEdgeEnable = 0;
bool bgEnable = 0;
bool spriteEnable = 0;
uint3 emphasis;
//$2002
bool spriteOverflow;
bool spriteZeroHit;
bool spriteOverflow = 0;
bool spriteZeroHit = 0;
//$2003
uint8 oamAddress;
@@ -106,8 +107,8 @@ struct PPU : Thread {
uint16 tiledataLo;
uint16 tiledataHi;
uint oamIterator;
uint oamCounter;
uint oamIterator = 0;
uint oamCounter = 0;
OAM oam[8]; //primary
OAM soam[8]; //secondary

View File

@@ -3,14 +3,11 @@ auto System::serialize() -> serializer {
uint signature = 0x31545342;
char version[16] = {0};
char hash[64] = {0};
char description[512] = {0};
memory::copy(&version, (const char*)Emulator::SerializerVersion, Emulator::SerializerVersion.size());
memory::copy(&hash, (const char*)cartridge.sha256(), 64);
s.integer(signature);
s.array(version);
s.array(hash);
s.array(description);
serializeAll(s);
@@ -20,18 +17,16 @@ auto System::serialize() -> serializer {
auto System::unserialize(serializer& s) -> bool {
uint signature;
char version[16];
char hash[64];
char description[512];
s.integer(signature);
s.array(version);
s.array(hash);
s.array(description);
if(signature != 0x31545342) return false;
if(string{version} != Emulator::SerializerVersion) return false;
power();
power(/* reset = */ false);
serializeAll(s);
return true;
}
@@ -54,12 +49,10 @@ auto System::serializeInit() -> void {
uint signature = 0;
char version[16];
char hash[64];
char description[512];
s.integer(signature);
s.array(version);
s.array(hash);
s.array(description);
serializeAll(s);

View File

@@ -2,7 +2,6 @@
namespace Famicom {
#include "video.cpp"
#include "serialization.cpp"
System system;
Scheduler scheduler;
@@ -62,20 +61,17 @@ auto System::unload() -> void {
information.loaded = false;
}
auto System::power() -> void {
Emulator::video.reset();
Emulator::video.setInterface(interface);
configureVideoPalette();
configureVideoEffects();
auto System::power(bool reset) -> void {
Emulator::video.reset(interface);
Emulator::video.setPalette();
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
Emulator::audio.reset(interface);
scheduler.reset();
cartridge.power();
cpu.power();
apu.power();
ppu.power();
cpu.power(reset);
apu.power(reset);
ppu.power(reset);
scheduler.primary(cpu);
controllerPort1.power(ID::Port::Controller1);

View File

@@ -11,15 +11,11 @@ struct System {
auto load(Emulator::Interface*) -> bool;
auto save() -> void;
auto unload() -> void;
auto power() -> void;
auto power(bool reset) -> void;
auto init() -> void;
auto term() -> void;
//video.cpp
auto configureVideoPalette() -> void;
auto configureVideoEffects() -> void;
//serialization.cpp
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;

View File

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

View File

@@ -1,13 +1,13 @@
processors += lr35902
processors += sm83
objects += gb-interface gb-system
objects += gb-memory gb-cartridge
objects += gb-cpu gb-ppu gb-apu
obj/gb-interface.o: gb/interface/interface.cpp $(call rwildcard,gb/interface/)
obj/gb-system.o: gb/system/system.cpp $(call rwildcard,gb/system/)
obj/gb-cartridge.o: gb/cartridge/cartridge.cpp $(call rwildcard,gb/cartridge/)
obj/gb-memory.o: gb/memory/memory.cpp $(call rwildcard,gb/memory/)
obj/gb-cpu.o: gb/cpu/cpu.cpp $(call rwildcard,gb/cpu/)
obj/gb-ppu.o: gb/ppu/ppu.cpp $(call rwildcard,gb/ppu/)
obj/gb-apu.o: gb/apu/apu.cpp $(call rwildcard,gb/apu/)
obj/gb-interface.o: gb/interface/interface.cpp
obj/gb-system.o: gb/system/system.cpp
obj/gb-cartridge.o: gb/cartridge/cartridge.cpp
obj/gb-memory.o: gb/memory/memory.cpp
obj/gb-cpu.o: gb/cpu/cpu.cpp
obj/gb-ppu.o: gb/ppu/ppu.cpp
obj/gb-apu.o: gb/apu/apu.cpp

View File

@@ -55,8 +55,8 @@ auto APU::power() -> void {
create(Enter, 2 * 1024 * 1024);
if(!Model::SuperGameBoy()) {
stream = Emulator::audio.createStream(2, frequency());
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
stream->addHighPassFilter(20.0, Emulator::Filter::Order::First);
stream->addDCRemovalFilter();
}
for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
@@ -68,8 +68,8 @@ auto APU::power() -> void {
phase = 0;
cycle = 0;
LinearFeedbackShiftRegisterGenerator r;
for(auto& n : wave.pattern) n = r();
PRNG::PCG prng;
for(auto& n : wave.pattern) n = prng.random();
}
auto APU::readIO(uint16 addr) -> uint8 {

View File

@@ -17,24 +17,43 @@ Cartridge cartridge;
#include "tama/tama.cpp"
#include "serialization.cpp"
auto Cartridge::Enter() -> void {
while(true) scheduler.synchronize(), cartridge.main();
}
auto Cartridge::main() -> void {
mapper->main();
}
auto Cartridge::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(cpu);
}
auto Cartridge::load() -> bool {
information = {};
rom = {};
ram = {};
rtc = {};
mapper = &mbc0;
accelerometer = false;
rumble = false;
if(Model::GameBoy()) {
if(auto loaded = platform->load(ID::GameBoy, "Game Boy", "gb")) {
information.pathID = loaded.pathID();
information.pathID = loaded.pathID;
} else return false;
}
if(Model::GameBoyColor()) {
if(auto loaded = platform->load(ID::GameBoyColor, "Game Boy Color", "gbc")) {
information.pathID = loaded.pathID();
information.pathID = loaded.pathID;
} else return false;
}
if(Model::SuperGameBoy()) {
if(auto loaded = platform->load(ID::SuperGameBoy, "Game Boy", "gb")) {
information.pathID = loaded.pathID();
information.pathID = loaded.pathID;
} else return false;
}
@@ -43,10 +62,9 @@ auto Cartridge::load() -> bool {
} else return false;
auto document = BML::unserialize(information.manifest);
auto board = document["board"];
information.title = document["information/title"].text();
information.title = document["game/label"].text();
auto mapperID = document["board/mapper"].text();
auto mapperID = document["game/board"].text();
if(mapperID == "MBC0" ) mapper = &mbc0;
if(mapperID == "MBC1" ) mapper = &mbc1;
if(mapperID == "MBC1M") mapper = &mbc1m;
@@ -59,53 +77,63 @@ auto Cartridge::load() -> bool {
if(mapperID == "HuC1" ) mapper = &huc1;
if(mapperID == "HuC3" ) mapper = &huc3;
if(mapperID == "TAMA" ) mapper = &tama;
if(!mapper) mapper = &mbc0;
accelerometer = (bool)document["board/accelerometer"];
rumble = (bool)document["board/rumble"];
accelerometer = (bool)document["game/board/accelerometer"];
rumble = (bool)document["game/board/rumble"];
rom.size = max(0x4000, document["board/rom/size"].natural());
rom.data = (uint8*)memory::allocate(rom.size, 0xff);
if(auto name = document["board/rom/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) {
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=ROM,content=Program)"]}) {
rom.size = max(0x4000, (uint)memory.size);
rom.data = memory::allocate<uint8>(rom.size, 0xff);
if(auto fp = platform->open(pathID(), memory.name(), File::Read, File::Required)) {
fp->read(rom.data, min(rom.size, fp->size()));
}
}
ram.size = document["board/ram/size"].natural();
ram.data = (uint8*)memory::allocate(ram.size, 0xff);
if(auto name = document["board/ram/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
fp->read(ram.data, min(ram.size, fp->size()));
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
ram.size = memory.size;
ram.data = memory::allocate<uint8>(ram.size, 0xff);
if(memory.nonVolatile) {
if(auto fp = platform->open(pathID(), memory.name(), File::Read, File::Optional)) {
fp->read(ram.data, min(ram.size, fp->size()));
}
}
}
rtc.size = document["board/rtc/size"].natural();
rtc.data = (uint8*)memory::allocate(rtc.size, 0xff);
if(auto name = document["board/rtc/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
fp->read(rtc.data, min(rtc.size, fp->size()));
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RTC,content=Time)"]}) {
rtc.size = memory.size;
rtc.data = memory::allocate<uint8>(rtc.size, 0xff);
if(memory.nonVolatile) {
if(auto fp = platform->open(pathID(), memory.name(), File::Read, File::Optional)) {
fp->read(rtc.data, min(rtc.size, fp->size()));
}
}
}
information.sha256 = Hash::SHA256(rom.data, rom.size).digest();
information.sha256 = Hash::SHA256({rom.data, rom.size}).digest();
mapper->load(document);
return true;
}
auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest);
if(auto name = document["board/ram/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Write)) {
fp->write(ram.data, ram.size);
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
if(memory.nonVolatile) {
if(auto fp = platform->open(pathID(), memory.name(), File::Write)) {
fp->write(ram.data, ram.size);
}
}
}
if(auto name = document["board/rtc/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Write)) {
fp->write(rtc.data, rtc.size);
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RTC,content=Time)"]}) {
if(memory.nonVolatile) {
if(auto fp = platform->open(pathID(), memory.name(), File::Write)) {
fp->write(rtc.data, rtc.size);
}
}
}
mapper->save(document);
}
auto Cartridge::unload() -> void {
@@ -142,6 +170,8 @@ auto Cartridge::writeIO(uint16 addr, uint8 data) -> void {
}
auto Cartridge::power() -> void {
create(Enter, 4 * 1024 * 1024);
for(uint n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this;
for(uint n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this;
bus.mmio[0xff50] = this;
@@ -167,4 +197,10 @@ auto Cartridge::Memory::write(uint address, uint8 byte) -> void {
data[address] = byte;
}
//
auto Cartridge::Mapper::main() -> void {
cartridge.step(cartridge.frequency());
}
}

View File

@@ -1,9 +1,10 @@
struct Cartridge : MMIO {
struct Cartridge : Thread, MMIO {
auto pathID() const -> uint { return information.pathID; }
auto sha256() const -> string { return information.sha256; }
auto hash() const -> string { return information.sha256; }
auto manifest() const -> string { return information.manifest; }
auto title() const -> string { return information.title; }
static auto Enter() -> void;
auto load() -> bool;
auto save() -> void;
auto unload() -> void;
@@ -11,6 +12,8 @@ struct Cartridge : MMIO {
auto readIO(uint16 address) -> uint8;
auto writeIO(uint16 address, uint8 data) -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto power() -> void;
auto second() -> void;
@@ -35,6 +38,9 @@ struct Cartridge : MMIO {
private:
struct Mapper {
virtual auto load(Markup::Node document) -> void {}
virtual auto save(Markup::Node document) -> void {}
virtual auto main() -> void;
virtual auto second() -> void {}
virtual auto read(uint16 address) -> uint8 = 0;
virtual auto write(uint16 address, uint8 data) -> void = 0;

View File

@@ -32,7 +32,7 @@ auto Cartridge::MBC5::write(uint16 address, uint8 data) -> void {
}
if((address & 0xe000) == 0x4000) { //$4000-5fff
if(cartridge.rumble) platform->inputRumble(ID::Port::Hardware, ID::Device::Controls, 10, data.bit(3));
if(cartridge.rumble) platform->inputRumble(ID::Port::Cartridge, ID::Device::MBC5, 0, data.bit(3));
io.ram.bank = data.bits(0,3);
return;
}

View File

@@ -0,0 +1,239 @@
//Microchip 93LCx6
// 93LC46 => 1024 cells => 128 x 8-bit or 64 x 16-bit
// 93LC56 => 2048 cells => 256 x 8-bit or 128 x 16-bit
// 93LC66 => 4096 cells => 512 x 8-bit or 256 x 16-bit
auto Cartridge::MBC7::EEPROM::load(Markup::Node document) -> void {
for(auto& byte : data) byte = 0xff;
size = 512; //EEPROM size is in bytes
width = 16; //16-bit configuration
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=EEPROM,content=Save)"]}) {
if(memory.size == 128) size = 128;
if(memory.size == 256) size = 256;
if(memory.size == 512) size = 512;
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read, File::Optional)) {
fp->read(data, min(fp->size(), sizeof(data)));
}
}
//note: the 93LC56 alone has an extra dummy address bit
if(size == 128) input.addressLength = width == 16 ? 6 : 7; //93LC46
if(size == 256) input.addressLength = width == 16 ? 8 : 9; //93LC56
if(size == 512) input.addressLength = width == 16 ? 8 : 9; //93LC66
input.dataLength = width;
}
auto Cartridge::MBC7::EEPROM::save(Markup::Node document) -> void {
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=EEPROM,content=Save)"]}) {
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Write)) {
fp->write(data, size);
}
}
}
auto Cartridge::MBC7::EEPROM::main() -> void {
//step by approximately one millisecond
cartridge.step(cartridge.frequency() / 1000);
//set during programming commands
if(busy) busy--;
}
auto Cartridge::MBC7::EEPROM::power() -> void {
select = 0;
clock = 0;
writable = 0;
busy = 0;
input.flush();
output.flush();
}
auto Cartridge::MBC7::EEPROM::readIO() -> uint8 {
uint8 data = 0b00'1111'00;
data.bit(7) = select;
data.bit(6) = clock;
data.bit(1) = input.edge();
if(!select) {
data.bit(0) = 1; //high-z when the chip is idle (not selected)
} else if(busy) {
data.bit(0) = 0; //low when a programming command is in progress
} else if(output.count) {
data.bit(0) = output.edge(); //shift register data during read commands
} else {
data.bit(0) = 1; //high-z during all other commands
}
return data;
}
auto Cartridge::MBC7::EEPROM::writeIO(uint8 data) -> void {
//chip enters idle state on falling CS edge
if(select && !data.bit(7)) return power();
//chip leaves idle state on rising CS edge
if(!(select = data.bit(7))) return;
//input shift register clocks on rising edge
if(!clock.raise(data.bit(6))) return;
//read mode
if(output.count && !data.bit(1)) {
if(input.start() && *input.start() == 1) {
if(input.opcode() && *input.opcode() == 0b10) {
output.read();
if(output.count == 0) {
//sequential read mode
input.increment();
read();
}
}
}
return;
}
output.flush();
input.write(data.bit(1));
//wait for start
if(!input.start()) return;
uint start = *input.start();
//start bit must be set
if(start != 1) return input.flush();
//wait for opcode
if(!input.opcode()) return;
uint opcode = *input.opcode();
//wait for address
if(!input.address()) return;
if(opcode == 0b00) {
auto mode = *input.mode();
if(mode == 0b00) return writeDisable();
if(mode == 0b01) return writeAll();
if(mode == 0b10) return eraseAll();
if(mode == 0b11) return writeEnable();
}
if(opcode == 0b01) return write();
if(opcode == 0b10) return read();
if(opcode == 0b11) return erase();
}
//
auto Cartridge::MBC7::EEPROM::read() -> void {
uint address = *input.address() << (width == 16) & size - 1;
output.flush();
for(uint4 index : range(width)) {
output.write(data[address + !index.bit(3)].bit(index.bits(0,2)));
}
output.write(0); //reads have an extra dummy data bit
}
auto Cartridge::MBC7::EEPROM::write() -> void {
if(!input.data()) return; //wait for data
if(!writable) return input.flush();
uint address = *input.address() << (width == 16) & size - 1;
for(uint4 index : range(width)) {
data[address + !index.bit(3)].bit(index.bits(0,2)) = input.read();
}
busy = 4; //milliseconds
return input.flush();
}
auto Cartridge::MBC7::EEPROM::erase() -> void {
if(!writable) return input.flush();
uint address = *input.address() << (width == 16) & size - 1;
for(uint index : range(width)) {
data[address + index / 8].bit(index % 8) = 1;
}
busy = 4; //milliseconds
return input.flush();
}
auto Cartridge::MBC7::EEPROM::writeAll() -> void {
if(!input.data()) return; //wait for data
if(!writable) return input.flush();
auto word = *input.data();
for(uint address = 0; address < 512;) {
data[address++] = word.byte(width == 16);
data[address++] = word.byte(0);
}
busy = 16; //milliseconds
return input.flush();
}
auto Cartridge::MBC7::EEPROM::eraseAll() -> void {
if(!writable) return input.flush();
for(auto& byte : data) byte = 0xff;
busy = 8; //milliseconds
return input.flush();
}
auto Cartridge::MBC7::EEPROM::writeEnable() -> void {
writable = true;
return input.flush();
}
auto Cartridge::MBC7::EEPROM::writeDisable() -> void {
writable = false;
return input.flush();
}
//
auto Cartridge::MBC7::EEPROM::ShiftRegister::flush() -> void {
value = 0;
count = 0;
}
auto Cartridge::MBC7::EEPROM::ShiftRegister::edge() -> uint1 {
return value.bit(0);
}
auto Cartridge::MBC7::EEPROM::ShiftRegister::read() -> uint1 {
uint1 bit = value.bit(0);
value >>= 1;
count--;
return bit;
}
auto Cartridge::MBC7::EEPROM::ShiftRegister::write(uint1 bit) -> void {
value <<= 1;
value.bit(0) = bit;
count++;
}
//
auto Cartridge::MBC7::EEPROM::InputShiftRegister::start() -> maybe<uint1> {
if(count < 1) return {};
return {value >> count - 1 & 1};
}
auto Cartridge::MBC7::EEPROM::InputShiftRegister::opcode() -> maybe<uint2> {
if(count < 1 + 2) return {};
return {value >> count - 3 & 3};
}
auto Cartridge::MBC7::EEPROM::InputShiftRegister::mode() -> maybe<uint2> {
if(count < 1 + 2 + addressLength) return {};
return {value >> count - 5 & 3};
}
auto Cartridge::MBC7::EEPROM::InputShiftRegister::address() -> maybe<uint9> {
if(count < 1 + 2 + addressLength) return {};
return {value >> count - (3 + addressLength) & (1 << addressLength) - 1};
}
auto Cartridge::MBC7::EEPROM::InputShiftRegister::data() -> maybe<uint16> {
if(count < 1 + 2 + addressLength + dataLength) return {};
return {value >> count - (3 + addressLength + dataLength) & (1 << dataLength) - 1};
}
auto Cartridge::MBC7::EEPROM::InputShiftRegister::increment() -> void {
value.bits(0, addressLength - 1)++;
}

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