Compare commits

...

395 Commits
v096 ... v105

Author SHA1 Message Date
Tim Allen
f8e71b50d0 Update to v105 release.
byuu says:

This release provides several major improvements to Mega Drive emulation
which enhances compatibility a good deal. It also includes important
Super Famicom mosaic emulation improvements, plus a much-needed SuperFX
save state issue fix.

Changelog (since v104):

  - higan: many improvements to Emulator::Interface to support
    forks/frontends
  - higan: refreshed program icon
  - icarus: new program icon
  - Game Boy Advance: slight emulation speedup over v104
  - Game Boy Advance: synchronize APU FIFO updates better
  - Mega Drive: added automatic region detection [hex_usr]
  - Mega Drive: support 8-bit SRAM
  - Game Boy Advance: fixed bug when changing to THUMB mode via MSR
    [MerryMage]
  - Master System: fix bug in backdrop color and background 0 priority
    [hex_usr]
  - Mega Drive: backgrounds always update output priority bit [Cydrak]
  - Mega Drive: emulated interlaced video output
  - Mega Drive: emulated shadow/highlight mode [Cydrak]
  - Super Famicom: auto joypad polling clears the shift register when
    starting
  - Super Famicom: added new low-entropy RAM initialization mode to more
    closely match hardware
  - Game Boy Advance: rumble will now time out after being left on for
    500ms
  - ruby: improved rumble support in udev input driver [ma_rysia]
  - M68K: `move.b (a7)[+/-]` adjust a7 by two
  - M68K: illegal/lineA/lineF opcodes do not modify the stack register
  - Mega Drive: emulate VIP status bit
  - uPD7725: improved emulation of OV1/S1 flags [byuu, AWJ, Lord
    Nightmare]
  - uPD7725: improved handling of DP, RP updates [Jonas Quinn]
  - Super Famicom: improved emulation of mosaic effects in hires,
    interlace, and offset-per-tile modes [byuu, Cydrak]
  - ruby: improved Direct3D exclusive mode monitor selection [Cydrak]
  - Super Famicom: fixed save state bug affecting SuperFX games
    [Cydrak]
  - Mega Drive: added workaround for Clang compiler bug; allowing this
    core to work on macOS [Cydrak, Sintendo]
  - higan: hotkeys now also trigger when the main window lacks focus yet
    higan is set to allow input on focus loss
  - higan: fixed an edge case where `int16_t` ↔ `double` audio
    conversion could possibly result in overflows
  - higan: fixed a crash on macOS when choosing quit from the
    application menu [ncbncb]

Changelog (since the previous WIP):

  - higan: restored `make console=true`
  - tomoko: if you allow input when main window focus is lost, hotkeys
    can now be triggered without focus as well
  - hiro/cocoa: fix crash on exit from menu [ncbncb]
  - ruby: smarter `double` → `int16_t` conversion to prevent
    underflow/overflow
2017-10-07 19:49:07 +11:00
Tim Allen
9a13863adb Update to v104r17 release.
byuu says:

Changelog:

  - processor/m68k: fix error in disassembler [Sintendo]
  - processor/m68k: work around Clang compiler bug [Cydrak, Sintendo]

This is one of the shortest WIPs I've done, but I'm trying not to change
anything before v105.
2017-10-05 17:13:03 +11:00
Tim Allen
5dbaec85a7 Update to v104r16 release.
byuu says:

Changelog:

  - processor/upd96050: always potentially update S1 on ALU ops, sans NOP
      - theory by Lord Nightmare. I'm impartial on this one, but may as
        well match his design
  - sfc: fixed save state hang [reported by FitzRoy; fixed by Cydrak]
  - icarus: do not save settings.bml file when in library mode
2017-10-02 19:04:28 +11:00
Tim Allen
92d86aef16 Remind myself what I need to do for a higan release. 2017-09-29 21:27:55 +10:00
Tim Allen
6524a7181d Update to v104r15 release.
byuu says:

Changelog:

  - processor/huc6280,mos6502,wdc65816: replaced abbreviated opcode
    names with descriptive names
  - nall: replaced `PLATFORM_MACOSX` define with `PLATFORM_MACOS`
  - icarus: added `Icarus::missing() -> string_vector` to list missing
    appended firmware files by name
  - ruby, hiro: fix macosx→macos references

The processor instruction renaming was really about consistency with the
other processor cores. I may still need to do this for one or two more
processors.

The icarus change should allow a future release of the icarus
application to import games with external SNES coprocessor firmware once
again. It will also allow this to be possible when used in library mode.
2017-09-29 20:36:35 +10:00
Tim Allen
fbc58c70ae Update to v104r14 release.
byuu says:

Changelog:

  - Emulator::Interface::videoResolution() -\> VideoResolution renamed
    to videoInformation() -\> VideoInformation
  - added double VideoInformation::refreshRate
  - higan: added `binary := (application|library)` — set this to
    `library` to produce a dynamic link library
  - higan: removed `-march=native` for macOS application builds; and for
    all library builds
  - higan: removed `console` build flag; uncomment  `link += -mwindows`
    instead
  - nall/GNUmakefile: `macosx` platform renamed `macos`
      - still need to do this for nall/intrinsics.hpp
  - Game Gear: return region=NTSC as the only option, so that the system
    frequency is always set correctly
  - hiro/cocoa: fixed typo [Sintendo]
  - hiro/Windows: removed GetDpiForMonitor, as it's Windows 8+ only; DPI
    is no longer per-monitor aware
  - icarus: core Icarus class now has virtual functions for
    directory::create, <file::exists>, <file::copy>, <file::write>
  - icarus: Sufami Turbo can import save RAM files now
  - icarus: setting `ICARUS_LIBRARY` define will compile icarus without
    main(), GUI components
  - ruby/video/Direct3D: choose the current monitor instead of top-left
    monitor for fullscreen exclusive [Cydrak]
  - ruby/video/Direct3D: do not set `WS_EX_TOPMOST` on fullscreen
    exclusive window [Cydrak]
      - this isn't necessary for exclusive mode, and it just makes
        getting out of the application more difficult
2017-09-24 11:01:48 +10:00
Tim Allen
c63e6f2953 Apparently icarus requires dashes in PF94 game ROM names. 2017-09-12 10:45:15 +10:00
Tim Allen
b899ee9a9c Document the command-line interface for games with cartridge slots. 2017-09-09 18:32:11 +10:00
Tim Allen
28fc75737e Add directions for manually importing PowerFest '94.
Special thanks to hex_usr for figuring out the details:
https://board.byuu.org/viewtopic.php?p=46289#p46289
2017-09-09 16:50:33 +10:00
Tim Allen
1ff315838e Update to v104r13 release.
byuu says:

Changelog:

  - nall/GNUmakefile: build=release changed to -O2, build=optimize is
    now -O3
  - hiro: added Monitor::dpi(uint index) → Position [returns logical
    DPI for x, y]
      - Position is a bad name, but dpi(monitor).(x,y)() make more sense
        than .(width,height)()
  - hiro: Position, Size, Geometry, Font changed from using signed int
    to float
  - hiro: Alignment changed from using double to float
  - hiro: added skeleton (unused) Application::scale(), setScale()
    functions

Errata:

  - hiro/cocoa's Monitor::dpi() is untested. Probably will cause issues
    with macOS' automatic scaling.
  - hiro/gtk lacks a way to get both per-monitor and per-axis (x,y) DPI
    scaling
  - hiro/qt lacks a way to get per-monitor DPI scaling (Qt 5.x has this,
    but I still use Qt 4.x)
      - and just to get global DPI, hiro/qt's DPI retrieval has to use
        undocumented functions ... fun

The goal with this WIP was basically to prepare hiro for potential
automatic scaling. It'll be extremely difficult, but I'm convinced that
it must be possible if macOS can do it.

By moving from signed integers to floats for coordinates, we can now
scale and unscale without losing precision. That of course isn't the
hard part, though. The hard part is where and how to do the scaling. In
the ideal application, hiro/core and hiro/extension will handle 100% of
this, and the per-platform hiro/(cocoa,gtk,qt,windows) will not be aware
of what's going on, but ... to even make that possible, things will need
to change in every per-platform core, eg the per-platform code will have
to call a core function to change geometry, which will know about the
scaling and unscale the values back down again.

Gonna be a lot of work, but ... it's a start.
2017-09-08 16:06:21 +10:00
Tim Allen
2e4cd09800 Document the new Credits menu item. 2017-09-06 12:41:32 +10:00
Tim Allen
4fb8ce2821 Update to v104r12 release.
byuu says:

Changelog:

  - higan: URLs updated to HTTPS
  - sfc/ppu/background: use hires/interlace/mosaic-adjusted X/Y
    coordinates for offset-per-tile mode
  - sfc/ppu/background: hires mosaic seems to advance pixel counter on
    subscreen pixels
  - tomoko: added “Help→Credits” menu option (currently the page does
    not exist; should before v105)
  - tomoko: reduced volume slider from {0% - 500%} to {0% - 200%}.
    Distortion is too intense above 200%.
      - technically, I've encountered distortion at 200% as well in
        Prince of Persia for the SNES
  - nall/run/invoke: use program path for working directory
      - allows you to choose “Library→Import ROMs” from a different
        directory on the command-line

I don't know how to assign credit for the mosaic stuff. It's been a
work-in-progress with me, Cydrak, and hex_usr.

The current design should be correct, but very unpleasant. The code
desperately needs to be refactored, but my recent attempt at doing so
ended in spectacular failure.
2017-09-06 12:38:00 +10:00
Tim Allen
3dce3aa3c8 Update to v104r11 release.
byuu says:

Changelog:

  - sfc/ppu/background: minor code cleanup and simplification
  - sfc/ppu/background: $2106 MOSAIC register was implemented
    incorrectly
  - sfc/ppu/background: fixed mosaic effects in hires mode (temporary
    fix)
  - sfc/ppu/background: fixed mosaic effects in interlace mode [Cydrak]

Errata:

  - sfc/ppu/background/background.cpp:48: should be
    `if(!mosaic.enable) {`

Turns out there is only one mosaic size, and the other four bits are
per-BG mosaic enable. This matters a lot for hires/interlace, as
mosaicSize=0 (2x2) is not the same thing as mosaicEnable=false (1x1).

Although I've now implemented this, I really don't like how my mosaic
implementation works right now. I tried to redesign the entire system,
and completely failed. So I started over from v104r10 again and instead
went with a more evolutionary improvement for now. I'll keep trying.

Also, the combination of mosaic + offset-per-tile is still sketchy, as
is mode 6 offset-per-tile. I'll get to those in the future as well.
2017-09-05 10:56:52 +10:00
Tim Allen
28060d3a69 Update to v104r10 release.
byuu says:

Changelog:

  - processor/upd96050: per manual errata note, SGN always uses SA1;
    never SB1 [fixes v104r09 regression]
  - processor/upd96050: new OV1/S1 calculation that doesn't require OV0
    history buffer [AWJ]
  - processor/upd96050: do not update DP in OP if DST=4 [Jonas Quinn]
  - processor/upd96050: do not update RP in OP if DST=5 [Jonas Quinn]
  - resource: recreated higan+icarus icons, higan logo as 32-bit PNGs

So higan v104r08 and earlier were 930KiB for the source tarball. After
creating new higan and icarus icons, the size jumped to 1090KiB, which
was insane for only adding one additional icon.

After digging into why, I discovered that ImageMagick defaults to
64-bit!! (16-bits per channel) PNG images when converting from SVG.
You know, for all those 16-bit per channel monitors that don't exist.
Sigh. Amazingly, nobody ever noticed this.

The logo went from 78.8KiB to 24.5KiB, which in turn also means the
generated resource.cpp shrank dramatically.

The old higan icon was 32-bit PNG, because it was created before I
installed FreeBSD and switched to ImageMagick. But the new higan icon,
plus the new icarus icon, were both 64-bit as well. And they're now
32-bit.

So the new tarball size, thanks to the logo optimization, dropped to
830KiB.

Cydrak had some really interesting results in converting higan's
resources to 8-bit palletized PNGs with the tRNS extension for alpha
transparency. It reduces the file sizes even more without much visual
fidelity loss. Eg the higan logo uses 778 colors currently, and 256
represents nearly all of it very well to the human eye. It's based off
of only two colors, the rest are all anti-aliasing. Unfortunately,
nall/image doesn't support this yet, and I didn't want to flatten the
higan logo to not have transparency, in case I ever want to change the
about screen background color.
2017-09-01 21:21:06 +10:00
Tim Allen
9dcbd12159 Don't let gitlab cache .o files.
higan's makefiles don't always model dependencies properly, so caching
.o files just invites stale .o files.
2017-09-01 16:05:14 +10:00
Tim Allen
5352c5ab27 Update to v104r09 release.
byuu says:

Changelog:

  - processor/upd96050: SGN should select between (A,B).S1 flag using
    ASL opcode bit
  - processor/upd96050: use a temporary to cache new S1, then compute
    OV1 using old S1, then assign new S1
  - processor/upd96050: add SR.(siack,soack) and connect to relevant
    jump instructions (serial not implemented)
  - processor/upd96050: initialize SR properly in power() [r08
    regression]
  - icarus: improve Makefile rules [Screwtape]
  - higan: new program icon
  - icarus: new program icon
2017-08-31 23:58:54 +10:00
Tim Allen
b47488ab09 As of higan v104r07, higan no longer imports separate firmware files.
icarus now requires games and firmware to be combined into a single file,
so we must describe how to achieve that.
2017-08-31 17:05:47 +10:00
Tim Allen
96c45420d1 Use the en_AU spelling of "synchronise".
I've generally tried to keep to en_AU spellings rather than en_US spellings
for the sake of my own sanity, but it's difficult when documenting a program
that exclusively uses en_US spellings since I'm quoting the text on
menu-items and in config files.
2017-08-31 15:50:34 +10:00
Tim Allen
fd9194e4c2 On Linux, install higan before icarus.
Strictly speaking, there's no dependency between them, but higan's
install target happens to create the destination bin directory if it
doesn't already exist, so we want to run that install script first.
2017-08-31 15:34:57 +10:00
Tim Allen
593d32b885 Many typos and grammar fixes from dan1982 on the higan forums. 2017-08-31 15:19:43 +10:00
Tim Allen
fb31df6eb8 Windows changed their not-found error message apparently. 2017-08-31 15:12:36 +10:00
Tim Allen
a81802422f No-Intro changed their naming conventions.
Sufami Turbo games are in a separate set now, they don't have the "(ST)"
marker in the filename.

Satellaview memory paks may have "(BS)", "(BS SoundLink)" or "(BSROM)"
for various reasons that aren't important to higan, so let's just not mention
them at all.
2017-08-31 14:57:24 +10:00
Tim Allen
a1eaec493a Italicise more game names.
Also, it turns out the canonical spelling is "Mega Man", not "Megaman".
2017-08-31 14:56:46 +10:00
Tim Allen
2956930d1c Standardise on "SHA256". 2017-08-31 14:51:48 +10:00
Tim Allen
c557d68ec4 Spell-check the documentation.
Changes include a few typos, a few capitalization changes, and a lot of hyphenation of compound words that were not as widely used as I thought.
2017-08-31 14:48:52 +10:00
Tim Allen
6b8c003ff8 Sufami Turbo carts are not memory paks! 2017-08-30 14:12:44 +10:00
Tim Allen
25bda4f159 Update to v104r08 release.
byuu says:

Changelog:

  - processor/upd96050: code cleanups
  - processor/upd96050: improved emulation of S1/OV1 flags [thanks to
    Cydrak, Lord Nightmare]
  - tomoko/settings/audio: reduced the size of the frequency/latency
    combo boxes to show longer device driver names

Errata: I need to clear regs.sr in uPD96050::power()

Note: the S1/OV1 emulation is likely not 100% correct yet, but it's a
step in the right direction. No SNES games actually use S1/OV1, so this
shouldn't result in any issues, I'd just like to have this part of the
chip emulated correctly.
2017-08-30 13:44:51 +10:00
Tim Allen
d8a8f06c35 Finally finished reviewing and polishing all the text! 2017-08-29 17:22:11 +10:00
Tim Allen
fab830f84b byuu mentioned Ballz 3D requires DSP1B. 2017-08-29 13:50:52 +10:00
Tim Allen
f669c424c4 higan v104r07 changes the expected filenames for the SGB boot ROM. 2017-08-29 13:49:44 +10:00
Tim Allen
9c25f128f9 Update to v104r07 release.
byuu says:

Changelog:

  - md/vdp: added VIP bit to status register; fixes Cliffhanger
  - processor/m68k/disassembler: added modes 7 and 8 to LEA address
    disassembly
  - processor/m68k/disassembler: enhanced ILLEGAL to display LINEA/LINEF
    $xxx variants
  - processor/m68k: ILLEGAL/LINEA/LINEF do not modify the stack
    register; fixes Caeser no Yabou II
  - icarus/sfc: request sgb1.boot.rom and sgb2.boot.rom separately; as
    they are different
  - icarus/sfc: removed support for external firmware when loading ROM
    images

The hack to run Mega Drive Ballz 3D isn't in place, as I don't know if
it's correct, and the graphics were corrupted anyway.

The SGB boot ROM change is going to require updating the icarus database
as well. I will add that in when I start dumping more cartridges here
soon.

Finally ... I explained this already, but I'll do so here as well: I
removed icarus' support for loading SNES coprocessor firmware games with
external firmware files (eg dsp1.program.rom + dsp1.data.rom in the same
path as supermariokart.sfc, for example.)

I realize most are going to see this as an antagonizing/stubborn move
given the recent No-Intro discussion, and I won't deny that said thread
is why this came to the forefront of my mind. But on my word, I honestly
believe this was an ineffective solution for many reasons not related to
our disagreements:

 1. No-Intro distributes SNES coprocessor firmware as a merged file, eg
    "DSP1 (World).zip/DSP1 (World).bin" -- icarus can't possibly know
    about every ROM distribution set's naming conventions for firmware.
    (Right now, it appears GoodSNES and NSRT are mostly dead; but there
    may be more DATs in the future -- including my own.)
 2. Even if the user obtains the firmware and tries to rename it, it
    won't work: icarus parses manifests generated by the heuristics
    module and sees two ROM files: dsp1.program.rom and dsp1.data.rom.
    icarus cannot identify a file named dsp1.rom as containing both
    of these sub-files. Users are going to have to know how to split
    files, which there is no way to do on stock Windows. Merging files,
    however, can be done via `copy /b supermariokart.sfc+dsp1.rom
    supermariokartdsp.sfc`; - and dsp1.rom can be named whatever now.
    I am not saying this will be easy for the average user, but it's
    easier than splitting files.
 3. Separate firmware breaks icarus' database lookup. If you have
    pilotwings.sfc but without firmware, icarus will not find a match
    for it in the database lookup phase. It will then fall back on
    heuristics. The heuristics will pick DSP1B for compatibility with
    Ballz 3D which requires it. And so it will try to pull in the
    wrong firmware, and the game's intro will not work correctly.
    Furthermore, the database information will be unavailable, resulting
    in inaccurate mirroring.

So for these reasons, I have removed said support. You must now load
SNES coprocessor games into higan in one of two ways: 1) game paks with
split files; or 2) SFC images with merged firmware.

If and when No-Intro deploys a method I can actually use, I give you all
my word I will give it a fair shot and if it's reasonable, I'll support
it in icarus.
2017-08-28 22:46:14 +10:00
Tim Allen
c273297577 More cleanups. 2017-08-26 15:17:15 +10:00
Tim Allen
afa8ea61c5 Update to v104r06 release.
byuu says:

Changelog:

  - gba,ws: removed Thread::step() override¹
  - processor/m68k: move.b (a7)+ and move.b (a7)- adjust a7 by two, not
    by one²
  - tomoko: created new initialize(Video,Audio,Input)Driver() functions³
  - ruby/audio: split Audio::information into
    Audio::available(Devices,Frequencies,Latencies,Channels)³
  - ws: added Model::(WonderSwan,WonderSwanColor,SwanCrystal)()
    functions for consistency with other cores

¹: this should hopefully fix GBA Pokemon Pinball. Thanks to
SuperMikeMan for pointing out the underlying cause.

²: this fixes A Ressaha de Ikou, Mega Bomberman, and probably more
games.

³: this is the big change: so there was a problem with WASAPI where
you might change your device under the audio settings panel. And your
new device may not support the frequency that your old device used. This
would end up not updating the frequency, and the pitch would be
distorted.

The old Audio::information() couldn't tell you what frequencies,
latencies, or channels were available for all devices simultaneously, so
I had to split them up. The new initializeAudioDriver() function
validates you have a correct driver, or it defaults to none. Then it
validates a correct device name, or it defaults to the first entry in
the list. Then it validates a correct frequency, or defaults to the
first in the list. Then finally it validates a correct latency, or
defaults to the first in the list.

In this way ... we have a clear path now with no API changes required to
select default devices, frequencies, latencies, channel counts: they
need to be the first items in their respective lists.

So, what we need to do now is go through and for every audio driver that
enumerates devices, we need to make sure the default device gets added
to the top of the list. I'm ... not really sure how to do this with most
drivers, so this is definitely going to take some time.

Also, when you change a device, initializeAudioDriver() is called again,
so if it's a bad device, it will disable the audio driver instead of
continuing to send samples at it and hoping that the driver blocked
those API calls when it failed to initialize properly.

Now then ... since it was a decently-sized API change, it's possible
I've broken compilation of the Linux drivers, so please report any
compilation errors so that I can fix them.
2017-08-26 11:15:49 +10:00
Tim Allen
ea3c2dafda More cleanups. 2017-08-25 18:40:20 +10:00
Tim Allen
b38a657192 Update to v104r05 release.
byuu says:

Changelog:

  - emulator/random: new array function with more realistic RAM
    initializations
  - emulator/random: both low and high entropy register initializations
    now use PCG
  - gba/player: rumble will time out and disable after being left on for
    500ms; fixes Pokemon Pinball issue
  - ruby/input/udev: fixed rumble effects [ma\_rysia]
  - sfc/system: default to low-entropy randomization of memory

The low-entropy memory randomization is modeled after one of my SHVC
2/1/3 systems. It generates striped patterns in memory, using random
inputs (biased to 0x00/0xff), and has a random chance of corrupting 1-2
bits of random values in the pool of memory (to prevent easy emulator
detection and to match observed results on hardware.)

The reasoning for using PCG on register initializations, is that I don't
believe they're going to have repeating patterns like RAM does anyway.
And register initializations are way more vital.

I want to have the new low-entropy RAM mode tested, so at least for the
next few WIPs, I've set the SNES randomization over to low-entropy.
We'll have to have a long discussion and decide whether we want official
releases to use high-entropy or low-entropy.

Also, I figured out the cause of the Prince of Persia distortion ... I
had the volume under the audio settings tab set to 200%. I didn't
realize there were SNES games that clipped so easily, given how
incredibly weak SNES audio is compared to every other sound source on my
PC. So with no entropy or low-entropy, indeed the game now sounds just
fine.

I can't actually test the udev fixes, so I guess we'll see how that goes
for Screwtape and ma\_rysia.
2017-08-25 00:24:34 +10:00
Tim Allen
a8f2bfc533 Another doc cleanup. 2017-08-24 22:13:44 +10:00
Tim Allen
d060904b8d Revert an accidental ruby change.
I made a change to ruby/input/joypad/udev.cpp while diagnosing a problem
with higan's rumble behaviour on Linux, and accidentally committed it
in 15b3dc8b0b as part of a documentation
change.
2017-08-24 22:09:49 +10:00
Tim Allen
56293c585b Clean up the higan Settings docs. 2017-08-24 22:09:03 +10:00
Tim Allen
15b3dc8b0b More cleanups and revision. 2017-08-24 18:34:37 +10:00
Tim Allen
d621136d69 Update to v104r04 release.
byuu says:

Changelog:

  - higan/emulator: added new Random class with three entropy settings:
    none, low, and high
  - md/vdp: corrected Vcounter readout in interlace mode [MoD]
  - sfc: updated core to use the new Random class; defaults to high
    entropy

No entropy essentially returns 0, unless the random.bias(n) function is
called, in which case, it returns n. In this case, n is meant to be the
"logical/ideal" default value that maximizes compatibility with games.

Low entropy is a very simple entropy modeled after RAM initialization
striping patterns (eg 32 0x00s, followed by 32 0xFFs, repeating
throughout.) It doesn't "glitch" like real hardware does on rare
occasions (parts of the pattern being broken from time to time.) It also
only really returns 0 or ~0. So the entropy is indeed extremely low, and
not very useful at all for detecting bugs. Over time, we can try to
improve this, of course.

High entropy is PCG. This replaces the older, lower-entropy and more
predictable, LFSR. PCG should be more than enough for emulator
randomness, while still being quite fast.

Unfortunately, the bad news ... both no entropy and low entropy fix the
Konami logo popping sound in Prince of Persia, but all three entropy
settings still cause the distortion in-game, especially evident at the
title screen. So ... this may be a more serious bug than first
suspected.
2017-08-24 12:45:24 +10:00
Tim Allen
c0934b826c Mention shaders in our install instructions.
If we're going to lug them around, we might as well use them.
2017-08-23 20:55:09 +10:00
Tim Allen
86f3a8e670 Install shaders somewhere that higan will find them. 2017-08-23 20:46:24 +10:00
Tim Allen
b308661fa3 General tidying and tightening of docs. 2017-08-23 17:48:24 +10:00
Tim Allen
a4483339e5 Rename higan-config.md to higan-settings.md
The file was originally named `higan-config.md` because the dialog box
it described was called "higan Configuration", which was because the
menu-item to open it was in a menu called "Settings" and it would have
been weird to have a "Settings" item in a "Settings" menu.

Now there are individual menu-items for each tab in the Settings dialog,
none of which are named Settings, so the dialog has been renamed back to
"Settings", and the filename should match.
2017-08-23 17:05:33 +10:00
Tim Allen
1c13f0ad42 Hint more strongly that Linux distros may already have higan packages 2017-08-23 16:57:17 +10:00
Tim Allen
d416bb7231 *facepalm* 2017-08-23 16:56:51 +10:00
Tim Allen
4ba37e6cbe We're kind of official docs, now. 2017-08-23 16:55:58 +10:00
Tim Allen
6c7a9adaad Consistently use "folder" over "directory", except for Linux documentation.
For Windows documentation, "folder" is the correct term. For generic
documentation, probably either would do but I suspect people are
slightly more likely to be familiar with "folder".
2017-08-23 14:59:16 +10:00
Tim Allen
bafdb09e1b Let's be consistent about quoting console names here. 2017-08-23 14:52:54 +10:00
Tim Allen
53c0002274 Move the link to the Quick Start section as early as practical. 2017-08-23 14:51:38 +10:00
Tim Allen
15c467b482 This documentation is now up to date as of v104. 2017-08-23 14:51:24 +10:00
Tim Allen
bfcd4e376b Remove the mkdocs link to the higan repo.
It shows up as "Edit on Source" in mkdocs, which sounds weird, and it
doesn't show up on readthedocs at all, so let's get rid of it.
2017-08-23 13:43:50 +10:00
Tim Allen
d13f1dd9ea Update to v104r03 release.
byuu says:

Changelog:

  - md/vdp: added full interlace emulation [byuu, Sik, Eke, Mask of
    Destiny]
  - md/vdp: fix an issue with overscan/highlight when setting was
    disabled [hex\_usr]
  - md/vdp: serialize field, and all oam/objects state
  - icarus/md: do not enable RAM unless header 0x1b0-1b1 == "RA"
    [hex\_usr]

I really can't believe how difficult the interlace support was to add. I
must have tried a hundred combinations of adjusting Y, Vscroll, tile
addressing, heights, etc. Many of the changes were a wash that improved
some things, regressed others.

In the end I ended up needing input from three different people to
implement what should have been trivial. I don't know if the Mega Drive
is just that weird, if I've declined that much in skill since the days
when I implemented SNES interlace, or if I've just never been that good.

But either way, I'm disappointed in myself for not being able to figure
either this or shadow/highlight out on my own. Yet I'm extremely
grateful to my friends for helping carry me when I get stuck.

Since it wasn't ever documented before, I'm going to try and document
the changes necessary to implement interlace mode for any future
emudevs.
2017-08-22 19:11:43 +10:00
Tim Allen
8976438118 Fix all the broken links.
Also, some of the text in the higan and icarus settings docs wanted to
link to a discussion of why we ignore manifests by default; now we have
such a thing.
2017-08-22 18:12:17 +10:00
Tim Allen
11357169a5 Update to v104r02 release.
byuu says:

Changelog:

  - md/vdp: backgrounds always update priority bit output [Cydrak]
  - md/vdp: vcounter.d0 becomes vcounter.d8 in interlace mode 3
  - md/vdp: return field number in interlace modes from status register
  - md/vdp: rework scanline/frame counting in main loop so first frame
    won't clock to field 1 instead of field 0
  - md/vdp: add support for shadow/highlight mode; optimize to minimal
    code [Cydrak]
  - md/vdp: update outputPixel() to support interlace modes
  - sfc/cpu: auto joypad polling start should clear the shift registers;
    fixes Nuke (PD)
      - thanks to BMF54123 for this bug report
  - tomoko: if an invalid video/audio/input driver is found in the
    configuration file, it's reset to "None"
      - prevents showing the wrong driver under advanced settings; no
        longer requires possibly two reboots to fix

Note: the Mega Drive interlace mode 1 should be working fully, but I
don't know any games that use it. Interlace mode 3 (Sonic 2's two-player
mode) does not work at all yet, but this is a good start.
2017-08-22 11:09:07 +10:00
Tim Allen
9be4e59a05 Starting to fix broken links in the documentation.
Also, moved the "you should install the GBA BIOS" advice from the
install pages (where I doubt anybody would see it) to the "importing and
playing" guide, which I figure people are more likely to stumble across.
The actually instructions are still in `install/general.md` for lack of
anywhere more appropriate.
2017-08-21 23:29:53 +10:00
Tim Allen
281b22e1c0 Remove leading "What is..." headings from Concepts pages.
It's kind of implicit that an article about the concept of X will start
by describing what X is.
2017-08-21 17:07:49 +10:00
Tim Allen
c9c931ecab Refresh the "game library" documentation. 2017-08-21 17:05:17 +10:00
Tim Allen
366e9cebff Update to v104r01 release.
byuu says:

Changelog:

  - gba/cpu: synchronize to the PPU, not oneself, when the CPU is
    stopped
      - this bug was patched in the official v104 release; but not in
        the .tar.xz archive
  - ms/vdp: backdrop color is on the second 16-entry palette, not the
    first [hex\_usr]
  - ms/vdp: fix background color 0 priority; fixes Alex Kidd in High
    Tech World text boxes [hex\_usr]
  - tomoko: choose first option when loading files via the command-line
    [hex\_usr]
  - icarus: lo/hi RAM addressing was backwards; M68K is big endian;
    fixes save files in Sonic 3

Many thanks to hex\_usr for the Master System / Game Gear VDP fix.
That's a tricky system to get good technical information on. The fix
should be correct, but please report if you spot any regressions just in
case.
2017-08-18 22:48:29 +10:00
Tim Allen
58d70c7c9a Add a link to the bsnes-mercury fork.
Also, sort the list of forks by the version they forked from.
2017-08-18 17:20:21 +10:00
Tim Allen
d0b90d0b5c Break "Manifests and Game Folders" into two articles.
Also, substantially re-work each of them.
2017-08-18 15:18:15 +10:00
Tim Allen
e0512b9100 Mention connecting controllers in the console menu. 2017-08-14 21:43:15 +10:00
Tim Allen
08e1f93f71 Update the rest of the settings docs to match the new Video settings. 2017-08-14 18:12:49 +10:00
Tim Allen
6caad914ad Update the video settings documentation. 2017-08-14 17:51:00 +10:00
Tim Allen
c39ef91307 Fully document the "no audio → too fast" problem. 2017-08-13 00:07:55 +10:00
Tim Allen
f0cf1df4af Document higan's region-selection features. 2017-08-12 23:41:58 +10:00
Tim Allen
0034adab3b Update to v104 public release.
[As mentioned in the v104 internal release notes, byuu fixed a small typo in
the GBA core. -Ed.]

byuu says:

There are lots of improvements in this new release, both to core
emulation and to the user interface. However, some of these changes are
quite substantial, so regressions are a possibility. Please report any
regressions from v103 on the forums if found.

Note that Mega Drive save RAM files will not be compatible with v103,
but will now be compatible with save RAM files from all other Mega Drive
emulators, and the format will be stable going forward.

Also!! Thanks to the tireless work of Screwtape, the
Help->Documentation link in higan now takes you to a very comprehensive
user guide. Please be sure to consult this if you have any questions
about using higan.

Lastly, I've added a link to my Patreon page (https://patreon.com/byuu/)
to the higan downloads page. The money will go exclusively toward
purchasing SNES games for preservation, hardware and flash carts for
reverse engineering, equipment such as backup drives, etc. Donating is
entirely optional and comes with no rewards, but would of course be
greatly appreciated! ^^;

Changelog (since v103):

  - nall/dsp: improved first-order IIR filtering
  - Famicom: improved audio filtering (90hz lowpass + 440hz lowpass +
    14khz highpass)
  - Game Boy Advance: corrected bug in PSG wave channel emulation
    [Cydrak]
  - Mega Drive: added first-order 2.84KHz low-pass filter to match VA6
    model hardware
  - Mega Drive: lowered PSG volume relative to YM2612 to match VA6 model
    hardware
  - Mega Drive: Hblank flag is not always set during Vblank
  - Mega Drive: fix PAL mode reporting from control port reads
  - Famicom: improved phase duty cycle emulation (mode 3 is 25% phase
    inverted; counter decrements)
  - Mega Drive: reset does not cancel 68K bus requests
  - Mega Drive: 68K is not granted bus access on Z80 reset
  - Mega Drive: CTRL port is now read-write, maintains value across
    controller changes
  - Z80: IX, IY override mode can now be serialized in save states
  - 68K: fixed calculations for ABCD, NBCD, SBCD [hex\_usr,
    SuperMikeMan]
  - SPC700: improved all cycle timings to match results observed by
    Overload with a logic anaylzer
  - Super Famicom: SMP uses a separate 4x8-bit buffer for $f4-f7; not
    APU RAM [hex\_usr]
  - Super Famicom: SMP TEST register is now finally 100% fully emulated
    [byuu, AWJ]
  - Game Boy Advance: DMA can run between CPU instruction cycles
  - Game Boy Advance: added 2-cycle delay between DMA activation and
    transfers
  - higan: improved aspect ratio correction accuracy at higher video
    scaling sizes
  - higan: overscan masking will now actually crop the underlying video
    instead of just blanking it
  - Mega Drive: center video when overscan is disabled
  - higan: added increment/decrement quick save slot hotkeys
  - Game Boy Advance: fixed wave RAM nibble ordering (fixes audio in
    Castlevania, Pocket NES)
  - higan: added new adaptive windowed mode: resizes the window to the
    current emulated system's size
  - higan: added new integral scaling mode: resizes the window to fill
    as much of the screen as possible
  - higan: main window is now resizable and will automatically scale
    contents based on user settings
  - higan: fixed one-time blinking of the main window on startup caused
    by focus stealing bug
  - ruby: fixed major memory leak in Direct3D driver
  - ruby: added fullscreen exclusive mode to Direct3D driver
  - Super Famicom: corrected latching behavior of BGnHOFS PPU registers
  - higan: all windows sans the main viewport can be dismissed with the
    escape key now
  - ruby: complete API rewrite; many audio drivers now support device
    selection
  - higan: output frequency can now be modified
  - higan: configuration settings split to individual menu options for
    faster access to individual pages
  - ruby: improved WASAPI driver to event-driven model; more compatible
    in exclusive mode now
  - libco: fix compilation of sjlj and fiber targets [Screwtape]
  - ruby: added YV12 and I420 support to X-Video driver
  - Game Boy: added TAMA emulation (RTC emulation is not working yet)
    [thanks to endrift for notes]
  - Game Boy: correct data ordering of MMM01 ROMs (MMM01 ROMs will need
    to be re-imported into higan)
  - Game Boy: store MBC2 save RAM as 256-bytes instead of 512-bytes (RAM
    is 4-bit; not 8-bit with padding)
  - Game Boy: fixed a bug with RAM serialization in games without a
    battery
  - Mega Drive: fix CRAM reads (fixes Sonic Spinball) [hex\_usr]
  - Game Boy: added rumble support to MBC5 games such as Pokemon Pinball
  - Game Boy: added MBC7 emulation (accelerometer X-axis, EEPROM not
    working yet) [thanks to endrift for notes]
  - hiro: macOS compilation fixes and UI improvements [MerryMage,
    ncbncb]
  - Game Boy: added MBC6 emulation (no phone link or flash support;
    timing bugs in game still)
  - Game Boy: HDMA syncs to other components after each byte transfer
    now
  - Game Boy: disabling the LCD completely halts the PPU (fixes onscreen
    graphical corruption in some games)
  - Mega Drive: added 6-button Fighting Pad emulation [hex\_usr]
  - 68K: TAS sets d7 when EA mode is a direct register (fixes Asterix
    graphical corruption)
  - Game Boy: STAT mode is forced to zero when LCD is disabled (fixes
    Pokemon Pinball)
  - LR35902: complete rewrite
  - icarus: high-DPI is not supported on Windows yet; remove setting for
    consistency with higan window sizes
  - hiro: added full support for high-DPI displays on macOS [ncbncb]
  - ARM7TDMI: complete rewrite
  - Super Famicom: disabled channels during HDMA initialization appear
    to set DoTransfer flag
  - V30MZ: code cleanup
  - Mega Drive: added optional TMSS emulation; disabled by default
    [hex\_usr]
  - ARM7TDMI: pipeline decode stage caches CPSR.T [MerryMage]
  - ARM7TDMI: fixed timing of THUMB stack multiple instruction
    [Cydrak]
  - higan: detect when ruby drivers crash; disable drivers on next
    startup to prevent crash loop
  - Mega Drive: added automatic region detection (favors NTSC-J >
    NTSC-U > PAL) [hex\_usr]
  - Mega Drive: support 8-bit SRAM
  - ARM7TDMI: PC should be incremented by 2 when setting CPSR.T via MSR
    instruction [MerryMage]
  - ruby: add Windows ASIO driver support (does not work on some systems
    due to buggy vendor drivers)
  - higan: default to safe drivers on a new install; due to instability
    with some optimal drivers
2017-08-12 22:40:54 +10:00
Tim Allen
c4425b5fa8 Flesh out the Quick Start documentation. 2017-08-12 22:32:11 +10:00
Tim Allen
722a2d797d Document the rotation feature. 2017-08-12 20:58:02 +10:00
Tim Allen
60345fe8b5 Describe Game Boy rumble support. 2017-08-12 20:58:02 +10:00
Tim Allen
f3af7f177b A brief discussion of GBA save types. 2017-08-12 20:58:02 +10:00
Tim Allen
5f67b2a8fc Finish describing video shader issues. 2017-08-12 20:58:02 +10:00
Tim Allen
1ae228663d GBC save-states don't work in GB mode, quelle surprise. 2017-08-12 20:58:02 +10:00
Tim Allen
59eb05226f Finish the section about Game Boy Color games. 2017-08-12 20:58:02 +10:00
Tim Allen
fe9820481f Start fleshing out the "console-specific notes" section. 2017-08-12 20:58:02 +10:00
Tim Allen
1faa263a4a Address the idea of multithreading. 2017-08-12 20:58:02 +10:00
Tim Allen
45c8f09330 Be more descriptive about higan's overhead. 2017-08-12 20:58:02 +10:00
Tim Allen
09c9a55588 Give some reasons why games might run slow. 2017-08-12 20:58:02 +10:00
Tim Allen
defb1eedd3 Another entry for the FAQ. 2017-08-12 20:58:02 +10:00
Tim Allen
accffa8d1b A tool to check our docs for broken links.
Annoyingly, mkdocs won't validate inter- or intra-document links,
so we'll need to use a third-party tool instead.
2017-08-12 20:58:02 +10:00
Tim Allen
4c4a1dcb67 Fill out the FAQ. 2017-08-12 20:58:02 +10:00
Tim Allen
c52b05a0de More mental notes. 2017-08-12 20:58:02 +10:00
Tim Allen
5af4f45a25 Document higan's video sync/audio sync issues. 2017-08-12 20:58:02 +10:00
Tim Allen
a350195c8c Describe save states. 2017-08-12 20:58:02 +10:00
Tim Allen
55bae99bb6 More notes. 2017-08-12 20:58:02 +10:00
Tim Allen
0ca6aaf6b7 I've been reminded that region-selection is a thing. 2017-08-12 20:58:02 +10:00
Tim Allen
bc9e8c8f4a Document the "no audio → fast forward" behaviour. 2017-08-12 20:58:01 +10:00
Tim Allen
b26db6c9b8 Fix a broken link. 2017-08-12 20:58:01 +10:00
Tim Allen
246887d65c Document which drivers people should use. 2017-08-12 20:58:01 +10:00
Tim Allen
0749009652 Be clearer that some drivers don't support the basic shaders. 2017-08-12 20:58:01 +10:00
Tim Allen
3a967430eb Missing last newline. 2017-08-12 20:58:01 +10:00
Tim Allen
ce8f008894 Document using shaders. 2017-08-12 20:58:01 +10:00
Tim Allen
f6fab1a502 Add higan command-line docs. 2017-08-12 20:58:01 +10:00
Tim Allen
a8363c41ba Fix broken link. 2017-08-12 20:58:01 +10:00
Tim Allen
e888a607c1 Add a "Manifests and Game Folders" document.
I've occasionally wished for a single "what is a manifest" section I
could link to from other places; this is where it will go.
2017-08-12 20:58:01 +10:00
Tim Allen
fb52220b1b Move 'concepts' docs into a concepts folder. 2017-08-12 20:58:01 +10:00
Tim Allen
7760931ffc Link to RTD docs from the README. 2017-08-12 20:58:01 +10:00
Tim Allen
df2492c75e Give the Tools doco an intro, like Settings. 2017-08-12 20:58:01 +10:00
Tim Allen
6e6c29138e Make Settings tabs top-level sections.
Just like the tabs of the Tools window.
2017-08-12 20:58:01 +10:00
Tim Allen
5a44f249a4 Separate "how to import" from "what's the game library".
Changed "Configuration" to "Guides", because we want narrative
documentation for all kinds of things, not just documentation.
2017-08-12 20:58:01 +10:00
Tim Allen
3c3f1de317 Convert README docs to MkDocs format.
The existing documentation was getting *way* too long to be a single
document.

I've just bulk moved the existing content into the new structure, but it
definitely needs another pass to make the prose match, fix hyperlinks,
etc. etc.
2017-08-12 20:58:01 +10:00
Tim Allen
a99e601c87 Describe the rest of the interesting files. 2017-08-12 20:58:01 +10:00
Tim Allen
a62425745b Reminders for the future. 2017-08-12 20:58:01 +10:00
Tim Allen
232bcc0fa7 Fix typos 2017-08-12 20:58:01 +10:00
Tim Allen
926d65cfe0 Describe some files that appear in game folders. 2017-08-12 20:58:01 +10:00
Tim Allen
37a738a1a4 Discuss game patching. 2017-08-12 20:58:01 +10:00
Tim Allen
3e0adb1798 Move the discussion of SD2SNES-optimized MSU-1 patches into a separate paragraph. 2017-08-12 20:58:01 +10:00
Tim Allen
f5333c333c I forgot, higan *does* have some Satellaview emulation.
At least, it lets you choose "Satellaview" from the Expansion Port
menu. I have no idea how complete that is.
2017-08-12 20:58:01 +10:00
Tim Allen
427ffd97b8 Update for v103r17. 2017-08-12 20:58:01 +10:00
Tim Allen
8df735e6e2 Minor typo fixes. 2017-08-12 20:58:01 +10:00
Tim Allen
b7edf2abcf Reflect the menu-structure of 103r16.
Now that we have a Synchronize Video option again, I've added a place
in the FAQ to address why it does not play well with Synchronize Audio.
2017-08-12 20:58:01 +10:00
Tim Allen
50d5beda53 First draft of MSU-1 import docs. 2017-08-12 20:58:01 +10:00
Tim Allen
65dadd1576 Update the "Audio" tab to match 103r15. 2017-08-12 20:58:01 +10:00
Tim Allen
6f9839df62 Begin the MSU-1 section. 2017-08-12 20:58:01 +10:00
Tim Allen
781f1ff4c0 We should talk about patches at some point. 2017-08-12 20:58:01 +10:00
Tim Allen
66ef548781 Link to the BS-X Project page. 2017-08-12 20:58:00 +10:00
Tim Allen
cabb865d5b Fix typo. 2017-08-12 20:58:00 +10:00
Tim Allen
54ffe85479 Document the Super Game Boy. 2017-08-12 20:58:00 +10:00
Tim Allen
362d29c6a7 Make a proper heading for black Game Boy cartridges.
Also, remove the sections for importing and playing Game Boy and GBA
games... the Game Boy section was just going to be about black cartridges
anyway, and I have no idea what the GBA section was going to be for.
2017-08-12 20:58:00 +10:00
Tim Allen
ecbc3f6693 Fix underlining for the Satellaview heading. 2017-08-12 20:58:00 +10:00
Tim Allen
1cdd539115 Document the Sufami Turbo. 2017-08-12 20:58:00 +10:00
Tim Allen
1870788cb8 Reword for clarity. 2017-08-12 20:58:00 +10:00
Tim Allen
1b77a4ec8d Use italics for titles. 2017-08-12 20:58:00 +10:00
Tim Allen
b02c35edae The official name is "Satellaview".
Not "BS-X", and definitely not "Bandai Satellaview".
2017-08-12 20:58:00 +10:00
Tim Allen
e721064fe9 Document how to use BS-X games. 2017-08-12 20:58:00 +10:00
Tim Allen
f1e48b90ff Figured out what happens if you're missing firmware files. 2017-08-12 20:58:00 +10:00
Tim Allen
6099424f0f More table cleanups.
Wrap SHA256 hashes in `<code/>` so they'll all be visually the same length.

Make the references from DSP1 and DSP1B to the notes below more obviously
references, not content.

Reword the DSP1 note so that it makes sense even if you didn't just come
from the DSP1/1A row of the table.
2017-08-12 20:58:00 +10:00
Tim Allen
43c5368a30 Clean up table markup.
Use code, not pre, since we want inline markup, not block-level.

The attribute we want is named "rowspan", not "rows".
2017-08-12 20:58:00 +10:00
Tim Allen
f4bf02ba02 Replace the awkward nested lists of firmware info with a table. 2017-08-12 20:58:00 +10:00
Tim Allen
8c3d076bf4 Link to the official game folder docs. 2017-08-12 20:58:00 +10:00
Tim Allen
788bdb0ae2 Document importing regular and co-processor games. 2017-08-12 20:58:00 +10:00
Tim Allen
39822bc123 A rewording inspired by a forum thread.
Introduce the idea of "the console a game was intended to run on",
so it doesn't come out of left field in the "To play a game" instructions.
2017-08-12 20:58:00 +10:00
Tim Allen
6ba931987b Fix typo. 2017-08-12 20:58:00 +10:00
Tim Allen
4c4339d313 Add pointers to importing hints. 2017-08-12 20:57:59 +10:00
Tim Allen
d166c280c7 Add some TODO markers for completeness. 2017-08-12 20:57:59 +10:00
Tim Allen
73a37beae1 Fix typo. 2017-08-12 20:57:59 +10:00
Tim Allen
b61ca253de Document icarus. 2017-08-12 20:57:59 +10:00
Tim Allen
0ed9737dd4 We should document what goes into a game folder. 2017-08-12 20:57:59 +10:00
Tim Allen
adddc1dc0a We're going to need to document icarus, too. 2017-08-12 20:57:59 +10:00
Tim Allen
b021f5186c Move "Moving the Game Library" into its own section.
Also, move the higher-level questions about the game library
and game folders to the end of the section,
so people can more easily find the bits about importing types of game.
2017-08-12 20:57:59 +10:00
Tim Allen
1a68cb65eb Document higan's custom filesystem browser. 2017-08-12 20:57:59 +10:00
Tim Allen
82a64bb3ab Describe the Manifest Viewer. 2017-08-12 20:57:59 +10:00
Tim Allen
75f842cdb2 At least link to Quick Start near the top of the doc. 2017-08-12 20:57:59 +10:00
Tim Allen
9362726c65 Found some more info about higan and vsync. 2017-08-12 20:57:59 +10:00
Tim Allen
a3e1983d50 Remind myself to update the Video tab docs when it settles. 2017-08-12 20:57:59 +10:00
Tim Allen
20e3fdd1e3 higan 103r11 removed the "Mask Overscan" option. 2017-08-12 20:57:59 +10:00
Tim Allen
0bb6a3b0bc Document the State Manager. 2017-08-12 20:57:59 +10:00
Tim Allen
752139e66c We have a save-state section now, better use it. 2017-08-12 20:57:59 +10:00
Tim Allen
2f08bc3b1c higan v103r10 moved the per-game cheats.bml into the higan folder. 2017-08-12 20:57:59 +10:00
Tim Allen
f4c043ce50 higan v103r10 standardized on "quick state". 2017-08-12 20:57:59 +10:00
Tim Allen
1bbe78f94e higan v103r09 changed the way video scaling works. 2017-08-12 20:57:59 +10:00
Tim Allen
ef5f0e9c21 Correct and expand the Cheat Editor docs. 2017-08-12 20:57:59 +10:00
Tim Allen
83ae1579a0 Add basic info about the cheat editor. 2017-08-12 20:57:59 +10:00
Tim Allen
e0b4fe6419 higan v103r08 updated how quickstates work.
Also, I found some more potential status bar messages.
2017-08-12 20:57:59 +10:00
Tim Allen
efbfe854d2 higan v103r08 changed how Mask Overscan works. 2017-08-12 20:57:59 +10:00
Tim Allen
5f2fbd0d72 Bust "Drivers" into its own section.
Also, add some details about Windows drivers.
2017-08-12 20:57:59 +10:00
Tim Allen
e0c021660a Document the configuration dialog. 2017-08-12 20:57:59 +10:00
Tim Allen
89192cee6c Finish documenting all the menus. 2017-08-12 20:57:59 +10:00
Tim Allen
e8b9b6cd7b The docs are growing large enough we'll need a quickstart at the beginning. 2017-08-12 20:57:59 +10:00
Tim Allen
3efe3cdfbf Make a note to talk about save-states at some point. 2017-08-12 20:57:59 +10:00
Tim Allen
224bbe03f4 Finish documenting the Settings menu. 2017-08-12 20:57:59 +10:00
Tim Allen
aad87da68a Add a warning about installing higan as root. 2017-08-12 20:57:59 +10:00
Tim Allen
444ec756bd "Aspect Correction" doesn't affect full-screen! 2017-08-12 20:57:59 +10:00
Tim Allen
96ebd87db4 Document the Video Shader menu. 2017-08-12 20:57:59 +10:00
Tim Allen
8ef704c16b Standardize on "The X menu allows you to...". 2017-08-12 20:57:58 +10:00
Tim Allen
380130fbd8 Document the $console menu. 2017-08-12 20:57:58 +10:00
Tim Allen
859c5b587b Let's standardise on "The X menu". 2017-08-12 20:57:58 +10:00
Tim Allen
7eceacf78e Remind myself what appears in the status bar. 2017-08-12 20:57:58 +10:00
Tim Allen
b3b3690948 We need to document all the parts of the higan UI. 2017-08-12 20:57:58 +10:00
Tim Allen
331c53c896 Be consistent about console naming like higan is. 2017-08-12 20:57:58 +10:00
Tim Allen
fd2cdc261c Describe how the higan game library works. 2017-08-12 20:57:58 +10:00
Tim Allen
d366c6ff43 Document higan's console naming policy. 2017-08-12 20:57:58 +10:00
Tim Allen
eef90a8f14 How to obtain and install the GBA BIOS. 2017-08-12 20:57:58 +10:00
Tim Allen
00fecd96a4 Remind people to set up the GBA BIOS after installing. 2017-08-12 20:57:58 +10:00
Tim Allen
f23e54f6b1 Refer to directories as "folders" in Windows.
Sure, probably everybody is familiar with either term,
but Windows users are probably more familiar with the term
that shows up all over their UI.
2017-08-12 20:57:58 +10:00
Tim Allen
54e48aabbe Describe how to contribute to higan.
Since higan does not follow the conventional git development model,
we should prominently document how people can contribute.
2017-08-12 20:57:58 +10:00
Tim Allen
0902869032 Fix intra-document links. 2017-08-12 20:57:58 +10:00
Tim Allen
7d1685f7f5 Finish the build, install and uninstall instructions for Linux. 2017-08-12 20:57:58 +10:00
Tim Allen
775be26350 Let's have separate install and uninstall instructions for compiled Windows builds. 2017-08-12 20:57:58 +10:00
Tim Allen
d38893008c Remind me to mention PSG volume for the Mega Drive. 2017-08-12 20:57:58 +10:00
Tim Allen
d250096e48 Fix formatting in the Windows build instructions. 2017-08-12 20:57:58 +10:00
Tim Allen
68d57dc46c Make the Linux build instructions match the Windows ones. 2017-08-12 20:57:58 +10:00
Tim Allen
acbc52ab07 Update the Windows build instructions. 2017-08-12 20:57:58 +10:00
Tim Allen
7406d903e1 Apparently higan writes to %LOCALAPPDATA% in Windows. 2017-08-12 20:57:58 +10:00
Tim Allen
d739e33243 higan requires Windows 7, not XP. 2017-08-12 20:57:58 +10:00
Tim Allen
1b62f18139 first info about building for Linux 2017-08-12 20:57:58 +10:00
Tim Allen
d0d7436d50 Notes about what to cover for control config. 2017-08-12 20:57:58 +10:00
Tim Allen
08fc1f659a Wrote some stuff about installing on Windows.
Let's hope it's vaguely plausible!
2017-08-12 20:57:58 +10:00
Tim Allen
e8b2f22623 WIP 2017-08-12 20:57:58 +10:00
Tim Allen
cec9d91b39 Add internal hyperlinks to other sections 2017-08-12 20:57:58 +10:00
Tim Allen
11a3195e84 Fix typo 2017-08-12 20:57:58 +10:00
Tim Allen
1fb37f087f Record some official and unofficial higan resources. 2017-08-12 20:57:58 +10:00
Tim Allen
8ece9ed16b Remember to talk about GBA saves 2017-08-12 20:57:58 +10:00
Tim Allen
302b369781 Outline of a README file. 2017-08-12 20:57:58 +10:00
Tim Allen
ba384a7c48 Update to v104 release.
byuu says:

Changelog:

  - emulator/interface: removed unused Region struct
  - gba/cpu: optimized CPU::step() as much as I could for a slight
    speedup¹
  - gba/cpu: synchronize the APU better during FIFO updates
  - higan/md, icarus: add automatic region detection; make it the
    default option [hex\_usr]
      - picks NTSC-J if there's more than one match ... eventually, this
        will be a setting
  - higan/md, icarus: support all three combinations of SRAM (8-bit low,
    8-bit high, 16-bit)
  - processor/arm7tdmi: fix bug when changing to THUMB mode via MSR
    [MerryMage]
  - tomoko: redesigned crash detector to only occur once for all three
    ruby drivers
      - this will reduce disk thrashing since the configuration file
        only needs to be written out one extra time
      - technically, it's twice ... but we should've always been writing
        one out on first run in case it crashes then
  - tomoko: defaulted back to the safest ruby drivers, given the optimal
    drivers have some stability concerns

¹: minor errata: spotted a typo saying `synchronize(cpu)` when the CPU
is stopped, instead of `synchronize(ppu)`. This will be fixed in the v104
official 7zip archives.

I'm kind of rushing here but, it's really good timing for me to push out
a new official release. The blocking issues are resolved or close to it,
and we need lots of testing of the new major changes.

I'm going to consider this a semi-stable testing release and leave links
to v103 just in case.
2017-08-12 20:53:13 +10:00
Tim Allen
55f19c3e0d Update to v103r32 release.
byuu says:

Changelog:

  - Master System: merged Bus into CPU
  - Mega Drive: merged BusCPU into CPU; BusAPU into AU
  - Mega Drive: added TMSS emulation; disabled by default [hex\_usr]
      - VDP lockout not yet emulated
  - processor/arm7tdmi: renamed interrupt() to exception()
  - processor/arm7tdmi: CPSR.F (FIQ disable) flag is set on reset
  - processor/arm7tdmi: pipeline decode stage caches CPSR.T (THUMB mode)
    [MerryMage]
      - fixes `msr_tests.gba` test F
  - processor/arm7tdmi/disassembler: add PC address to left of currently
    executing instruction
  - processor/arm7tdmi: stop forcing CPSR.M (mode flags) bit 4 high (I
    don't know what really happens here)
  - processor/arm7tdmi: undefined instructions now generate Undefined
    0x4 exception
  - processor/arm7tdmi: thumbInstructionAddRegister masks PC by &~3
    instead of &~2
      - hopefully this is correct; &~2 felt very wrong
  - processor/arm7tdmi: thumbInstructionStackMultiple can use sequential
    timing for PC/LR PUSH/POP [Cydrak]
  - systems/Mega Drive.sys: added tmss.rom; enable with cpu version=1
  - tomoko: detect when a ruby video/audio/input driver crashes higan;
    disable it on next program startup

v104 blockers:

  - Mega Drive: support 8-bit SRAM (even if we don't support 16-bit;
    don't force 8-bit to 16-bit)
  - Mega Drive: add region detection support to icarus
  - ruby: add default audio device information so certain drivers won't
    default to silence out of the box
2017-08-12 02:02:09 +10:00
Tim Allen
406b6a61a5 Update to v103r31 release.
byuu says:

Changelog:

  - gba/cpu: slight speedup to CPU::step()
  - processor/arm7tdmi: fixed about ten bugs, ST018 and GBA games are
    now playable once again
  - processor/arm: removed core from codebase
  - processor/v30mz: code cleanup (renamed functions; updated
    instruction() for consistency with other cores)

It turns out on my much faster system, the new ARM7TDMI core is very
slightly slower than the old one (by about 2% or so FPS.) But the
CPU::step() improvement basically made it a wash.

So yeah, I'm in really serious trouble with how slow my GBA core is now.
Sigh.

As for higan/processor ... this concludes the first phase of major
cleanups and rewrites.

There will always be work to do, and I have two more phases in mind.

One is that a lot of the instruction disassemblers are very old. One
even uses sprintf still. I'd like to modernize them all. Also, the
ARM7TDMI core (and the ARM core before it) can't really disassemble
because the PC address used for instruction execution is not known prior
to calling instruction(), due to pipeline reload fetches that may occur
inside of said function. I had a nasty hack for debugging the new core,
but I'd like to come up with a clean way to allow tracing the new
ARM7TDMI core.

Another is that I'd still like to rename a lot of instruction function
names in various cores to be more descriptive. I really liked how the
LR35902 core came out there, and would like to get that level of detail
in with the other cores as well.
2017-08-10 21:26:02 +10:00
Tim Allen
1067566834 Update to v103r30 release.
byuu says:

Changelog:

  - processor/arm7tdmi: completed implemented
  - gba/cpu, sfc/coprocessor/armdsp: use arm7tdmi instead of arm
  - sfc/cpu: experimental fix for newly discovered HDMA emulation issue

Notes:

The ARM7TDMI core crashes pretty quickly when trying to run GBA games,
and I'm certain the same will be the case with the ST018. It was never
all that likely I could rewrite 70KiB of code in 20 hours and have it
work perfectly on the first try. So, now it's time for lots and lots of
debugging. Any help would *really* be appreciated, if anyone were up for
comparing the two implementations for regressions =^-^= I often have a
really hard time spotting simple typos that I make.

Also, the SNES HDMA fix is temporary. I would like it if testers could
run through a bunch of games that are known for being tricky with HDMA
(or if these aren't known to said tester, any games are fine then.) If
we can confirm regressions, then we'll know the fix is either incorrect
or incomplete. But if we don't find any, then it's a good sign that
we're on the right path.
2017-08-09 21:11:59 +10:00
Tim Allen
559eeccc89 Update to v103r29 release.
byuu says:

Changelog:

  - processor/arm7tdmi: implementation all nine remaining ARM
    instructions
  - processor/arm7tdmi: implemented five more THUMB instructions
    (sixteen remain)
2017-08-08 21:51:41 +10:00
Tim Allen
a72ff8b7fa Update to v103r28 release.
byuu says:

Changelog:

  - processor/arm7tdmi: implemented 10 of 19 ARM instructions
  - processor/arm7tdmi: implemented 1 of 22 THUMB instructions

Today's WIP was 6 hours of work, and yesterday's was 5 hours.

Half of today was just trying to come up with the design to use a
lambda-based dispatcher to map both instructions and disassembly,
similar to the 68K core. The problem is that the ARM core has 28 unique
bits, which is just far too many bits to have a full lookup table like
the 16-bit 68K core.

The thing I wanted more than anything else was to perform the opcode
bitfield decoding once, and have it decoded for both instructions and
the disassembler. It took three hours to come up with a design that
worked for the ARM half ... relying on #defines being able to pull in
other #defines that were declared and changed later after the first
one. But, I'm happy with it. The decoding is in the table building, as
it is with the 68K core. The decoding does happen at run-time on each
instruction invocation, but it has to be done.

As to the THUMB core, I can create a 64K-entry lambda table to cover all
possible encodings, and ... even though it's a cache killer, I've
decided to go for it, given the outstanding performance it obtained in
the M68K core, as well as considering that THUMB mode is far more common
in GBA games.

As to both cores ... I'm a little torn between two extremes:

On the one hand, I can condense the number of ARM/THUMB instructions
further to eliminate more redundant code. On the other, I can split them
apart to reduce the number of conditional tests needed to execute each
instruction. It's really the disassembler that makes me not want to
split them up further ... as I have to split the disassembler functions
up equally to the instruction functions. But it may be worth it if it's
a speed improvement.
2017-08-07 22:20:35 +10:00
Tim Allen
0b6f1df987 Update to v103r27 release.
byuu says:

Changelog:

  - hiro/windows: set dpiAware=false, fixes icarus window sizes relative
    to higan window sizes
  - higan, icarus, hiro, ruby: add support for high resolution displays
    on macOS [ncbncb]
  - processor/lr35902-legacy: removed
  - processor/arm7tdmi: new processor core started; intended to one day
    be a replacement for processor/arm

It will probably take several WIPs to get the new ARM core up and
running. It's the last processor rewrite. After this, all processor
cores will be up to date with all my current programming conventions.
2017-08-06 23:36:26 +10:00
Tim Allen
020caa546d Update to v103r26 release.
byuu says:

Changelog:

  - processor/lr35902: completed rewrite

I'd appreciate regression testing of the Game Boy and Game Boy Color
emulation between v103r24 and v103r26 (skip r25) if anyone wouldn't
mind.

I fixed up processor/lr35902-legacy to compile and run, so that trace
logs can be created between the two cores to find errors. I'm going to
kill processor/lr35902-legacy with the next WIP release, as well as make
changes to the trace format (add flags externally from AF; much easier
to read them that way), which will make it more difficult to do these
comparisons in the future, hence r26 may prove important later on if we
miss regressions this time.

As for the speed of the new CPU core, not too much to report ... at
least it's not slower :)

    Mega Man II: 212.5 to 214.5fps
    Shiro no Sho: 191.5 to 191.5fps
    Oracle of Ages: 182.5 to 190.5fps
2017-08-06 09:13:26 +10:00
Tim Allen
c2975e6898 Update to v103r25 release.
byuu says:

Changelog:

  - gb/cpu: force STAT mode to 0 when LCD is disabled (fixes Pokemon
    Pinball, etc)
  - gb/ppu: when LCD is disabled, require at least one-frame wait to
    re-enable, display white during this time
      - todo: should step by a scanline at a time: worst-case is an
        extra 99% of a frame to enable again
  - gba/ppu: cache tilemap lookups and attribute parsing
      - it's more accurate because the GBA wouldn't read this for every
        pixel
      - but unfortunately, this didn't provide any speedup at all ...
        sigh
  - ruby/audio/alsa: fixed const issue with free()
  - ruby/video/cgl: removed `glDisable(GL_ALPHA_TEST)` [deprecated]
  - ruby/video/cgl: removed `glEnable(GL_TEXTURE_2D)` [unnecessary as
    we use shaders]
  - processor/lr35902: started rewrite¹

¹: so, the Game Boy and Game Boy Color cores will be completely
broken for at least the next two or three WIPs.

The old LR35902 was complete garbage, written in early 2011. So I'm
rewriting it to provide a massive cleanup and consistency with other
processor cores, especially the Z80 core.

I've got about 85% of the main instructions implemented, and then I have
to do the CB instructions. The CB instructions are easier because
they're mostly just a small number of opcodes in many small variations,
but it'll still be tedious.
2017-08-04 23:05:12 +10:00
Tim Allen
571760c747 Update to v103r24 release.
byuu says:

Changelog:

  - gb/mbc6: mapper is now functional, but Net de Get has some text
    corruption¹
  - gb/mbc7: mapper is now functional²
  - gb/cpu: HDMA syncs other components after each byte transfer now
  - gb/ppu: LY,LX forced to zero when LCDC.d7 is lowered (eg disabled),
    not when it's raised (eg enabled)
  - gb/ppu: the LCD does not run at all when LCDC.d7 is clear³
      - fixes graphical corruption between scene transitions in Legend
        of Zelda - Oracle of Ages
      - thanks to Cydrak, Shonumi, gekkio for their input on the cause
        of this issue
  - md/controller: renamed "Gamepad" to "Control Pad" per official
    terminology
  - md/controller: added "Fighting Pad" (6-button controller) emulation
    [hex\_usr]
  - processor/m68k: fixed TAS to set data.d7 when
    EA.mode==DataRegisterDirect; fixes Asterix
  - hiro/windows: removed carriage returns from mouse.cpp and
    desktop.cpp
  - ruby/audio/alsa: added device driver selection [SuperMikeMan]
  - ruby/audio/ao: set format.matrix=nullptr to prevent a crash on some
    systems [SuperMikeMan]
  - ruby/video/cgl: rename term() to terminate() to fix a crash on macOS
    [Sintendo]

¹: The observation that this mapper split $4000-7fff into two banks
came from MAME's implementation. But their implementation was quite
broken and incomplete, so I didn't actually use any of it. The
observation that this mapper split $a000-bfff into two banks came from
Tauwasser, and I did directly use that information, plus the knowledge
that $0400/$0800 are the RAM bank select registers.

The text corruption is due to a race condition with timing. The game is
transferring font letters via HDMA, but the game code ends up setting
the bank# with the font a bit too late after the HDMA has already
occurred. I'm not sure how to fix this ... as a whole, I assumed my Game
Boy timing was pretty good, but apparently it's not that good.

²: The entire design of this mapper comes from endrift's notes.
endrift gets full credit for higan being able to emulate this mapper.
Note that the accelerometer implementation is still not tested, and
probably won't work right until I tweak the sensitivity a lot.

³: So the fun part of this is ... it breaks the strict 60fps rate of
the Game Boy. This was always inevitable: certain timing conditions can
stretch frames, too. But this is pretty much an absolute deal breaker
for something like Vsync timing. This pretty much requires adaptive sync
to run well without audio stuttering during the transition.

There's currently one very important detail missing: when the LCD is
turned off, presumably the image on the screen fades to white. I do not
know how long this process takes, or how to really go about emulating
it. Right now as an incomplete patch, I'm simply leaving the last
displayed image on the screen until the LCD is turned on again. But I
will have to output white, as well as add code to break out of the
emulation loop periodically when the LCD is left off eg indefinitely, or
bad things would happen. I'll work something out and then implement.

Another detail is I'm not sure how long it takes for the LCD to start
rendering again once enabled. Right now, it's immediate. I've heard it's
as long as 1/60th of a second, but that really seems incredibly
excessive? I'd like to know at least a reasonably well-supported
estimate before I implement that.
2017-08-04 23:05:06 +10:00
Tim Allen
7022d1aa51 Update to v103r23 release.
byuu says:

Changelog:

  - gb: added accelerometer X-axis, Y-Axis inputs¹
  - gb: added rumble input¹
  - gb/mbc5: added rumble support²
  - gb/mbc6: added skeleton driver, but it doesn't boot Net de Get
  - gb/mbc7: added mostly complete driver (only missing EEPROM), but it
    doesn't boot Kirby Tilt 'n' Tumble
  - gb/tama: added leap year assignment
  - tomoko: fixed macOS compilation [MerryMage]
  - hiro/cocoa: fix table cell redrawing on updates and automatic column
    resizing [ncbncb]
  - hiro/cocoa: fix some weird issue with clicking table view checkboxes
    on Retina displays [ncbncb]
  - icarus: enhance Game Boy heuristics³
  - nall: fix three missing return statements [Jonas Quinn]
  - ruby: hopefully fixed all compilation errors reported by Screwtape
    et al⁴

¹: because there's no concept of a controller for cartridge inputs,
I'm attaching to the base platform for now. An idea I had was to make
separate ports for each cartridge type ... but this would duplicate the
rumble input between MBC5 and MBC7. And would also be less discoverable.
But it would be more clean in that users wouldn't think the Game Boy
hardware had this functionality. I'll think about it.

²: it probably won't work yet. Rumble isn't documented anywhere, but
I dug through an emulator named GEST and discovered that it seems to use
bit 3 of the RAM bank select to be rumble. I don't know if it sets the
bit for rumbling, then clears when finished, or if it sets it and then
after a few milliseconds it stops rumbling. I couldn't test on my
FreeBSD box because SDL 1.2 doesn't support rumble, udev doesn't exist
on FreeBSD, and nobody has ever posted any working code for how to use
evdev (or whatever it's called) on FreeBSD.

³: I'm still thinking about specifying the MBC7 RAM as EEPROM, since
it's not really static RAM.

⁴: if possible, please test all drivers if you can. I want to ensure
they're all working. Especially let me know if the following work:
macOS: input.carbon Linux: audio.pulseaudiosimple, audio.ao (libao)

If I can confirm these are working, I'm going to then remove them from
being included with stock higan builds.

I'm also considering dropping SDL video on Linux/BSD. XShm is much
faster and supports blurring. I may also drop SDL input on Linux, since
udev works better. That will free a dependency on SDL 1.2 for building
higan. FreeBSD is still going to need it for joypad support, however.
2017-07-30 23:00:31 +10:00
Tim Allen
e1223366a7 Update to v103r22 release.
byuu says:

Changelog:

  - ruby: ported all remaining drivers to new API¹
  - ruby/wasapi: fix for dropping one sample per period [SuperMikeMan]
  - gb: emulated most of the TAMA RTC; but RTC state is still volatile²

¹: the new ports are:

  - audio/{directsound, alsa, pulseaudio, pulseaudiosimple, ao}
  - input/{udev, quartz, carbon}

It's pretty much guaranteed many of them will have compilation errors.
Please paste the error logs and I'll try to fix them up. It may take a
WIP or two to get there.

It's also possible things broke from the updates. If so, I could use
help comparing the old file to the new file, looking for mistakes, since
I can't test on these platforms apart from audio/directsound.

Please report working drivers in this list, so we can mark them off the
list. I'll need both macOS and Linux testers.

audio/directsound.cpp:112:

    if(DirectSoundCreate(0, &_interface, 0) != DS_OK) return terminate(), false;

²: once I get this working, I'll add load/save support for the RTC
values. For now, the RTC data will be lost when you close the emulator.

Right now, you can set the date/time in real-time mode, and when you
start the game, the time will be correct, and the time will tick
forward. Note that it runs off emulated time instead of actual real
time, so if you fast-forward to 300%, one minute will be 20 seconds.

The really big limitation right now is that when you exit the game, and
restart it, and resume a new game, the hour spot gets corrupted, and
this seems to instantly kill your pet. Fun. This is crazy because the
commands the game sends to the TAMA interface are identical between
starting a new game and getting in-game versus loading a game.

It's likely going to require disassembling the game's code and seeing
what in the hell it's doing, but I am extremely bad at LR35092 assembly.
Hopefully endrift can help here :|
2017-07-28 21:42:24 +10:00
Tim Allen
80841deaa5 Update to v103r21 release.
byuu says:

Changelog:

  - gb: added TAMA emulation [thanks to endrift for the initial notes]
  - gb: save RTC memory to disk (MBC3 doesn't write to said memory yet;
    TAMA doesn't emulate it yet)
  - gb: expect MMM01 boot loader to be at end of ROM instead of start
  - gb: store MBC2 save RAM as 256-bytes (512x4-bit) instead of
    512-bytes (with padding)
  - gb: major cleanups to every cartridge mapper; moved to Mapper class
    instead of MMIO class
  - gb: don't serialize all mapper states with every save state; only
    serialize the active mapper
  - gb: serialize RAM even if a battery isn't present¹
  - gb/cartridge: removed unnecessary code; refactored other code to
    eliminate duplication of functions
  - icarus: improve GB(C) heuristics generation to not include filenames
    for cartridges without battery backup
  - icarus: remove incorrect rearrangement of MMM01 ROM data
  - md/vdp: fix CRAM reads -- fixes Sonic Spinball colors [hex\_usr]
  - tomoko: hide the main higan window when entering fullscreen
    exclusive mode; helps with multi-monitor setups
  - tomoko: destroy ruby drivers before calling Application::quit()
    [Screwtape]
  - libco: add settings.h and defines to fiber, ucontext [Screwtape]

¹: this is one of those crystal clear indications that nobody's
actually playing the higan DMG/CGB cores, or at least not with save
states. This was a major mistake.

Note: I can't find any official documentation that `GL_ALPHA_TEST` was
removed from OpenGL 3.2. Since it's not hurting anything except showing
some warnings in debug mode, I'm just going to leave it there for now.
2017-07-26 22:42:06 +10:00
Tim Allen
d5c09c9ab1 Update to v103r20 release.
byuu says:

Changelog:

  - ruby/audio/xaudio2: ported to new ruby API
  - ruby/video/cgl: ported to new ruby API (untested, won't compile)
  - ruby/video/directdraw: ported to new ruby API
  - ruby/video/gdi: ported to new ruby API
  - ruby/video/glx: ported to new ruby API
  - ruby/video/wgl: ported to new ruby API
  - ruby/video/opengl: code cleanups

The macOS CGL driver is sure to have compilation errors. If someone will
post the compilation error log, I can hopefully fix it in one or two
iterations of WIPs.

I am unable to test the Xorg GLX driver, because my FreeBSD desktop
video card drivers do not support OpenGL 3.2. If the driver doesn't
work, I'm going to need help tracking down what broke from the older
releases.

The real fun is still yet to come ... all the Linux-only drivers, where
I don't have a single Linux machine to test with.

Todo:

  - libco/fiber
  - libco/ucontext (I should really just delete this)
  - tomoko: hide main UI window when in exclusive fullscreen mode
2017-07-24 15:23:40 +10:00
Tim Allen
8be474b0ac Update to v103r19 release.
byuu says:

Changelog:

  - tomoko: Application::onMain assigned at end of Program::Program()
    [Screwtape]¹
  - libco: add `#define _XOPEN_SOURCE 500` to fix compilation of sjlj.c
    [Screwtape]
  - ruby/audio/openal: fixed device driver string list enumeration
  - ruby/audio/wasapi: changing device re-initializes the driver now
  - ruby/audio/wasapi: probably a pointless change, but don't fill the
    buffer beyond the queue size with silence
  - ruby/video/xvideo: renamed from ruby/video/xv
  - ruby/video/xvideo: check to see if `XV_AUTOPAINT_COLORKEY` exists
    before setting it [SuperMikeMan]
  - ruby/video/xvideo: align buffer sizes to be evenly divisible by four
    [SuperMikeMan]
  - ruby/video/xvideo: fail nicely without crashing (hopefully)
  - ruby/video/xvideo: add support for YV12 and I420 12-bit planar YUV
    formats²

¹: prevents crashes when drivers fail to initialize from running the
main loop that polls input drivers before the input driver is
initialized (or fails to initialize itself.) Some drivers still don't
block their main functions when initialization fails, so they will still
crash, but I'll work to fix them.

²: this was a **major** pain in the ass, heh. You only get one chroma
sample for every four luma samples, so the color reproduction is even
worse than UYVY and YUYV (which is two to four chroma to luma.) Further,
the planar format took forever to figure out. Apparently it doesn't care
what portion of the image you specify in XvShmPutImage, it expects you
to use the buffer dimensions to locate the U and V portions of the data.

This is probably the most thorough X-Video driver in existence now.

Notes:

  - forgot to rename the configuration settings dialog window title to
    just "Settings"
2017-07-23 19:18:16 +10:00
Tim Allen
284e4c043e Update to v103r18 release.
byuu says:

Changelog:

  - tomoko: improved handling of changing audio devices on the audio
    settings panel
  - ruby/audio/wasapi: added device enumeration and selection support¹
  - ruby/audio/wasapi: release property store handle from audio device
  - ruby/audio/wasapi: fix exclusive mode buffer filling
  - ruby/video/glx2: ported to new API -- tested and confirmed working
    great²
  - ruby/video/sdl: fixed initialization -- tested and confirmed working
    on FreeBSD now³
  - ruby/video/xv: ported to new API -- tested and mostly working great,
    sans fullscreen mode⁴

Errata:

  - accidentally changed "Driver Settings" label to "Driver" on the
    audio settings tab because I deleted the line and forgot the
    "Settings" part
  - need to use "return initialize();" from setDevice() in the WASAPI
    driver, instead of "return true;", so device selection is currently
    not functioning in this WIP for said driver

¹: for now, this will likely end up selecting the first available
endpoint device, which is probably wrong. I need to come up with a
system to expose good 'default values' when selecting new audio drivers,
or changing audio device settings.

²: glx2 is a fallback driver for system with only OpenGL 2.0 and no
OpenGL 3.2 drivers, such as FreeBSD 10.1 with AMD graphics cards.

³: although I really should track down why InputManager::poll() is
crashing the emulator when Video::ready() returns false ...

⁴: really bizarrely, when entering fullscreen mode, it looks like the
image was a triangle strip, and the bottom right triange is missing, and
the top left triangle skews the entire image into it. I'm suspecting
this is a Radeon driver bug when trying to create a 2560x1600 X-Video
surface. The glitch persists when exiting fullscreen, too.

If anyone can test the X-Video driver on their Linux/BSD system, it'd be
appreciated. If it's just my video card, I'll ignore it. If not,
hopefully someone can find the cause of the issue :|
2017-07-20 21:52:47 +10:00
Tim Allen
0b4e7fb5a5 Update to v103r17 release.
byuu says:

Changelog:

  - tomoko: re-hid the video sync option¹
  - tomoko: removed " Settings" duplication on all the individual
    settings tab options
  - ruby/audio/wasapi: finished port to new syntax; adapted to an
    event-driven model; support 32-bit integral audio²
  - ruby/video/sdl: ported to new syntax; disabled driver on FreeBSD³

¹: still contemplating a synchronize submenu of {none, video, audio},
but ... the fact that video can't work on PAL, WonderSwan games is a
real limitation for it

²: this driver actually received a ton of work. There's also a new
ring-buffer queue, and I added special handling for when exclusive mode
fails because the latency requested is lower than the hardware can
support. It'll pick the closest latency to the minimum that is possible
in this case.

On my Audigy Rx, the results for non-exclusive mode are the same. For
exclusive mode, the framerate drops from 60fps to ~50fps for smaller
buffers, and ~55fps for larger buffers (no matter how big, it never hits
60fps.) This is a lot better than before where it was hitting ~15fps,
but unfortunately it's the best I can do.

The event system used by WASAPI is really stupid. It just uses SetEvent
at some arbitrary time, and you have to query to see how many samples
it's waiting on. This makes it unknowable how many samples we should
buffer before calling `WaitForSingleObject(INFINITE)`, and it's also
unclear how we should handle cases where there's more samples available
than our queue has: either we can fill it with zeroes, or we can write
less samples. The former should prevent audio looping effects when
running too slowly, whereas the latter could potentially be too
ambitious when the audio could've recovered from a minor stall.

It's shocking to me how there's as many ways to send audio to a sound
card as there are sound card APIs, when all that's needed is a simple
double buffer and a callback event from another thread to do it right.
It's also terrifying how unbelievably shitty nearly all sound card
drivers apparently are.

Also, I don't know if cards can output an actual 24-bit mode with three
byte audio samples, or if they always just take 32-bit samples and
ignore the lower 8-bits. Whatever, it's all nonsense for the final
output to be >16-bits anyway (hi, `double[]` input from ruby.)

³: unfortunately, this driver always crashes on FreeBSD (even before
the rewrite), so I'll need someone on Linux to test it and make sure it
actually works. I'll also need testing for a lot of the other drivers as
well, once they're ported over (I don't have X-video, PulseAudio, ALSA,
or udev.)

Note that I forgot to set `_ready=true` at the end of `initialize()`,
and `_ready=false` in `terminate()`, but it shouldn't actually matter
beyond showing you a false warning message on startup about it failing
to initialize.
2017-07-19 23:14:00 +10:00
Tim Allen
f87c6b7ecb Update to v103r16 release.
byuu says:

Changelog:

  - emulator/audio: added the ability to change the output frequency at
    run-time without emulator reset
  - tomoko: display video synchronize option again¹
  - tomoko: Settings→Configuration expanded to Settings→{Video,
    Audio, Input, Hotkey, Advanced} Settings²
  - tomoko: fix default population of audio settings tab
  - ruby: Audio::frequency is a double now (to match both
    Emulator::Audio and ASIO)³
  - tomoko: changing the audio device will repopulate the frequency and
    latency lists
  - tomoko: changing the audio frequency can now be done in real-time
  - ruby/audio/asio: added missing device() information, so devices can
    be changed now
  - ruby/audio/openal: ported to new API; added device selection support
  - ruby/audio/wasapi: ported to new API, but did not test yet (it's
    assuredly still broken)⁴

¹: I'm uneasy about this ... but, I guess if people want to disable
audio and just have smooth scrolling video ... so be it. With
Screwtape's documentation, hopefully that'll help people understand that
video synchronization always breaks audio synchronization. I may change
this to a child menu that lets you pick between {no synchronization,
video synchronization, audio synchronization} as a radio selection.

²: given how much more useful the video and audio tabs are now, I
felt that four extra menu items were worth saving a click and going
right to the tab you want. This also matches the behavior of the Tools
menu displaying all tool options and taking you directly to each tab.
This is kind of a hard change to get used to ... but I think it's for
the better.

³: kind of stupid because I've never seen a hardware sound card where
floor(frequency) != frequency, but whatever. Yay consistency.

⁴: I'm going to move it to be event-driven, and try to support 24-bit
sample formats if possible. Who knows which cards that'll fix and which
cards that'll break. I may end up making multiple WASAPI drivers so
people can find one that actually works for them. We'll see.
2017-07-17 20:32:36 +10:00
Tim Allen
4129630d97 Update to v103r15 release.
byuu says:

Changelog:

  - ruby: rewrote the API interfaces for Video, Audio, Input
  - ruby/audio: can now select the number of output channels (not useful
    to higan, sorry)
  - ruby/asio: various improvements
  - tomoko: audio settings panel can now select separate audio devices
    (for ASIO, OSS so far)
  - tomoko: audio settings panel frequency and latency lists are
    dynamically populated now

Note: due to the ruby API rewrite, most drivers will not compile. Right
now, the following work:

  - video: Direct3D, XShm
  - audio: ASIO, OSS
  - input: Windows, SDL, Xlib

It takes a really long time to rewrite these (six hours to do the
above), so it's going to be a while before we're back at 100%
functionality again.

Errata:

  - ASIO needs device(), setDevice()
  - need to call setDevice() at program startup to populate
    frequency/latency settings properly
  - changing the device and/or frequency needs to update the emulator
    resampler rates

The really hard part is going to be the last one: the only way to change
the emulator frequency is to flush all the audio streams and then
recompute all the coefficients for the resamplers. If this is called
during emulation, all audio streams will be erased and thus no sound
will be output. I'll most likely be forced to simply ignore
device/frequency changes until the user loads another game. It is at
least possible to toggle the latency dynamically.
2017-07-17 15:11:18 +10:00
Tim Allen
17697317d4 Update to v103r14 release.
byuu says:

Changelog:

  - tomoko: by popular choice, default to adaptive mode on new installs
  - hiro/windows: fix bug that was preventing the escape key from
    closing some dialog windows
  - nall/registry: use "\\\\" as separator instead of "/" ... because
    some registry keys contain "/" in them >_>
  - ruby: add ASIO driver stub (so far it can only initialize and grab
    the driver name/version information)
2017-07-15 22:00:20 +10:00
Tim Allen
ed5ec58595 Update to v103r13 release.
byuu says:

Changelog:

  - gb/interface: fix Game Boy Color extension to be "gbc" and not "gb"
    [hex\_usr]
  - ms/interface: move Master System hardware controls below controller
    ports
  - sfc/ppu: improve latching behavior of BGnHOFS registers (not
    hardware verified) [AWJ]
  - tomoko/input: rework port/device mapping to support non-sequential
    ports and devices¹
      - todo: should add move() to inputDevice.mappings.append and
        inputPort.devices.append
      - note: there's a weird GCC 4.9 bug with brace initialization of
        InputEmulator; have to assign each field separately
  - tomoko: all windows sans the main presentation window can be
    dismissed with the escape key
  - icarus: the single file selection dialog ("Load ROM Image...") can
    be dismissed with the escape key
  - tomoko: do not pause emulation when FocusLoss/Pause is set during
    exclusive fullscreen mode
  - hiro/(windows,gtk,qt): implemented Window::setDismissable() function
    (missing from cocoa port, sorry)
  - nall/string: fixed printing of largest possible negative numbers (eg
    `INT_MIN`) [Sintendo]
      - only took eight months! :D

¹: When I tried to move the Master System hardware port below the
controller ports, I ran into a world of pain.

The input settings list expects every item in the
`InputEmulator<InputPort<InputDevice<InputMapping>>>>` arrays to be
populated with valid results. But these would be sparsely populated
based on the port and device IDs from inside higan. And that is done so
that the Interface::inputPoll can have O(1) lookup of ports and devices.
This worked because all the port and device IDs were sequential (they
left no gaps in the maps upon creating the lists.)

Unfortunately by changing the expectation of port ID to how it appears
in the list, inputs would not poll correctly. By leaving them alone and
just moving Hardware to the third position, the Game Gear would be
missing port IDs of 0 and 1 (the controller ports of the Master System).
Even by trying to make separate MasterSystemHardware and
GameGearHardware ports, things still fractured when the devices were no
longer contigious.

I got pretty sick of this and just decided to give up on O(1)
port/device lookup, and moved to O(n) lookup. It only knocked the
framerate down by maybe one frame per second, enough to be in the margin
of error. Inputs aren't polled *that* often for loops that usually
terminate after 1-2 cycles to be too detrimental to performance.

So the new input system now allows non-sequential port and device IDs.

Remember that I killed input IDs a while back. There's never any reason
for those to need IDs ... it was easier to just order the inputs in the
order you want to see them in the user interface. So the input lookup is
still O(1). Only now, everything's safer and I return a
maybe<InputMapping&>, and won't crash out the program trying to use a
mapping that isn't found for some reason.

Errata: the escape key isn't working on the browser/message dialogs on
Windows, because of course nothing can ever just be easy and work for
me. If anyone else wouldn't mind looking into that, I'd greatly
appreciate it.

Having the `WM_KEYDOWN` test inside the main `Application_sharedProc`, it
seems to not respond to the escape key on modal dialogs. If I put the
`WM_KEYDOWN` test in the main window proc, then it doesn't seem to get
called for `VK_ESCAPE` at all, and doesn't get called period for modal
windows. So I'm at a loss and it's past 4AM here >_>
2017-07-12 18:24:27 +10:00
Tim Allen
434e303ffb Update to v103r12 release.
byuu says:

Changelog:

  - ruby/video: cleaned up Direct3D9 driver and fixed catastrophic
    memory leak
  - ruby/video: added fullscreen exclusive mode support to the Direct3D9
    driver¹
  - ruby/video: minor cosmetic code cleanups to various drivers
  - tomoko: added support to always allow input when in fullscreen
    exclusive mode
  - tomoko: fixed window to not remove resizability flag when exiting
    fullscreen mode

¹: I am assuming that exclusive mode will try to capture the primary
monitor. I don't know what will happen in multi-monitor setups, however,
as I don't use such a setup here.

Also, I am using `D3DPRESENT_DISCARD` instead of `D3DPRESENT_FLIP`. I'm
not sure if this will prove better or worse, but I've heard it will
waste less memory, and having a BackBufferCount of 1 should still result
in page flipping anyway. The difference is supposedly just that you
can't rely on the back buffer being a valid copy of the previous frame
like you can with FLIP.

Lastly, if you want Vsync, you can edit the configuration file to enable
that, and then turn off audio sync.

Errata: "pause emulation when focus is lost" is not working with
exclusive mode. I need to add a check to never auto-pause when in
exclusive mode. Thanks to bun for catching that one.
2017-07-09 12:23:17 +10:00
Tim Allen
ee982f098a Update to v103r11 release.
byuu says:

Changelog:

  - tomoko: removed "Settings→Video Emulation→Overscan Mask" setting¹
  - tomoko: remove a few unnecessary calls to resizeViewport on startup
  - tomoko: only resize main window from video settings when in adaptive
    or toggling adaptive mode²
  - hiro/windows: add `SWP_NOACTIVATE` flag to prevent focus stealing on
    resizing invisible windows³
  - hiro/windows: suppress spurious API-generated `onSize()` callback
    when calling `setVisible()`

¹: it just seemed like bad design to default to overscan masking
being disabled with overscan masks of 8 horizontal, 8 vertical out of
the box. Users would adjust the sliders and not see anything happening.
Instead, I've set the default masks to zero. If you want to turn off
overscan masking, simply slide those to zero again.

²: I figure the only way we're going to be able to fairly evaluate
Screwtape's suggestion is to try it both ways. And I will admit, I kind
of like the way this works as well ... a lot more so than I thought I
would, so I think it was a great suggestion. Still, now's the time if
people have strong opinions on this. Be sure to try both r10 and r11 to
compare. Barring no other feedback, I'm going to keep it this way.

³: this fixes the blinking of the main window on startup.

Screwtape, thanks again for the improvement suggestions. At this point
though, I am not using a tiling window manager. If you are able to patch
hiro/gtk and/or hiro/qt (I mostly use GTK) to work with tiling window
managers better, I wouldn't mind applying said patches, so long as they
don't break things on my own Xfce desktop with xfwm4.

Also, I noticed one issue with Xfce ... if the window is maximized and I
try to call `Window::setSize()`, it's not actually removing the maximize
flag. We'll need to look into how to add that to GTK, but I don't think
it's a huge issue. A similar glitch happens on windows where the icon
still reflects being maximized, but it does actually shrink, it just
sticks to the top left corner of the screen. So this isn't really a
critical bug, but would be extra polish.
2017-07-08 11:02:01 +10:00
Tim Allen
cbbf5ec114 Update to v103r10 release.
byuu says:

Changelog:

  - tomoko: video scaling options are now resolutions in the
    configuration file, eg "640x480", "960x720", "1280x960"
  - tomoko: main window is now always resizable instead of fixed width
    (also supports maximizing)
  - tomoko: added support for non-integral scaling in windowed mode
  - tomoko: made the quick/managed state messaging more consistent
  - tomoko: hide "Find Codes ..." button from the cheat editor window if
    the cheat database is not present
  - tomoko: per-game cheats.bml file now goes into the higan/ subfolder
    instead of the root folder

So the way the new video system works is you have the following options
on the video settings panel:

Windowed mode: { Aspect correction, Integral scaling, Adaptive }

Fullscreen mode: { Aspect correction, Integral scaling } (and one day,
hopefully Exclusive will be added here)

Whenever you adjust the overscan masking, or you change any of the
windowed or fullscreen mode settings, or you choose a different video
scale from the main menu, or you load a new game, or you unload a game,
or you rotate the display of an emulated system, the resizeViewport
logic will be invoked. This logic will remember the last option you
chose for video scale, and base the new window size on that value as an
upper limit of the new window size.

If you are in windowed mode and have adaptive enabled, it will shrink
the window to fit the contents of the emulated system's video output.
Otherwise, if you are not in integral scaling mode, it will scale the
video as large as possible to fit into the video scaled size you have
selected. Otherwise, it will perform an integral scale and center the
video inside of the viewport.

If you are in fullscreen mode, it's much the same, only there is no
adaptive mode.

A major problem with Xorg is that it's basically impossible to change
the resizability attribute of a window post-creation. You can do it, but
all kinds of crazy issues start popping up. Like if you toggle
fullscreen, then you'll find that the window won't grow past a certain
fairly small size that it's already at, and cannot be shrunk. And the
multipliers will stop expanding the window as large as they should. And
sometimes the UI elements won't be placed in the correct position, or
the video will draw over them. It's a big mess. So I have to keep the
main window always resizable. Also, note that this is not a limitation
of hiro. It's just totally broken in Xorg itself. No amount of fiddling
has ever allowed this to work reliably for me on either GTK+ 2 or Qt 4.

So what this means is ... the adaptive mode window is also resizable.
What happens here is, whenever you drag the corners of the main window
to resize it, or toggle the maximize window button, higan will bypass
the video scale resizing code and instead act as though the adaptive
scaling mode were disabled. So if integral scaling is checked, it'll
begin scaling in integral mode. Otherwise, it'll begin scaling in
non-integral mode.

And because of this flexibility, it no longer made sense for the video
scale menu to be a radio box. I know, it sucks to not see what the
active selection is anymore, but ... say you set the scale to small,
then you accidentally resized the window a little, but want it snapped
back to the proper small resolution dimensions. If it were a radio item,
you couldn't reselect the same option again, because it's already active
and events don't propagate in said case. By turning them into regular
menu options, the video scale menu can be used to restore window sizing.

Errata:

On Windows, the main window blinks a few times on first load. The fix
for that is a safeguard in the video settings code, roughly like so ...
but note you'd need to make a few other changes for this to work against
v103r10:

    auto VideoSettings::updateViewport(bool firstRun) -> void {
      settings["Video/Overscan/Horizontal"].setValue(horizontalMaskSlider.position());
      settings["Video/Overscan/Vertical"].setValue(verticalMaskSlider.position());
      settings["Video/Windowed/AspectCorrection"].setValue(windowedModeAspectCorrection.checked());
      settings["Video/Windowed/IntegralScaling"].setValue(windowedModeIntegralScaling.checked());
      settings["Video/Windowed/AdaptiveSizing"].setValue(windowedModeAdaptiveSizing.checked());
      settings["Video/Fullscreen/AspectCorrection"].setValue(fullscreenModeAspectCorrection.checked());
      settings["Video/Fullscreen/IntegralScaling"].setValue(fullscreenModeIntegralScaling.checked());
      horizontalMaskValue.setText({horizontalMaskSlider.position()});
      verticalMaskValue.setText({verticalMaskSlider.position()});
      if(!firstRun) presentation->resizeViewport();
    }

That'll get it down to one blink, as with v103 official. Not sure I can
eliminate that one extra blink.

I forgot to remove the setResizable toggle on fullscreen mode exit. On
Windows, the main window will end up unresizable after toggling
fullscreen. I missed that one because like I said, toggling resizability
is totally broken on Xorg. You can fix that with the below change:

    auto Presentation::toggleFullScreen() -> void {
      if(!fullScreen()) {
        menuBar.setVisible(false);
        statusBar.setVisible(false);
      //setResizable(true);
        setFullScreen(true);
        if(!input->acquired()) input->acquire();
      } else {
        if(input->acquired()) input->release();
        setFullScreen(false);
      //setResizable(false);
        menuBar.setVisible(true);
        statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean());
      }
      resizeViewport();
    }

Windows is stealing focus on calls to resizeViewport(), so we need to
deal with that somehow ...

I'm not really concerned about the behavior of shrinking the viewport
below the smallest multiplier for a given system. It might make sense to
snap it to the window size and forego all other scaling, but honestly
... meh. I don't really care. Nobody sane is going to play like that.
2017-07-07 13:38:46 +10:00
Tim Allen
7af270aa59 Update to v103r09 release.
byuu says:

Changelog:

  - gba/apu: fixed wave RAM nibble ordering (fixes audio in Castlevania,
    PocketNES)
  - emulator: restructured video information to just a single
    videoResolution() → VideoResolution function
      - returns "projected size" (between 160x144 and 320x240)
      - "internal buffer size" (up to 1280x480)
      - returns aspect correction multiplier that is to be applied to
        the width field
          - the value could be < 1.0 to handle systems with taller
            pixels; although higan doesn't emulate such a system
  - tomoko: all calculations for scaling and overscan masking are done
    by the GUI now
  - tomoko: aspect correction can be enabled in either windowed or
    fullscreen mode separately; moved to Video settings panel
  - tomoko: video scaling multipliers (against 320x240) can now me
    modified from the default (2,3,4) via the configuration file
      - use this as a really barebones way of supporting high DPI
        monitors; although the GUI elements won't scale nicely
      - if you set a value less than two, or greater than your
        resolution divided by 320x240, it's your own fault when things
        blow up. I'm not babysitting anyone with advanced config-file
        only options.
  - tomoko: added new adaptive windowed mode
      - when enabled, the window will shrink to eliminate any black
        borders when loading a game or changing video settings. The
        window will not reposition itself.
  - tomoko: added new adaptive fullscreen mode
      - when enabled, the integral scaling will be disabled for
        fullscreen mode, forcing the video to fill at least one
        direction of the video monitor completely.

I expect we will be bikeshedding for the next month on how to describe
the new video options, where they should appear in the GUI, changes
people want, etc ... but suffice to say, I'm happy with the
functionality, so I don't intend to make changes to -what- things do,
but I will entertain better ways to name things.
2017-07-06 18:29:12 +10:00
Tim Allen
191a71b291 Update to v103r08 release.
byuu says:

Changelog:

  - emulator: improved aspect correction accuracy by using
    floating-point calculations
  - emulator: added videoCrop() function, extended videoSize() to take
    cropping parameters¹
  - tomoko: the overscan masking function will now actually resize the
    viewport²
  - gba/cpu: fixed two-cycle delay on triggering DMAs; not running DMAs
    when the CPU is stopped
  - md/vdp: center video when overscan is disabled
  - pce/vce: resize video output from 1140x240 to 1120x240
  - tomoko: resize window scaling from 326x240 to 320x240
  - tomoko: changed save slot naming and status bar messages to indicate
    quick states vs managed states
  - tomoko: added increment/decrement quick state hotkeys
  - tomoko: save/load quick state hotkeys now save to slots 1-5 instead
    of always to 0
  - tomoko: increased overscan range from 0-16 to 0-24 (in case you want
    to mask the Master System to 240x192)

¹: the idea here was to decouple raw pixels from overscan masking.
Overscan was actually horrifically broken before. The Famicom outputs at
256x240, the Super Famicom at 512x480, and the Mega Drive at 1280x480.
Before, a horizontal overscan mask of 8 would not reduce the Super
Famicom or Mega Drive by nearly as much as the Famicom. WIth the new
videoCrop() function, the internals of pixel size distortions can be
handled by each individual core.

²: furthermore, by taking optional cropping information in
videoSize(), games can scale even larger into the viewport window. So
for example, before the Super Famicom could only scale to 1536x1440. But
by cropping the vertical resolution by 6 (228p effectively, still more
than NTSC can even show), I can now scale to 1792x1596. And wiht aspect
correction, that becomes a perfect 8:7 ratio of 2048x1596, giving me
perfectly crisp pixels without linear interpolation being required.

Errata: for some reason, when I save a new managed state with the SFC
core, the default description is being set to a string of what looks to
be hex numbers. I found the cause ... I'll fix this in the next release.

Note: I'd also like to hide the "find codes..." button if cheats.bml
isn't present, as well as update the SMP TEST register comment from
smp/timing.cpp
2017-07-05 16:39:14 +10:00
Tim Allen
d4876a831f Update to v103r07 release.
byuu says:

Changelog:

  - gba/cpu: massive code cleanup effort
  - gba/cpu: DMA can run in between active instructions¹
  - gba/cpu: added two-cycle startup delay between DMA activation and
    DMA transfers²
  - processor/spc700: BBC, BBC, CBNE cycle 4 is an idle cycle
  - processor/spc700: ADDW, SUBW, MOVW (read) cycle 4 is an idle cycle

¹: unfortunately, this causes yet another performance penalty for the
poor GBA core =( Also, I think I may have missed disabling DMAs while
the CPU is stopped. I'll fix that in the next WIP.

²: I put the waiting counter decrement at the wrong place, so this
doesn't actually work. Needs to be more like
this:

    auto CPU::step(uint clocks) -> void {
      for(auto _ : range(clocks)) {
        for(auto& timer : this->timer) timer.run();
        for(auto& dma : this->dma) if(dma.active && dma.waiting) dma.waiting--;
        context.clock++;
      }
      ...

    auto CPU::DMA::run() -> bool {
      if(cpu.stopped() || !active || waiting) return false;

      transfer();
      if(irq) cpu.irq.flag |= CPU::Interrupt::DMA0 << id;
      if(drq && id == 3) cpu.irq.flag |= CPU::Interrupt::Cartridge;
      return true;
    }

Of course, the real fix will be restructuring how DMA works, so that
it's always running in parallel with the CPU instead of this weird
design where it tries to run all channels in some kind of loop until no
channels are active anymore whenever one channel is activated.

Not really sure how to design that yet, however.
2017-07-05 15:29:27 +10:00
Tim Allen
16f736307e Update to v103r06 release.
byuu says:

Changelog:

  - processor/spc700: restored fetch/load/store/pull/push shorthand
    functions
  - processor/spc700: split functions that tested the algorithm used (`op
    != &SPC700:...`) to separate instructions
      - mostly for code clarity over code size: it was awkward having
        cycle counts change based on a function parameter
  - processor/spc700: implemented Overload's new findings on which
    cycles are truly internal (no bus reads)
  - sfc/smp: TEST register emulation has been vastly improved¹

¹: it turns out that TEST.d4,d5 is the external clock divider (used
when accessing RAM through the DSP), and TEST.d6,d7 is the internal
clock divider (used when accessing IPLROM, IO registers, or during idle
cycles.)

The DSP (24576khz) feeds its clock / 12 through to the SMP (2048khz).
The clock divider setting further divides the clock by 2, 4, 8, or 16.
Since 8 and 16 are not cleanly divislbe by 12, the SMP cycle count
glitches out and seems to take 10 and 2 clocks instead of 8 or 16. This
can on real hardware either cause the SMP to run very slowly, or more
likely, crash the SMP completely until reset.

What's even stranger is the timers aren't affected by this. They still
clock by 2, 4, 8, or 16.

Note that technically I could divide my own clock counters by 24 and
reduce these to {1,2,5,10} and {1,2,4,8}, I instead chose to divide by
12 to better illustrate this hardware issue and better model that the
SMP clock runs at 2048khz and not 1024khz.

Further, note that things aren't 100% perfect yet. This seems to throw
off some tests, such as blargg's `test_timer_speed`. I can't tell how
far off I am because blargg's test tragically doesn't print out fail
values. But you can see the improvements in that higan is now passing
all of Revenant's tests that were obviously completely wrong before.
2017-07-03 17:24:47 +10:00
Tim Allen
40802b0b9f Update to v103r05 release.
byuu says:

Changelog:

  - fc/controller: added ControllerPort class; removed Peripherals class
  - md/controller/gamepad: removed X,Y,Z buttons since this isn't a
    6-button controller
  - ms/controller: added ControllerPort class (not used in Game Gear
    mode); removed Peripherals class
  - pce/controller: added ControllerPort class; removed Peripherals
    class
  - processor/spc700: idle(address) is part of SMP class again, contains
    flag to detect mov (x)+ edge case
  - sfc/controller/super-scope,justifier: use CPU frequency instead of
    hard-coding NTSC frequency
  - sfc/cpu: move 4x8-bit SMP ports to SMP class
  - sfc/smp: move APU RAM to DSP class
  - sfc/smp: improved emulation of TEST registers bits 4-7 [information
    from nocash]
      - d4,d5 is RAM wait states (1,2,5,10)
      - d6,d7 is ROM/IO wait states (1,2,5,10)
  - sfc/smp: code cleanup to new style (order from lowest to highest
    bits; use .bit(s) functions)
  - sfc/smp: $00f8,$00f9 are P4/P5 auxiliary ports; named the registers
    better
2017-07-01 16:15:27 +10:00
Tim Allen
ff3750de4f Update to v103r04 release.
byuu says:

Changelog:

  - fc/apu: $4003,$4007 writes initialize duty counter to 0 instead of 7
  - fc/apu: corrected duty table entries for use with decrementing duty
    counter
  - processor/spc700: emulated the behavior of cycle 3 of (x)+
    instructions to not read I/O registers
      - specifically, this prevents reads from $fd-ff from resetting the
        timers, as observed on real hardware
  - sfc/controller: added ControllerPort class to match Mega Drive
    design
  - sfc/expansion: added ExpansionPort class to match Mega Drive design
  - sfc/system: removed Peripherals class
  - sfc/system: changed `colorburst()` to `cpuFrequency()`; added
    `apuFrequency()`
  - sfc: replaced calls to `system.region == System::Region::*` with
    `Region::*()`
  - sfc/expansion: remove thread from scheduler when device is destroyed
  - sfc/smp: `{read,write}Port` now use a separate 4x8-bit buffer instead
    of underlying APU RAM [hex\_usr]
2017-06-30 14:17:23 +10:00
Tim Allen
78f341489e Update to v103r03 release.
byuu says:

Changelog:

  - md/psg: fixed output frequency rate regression from v103r02
  - processor/m68k: fixed calculations for ABCD, NBCD, SBCD [hex\_usr,
    SuperMikeMan]
  - processor/spc700: renamed abbreviated instructions to functional
    descriptions (eg `XCN` → `ExchangeNibble`)
  - processor/spc700: removed memory.cpp shorthand functions (fetch,
    load, store, pull, push)
  - processor/spc700: updated all instructions to follow cycle behavior
    as documented by Overload with a logic analyzer

Once again, the changes to the SPC700 core are really quite massive. And
this time it's not just cosmetic: the idle cycles have been updated to
pull from various memory addresses. This is why I removed the shorthand
functions -- so that I could handle the at-times very bizarre addresses
the SPC700 has on its address bus during its idle cycles.

There is one behavior Overload mentioned that I don't emulate ... one of
the cycles of the (X) transfer functions seems to not actually access
the $f0-ff internal SMP registers? I don't fully understand what
Overload is getting at, so I haven't tried to support it just yet.

Also, there are limits to logic analyzers. In many cases the same
address is read from twice consecutively. It is unclear which of the two
reads the SPC700 actually utilizes. I tried to choose the most logical
values (usually the first one), but ... I don't know that we'll be able
to figure this one out. It's going to be virtually impossible to test
this through software, because the PC can't really execute out of
registers that have side effects on reads.
2017-06-28 17:24:46 +10:00
Tim Allen
3517d5c4a4 Update to v103r02 release.
byuu says:

Changelog:

  - fc/apu: improved phase duty cycle emulation (mode 3 is 25% phase
    inverted; counter decrements)
  - md/apu: power/reset do not cancel 68K bus requests
  - md/apu: 68K is not granted bus access on Z80 power/reset
  - md/controller: replaced System::Peripherals with ControllerPort
    concept
  - md/controller: CTRL port is now read-write, maintains value across
    controller changes (and soon, soft resets)
  - md/psg: PSG sampling rate unintentionally modified¹
  - processor/spc700: improve cycle timing of (indirect),y instructions
    [Overload]
  - processor/spc700: idle() cycles actually read from the program
    counter; much like the 6502 [Overload]
      - some of the idle() cycles should read from other addresses; this
        still needs to be supported
  - processor/spc700: various cleanups to instruction function naming
  - processor/z80: prefix state (HL→IX,IY override) can now be
    serialized
  - icarus: fix install rule for certain platforms (it wasn't buggy on
    FreeBSD, but was on Linux?)

¹: the clock speed of the PSG is oscillator/15. But I was setting the
sampling rate to oscillator/15/16, which was around 223KHz. I am not
sure whether the PSG should be outputting at 3MHz or 223KHz. Amazingly
... I don't really hear a difference either way `o_O` I didn't actually
mean to make this change; I just noticed it after comparing the diff
between r01 and r02. If this turns out to be wrong, set

    stream = Emulator::audio.createStream(1, frequency() / 16.0);

in md/psg.cpp to revert this change.
2017-06-27 11:18:28 +10:00
Tim Allen
ecc7e899e0 Update to v103r01 release.
byuu says:

Changelog:

  - nall/dsp: improve one pole coefficient calculations [Fatbag]
  - higan/audio: reworked filters to support selection of either one
    pole (first-order) or biquad (second-order) filters
      - note: the design is not stable yet; so forks should not put too
        much effort into synchronizing with this change yet
  - fc: added first-order filters as per NESdev wiki (90hz lowpass +
    440hz lowpass + 14khz highpass)
  - fc: created separate NTSC-J and NTSC-U regions
      - NESdev wiki says the Japanese Famicom uses a separate audio
        filtering strategy, but details are fuzzy
      - there's also cartridge audio output being disabled on NES units;
        and differences with controllers
      - this stuff will be supported in the future, just adding the
        support for it now
  - gba: corrected serious bugs in PSG wave channel emulation [Cydrak]
      - note that if there are still bugs here, it's my fault
  - md/psg,ym2612: added first-order low-pass 2840hz filter to match
    VA3-VA6 Mega Drives
  - md/psg: lowered volume relative to the YM2612
      - using 0x1400; multiple people agreed it was the closest to the
        hardware recordings against a VA6
  - ms,md/psg: don't serialize the volume levels array
  - md/vdp: Hblank bit acts the same during Vblank as outside of it (it
    isn't always set during Vblank)
  - md/vdp: return isPAL in bit 0 of control port reads
  - tomoko: change command-line option separator from : to |
      - [Editor's note: This change was present in the public v103,
        but it's in this changelog because it was made after the v103 WIP]
  - higan/all: change the 20hz high-pass filters from second-order
    three-pass to first-order one-pass
      - these filters are meant to remove DC bias, but I honestly can't
        hear a difference with or without them
      - so there's really no sense wasting CPU power with an extremely
        powerful filter here

Things I did not do:

  - change icarus install rule
  - work on 8-bit Mega Drive SRAM
  - work on Famicom or Mega Drive region detection heuristics in icarus

My long-term dream plan is to devise a special user-configurable
filtering system where you can set relative volumes and create your own
list of filters (any number of them in any order at any frequency), that
way people can make the systems sound however they want.

Right now, the sanest place to put this information is inside the
$system.sys/manifest.bml files. But that's not very user friendly, and
upgrading to new versions will lose these changes if you don't copy them
over manually. Of course, cluttering the GUI with a fancy filter editor
is probably supreme overkill for 99% of users, so maybe that's fine.
2017-06-26 11:41:58 +10:00
Tim Allen
f6d7922e62 Include the official shaders in build artifacts. 2017-06-22 16:28:21 +10:00
Tim Allen
a2baea248f Don't bundle the cheats database in built artifacts.
As of v103, higan no longer includes the cheat database.
2017-06-22 16:27:13 +10:00
Tim Allen
561c6413a4 Update to v103 final release.
byuu says (in the public annoucement):

With this release, PC Engine, Master System, Game Gear, Mega Drive and
the newly added SuperGrafx support are now quite usable! They're far
from the best emulators for these systems, but with many bugfixes, full
sound emulation, and save state support ... many games are now fully
playable with decent accuracy. The Game Boy Advance emulation is also
now substantially improved with vastly improved sound quality and a new
dot-based PPU renderer. With that change, every single component in
higan is now cycle-based. Regrettably, these changes do carry a ~20%
performance penalty compared to GBA emulation in v102.

Changelog (since the previous release):

  - added SuperGrafx emulation
  - improved audio band-pass filtering for all emulated systems
  - screen rotation is now a hotkey; automatically rotates control
    bindings
  - screen rotation now supported on the Game Boy Advance as well (used
    by a small number of games)
  - massive improvements to Mega Drive emulation
  - massive code cleanups and rewrites to many CPU emulation cores
    (MOS6502, WDC65816, SPC700, etc)
  - Famicom, Master System, Mega Drive: added PAL emulation support
  - PC Engine, SuperGrafx, Master System, Game Gear, Mega Drive: added
    save state support
  - PC Engine, SuperGrafx: added PSG sound emulation
  - Master System, Game Gear: added PSG sound emulation
  - Mega Drive: added Z80 APU emulation [with help from Cydrak]
  - Mega Drive: added PSG sound emulation
  - Mega Drive: added YM2612 sound emulation [Cydrak]
  - Super Famicom: fixed Super Game Boy emulation
  - PC Engine: added save RAM support (using per-game PCE-CD backup RAM)
  - Game Boy Advance: substantial improvements to audio emulation
    quality
  - Game Boy Advance: convert scanline-based PPU renderer to a dot-based
    renderer
  - Game Boy Advance: properly initialize CPU state (fixes Classic NES
    Series games)
  - Game Boy Advance: MUL timing corrected [Jonas Quinn]
  - Mega Drive: emulate special mappers for Phantasy Star IV, Super
    Street Fighter II, etc.
  - Super Famicom: use darker luma for INIDISP=0 (large improvement to
    Final Fantasy III opening)
  - Super Famicom: fixed bugs in SMP OR1,AND1 instructions
  - cheat code database regrettably removed from official releases; will
    be made available separately
  - Famicom: PAL emulation improvements [hex\_usr]
2017-06-22 16:11:59 +10:00
Tim Allen
b7006822bf Update to v103 WIP release.
byuu says (in the WIP forum):

Changelog:

  - higan: cheat codes accept = and ? separators now
      - the new preferred code format is: address=value or
        address=if-match?value
      - the old code format of address/value and address/if-match/value
        will continue to work
  - higan: cheats.bml is no longer included with the base distribution
      - mightymo stopped updating it in 2015, and it's not source code;
        it can still be pulled in from older releases
  - fc: improved PAL mode timing; use PAL APU timing tables; fix PAL
    noise period table [hex\_usr]
  - md: support aborting a Z80 bus wait in order to capture save states
    without freezing
      - note that this will violate accuracy; but in practice a slight
        desync is better than an emulator deadlock
  - sfc: revert DSP ENDX randomization for now (want to research it more
    before deploying in an official release)
  - sfc: fix Super Famicom.sys/manifest.bml APU RAM size [hex\_usr]
  - tomoko: cleaned up make install rules
  - hiro/cocoa: use ABGR for pixel data [Sintendo]

Note: I forgot to change the command-line and drag-and-drop separator
from : to | in this WIP. However, it is corrected in the v103 official
binary and source published on download.byuu.org. Sorry about that, I
know it makes the Git repository history more difficult. I'm not
concerned whether the : → | change is part of v103 or v103r01 in the
repository, and will leave this to your discretion, Screwtape.

I also still need to set the VDP bit to indicate PAL mode in the Mega
Drive core. This is what happens when I have 47 things I have to do,
given how lousy my memory is. I miss things.
2017-06-22 16:10:13 +10:00
Tim Allen
8476f35153 Update to v102r28 release.
byuu says:

Changelog:

  - higan: `Emulator::<Platform::load>()` now returns a struct containing
    both a path ID and a string option
  - higan: `Emulator::<Platform::load>()` now takes an optional final
    argument of string options
  - fc: added PAL emulation (finally, only took six years)
  - md: added PAL emulation
  - md: fixed address parameter to `VDP::Sprite::write()`; fixes missing
    sprites in Super Street Fighter II
  - md: emulated HIRQ counter; fixes many games
      - Super Street Fighter II - status bar
      - Altered Beast - status bar
      - Sonic the Hedgehog - Labyrinth Zone - water effect
      - etc.
  - ms: added PAL emulation
  - sfc: added the ability to override the default region auto-detection
  - sfc: removed "system.region" override setting from `Super Famicom.sys`
  - tomoko: added options list to game folder load dialog window
  - tomoko: added the ability to specify game folder load options on the
    command-line

So, basically ... Sega forced a change with the way region detection
works. You end up with games that can run on multiple regions, and the
content changes accordingly. Bare Knuckle in NTSC-J mode will become
Streets of Rage in NTSC-U mode. Some games can even run in both NTSC and
PAL mode.

In my view, there should be a separate ROM for each region a game was
released in, even if the ROM content were identical. But unfortunately
that's not how things were done by anyone else.

So to support this, the higan load dialog now has a drop-down at the
bottom-right, where you can choose the region to load games from. On the
SNES, it defaults to "Auto", which will pull the region setting from the
manifest, or fall back on NTSC. On the Mega Drive ... unfortunately, I
can't auto-detect the region from the ROM header. $1f0 is supposed to
contain a string like "JUE", but instead you get games like Maui Mallard
that put an "A" there, and other such nonsense. Sega was far more lax
than Nintendo with the ROM header validity. So for now at least, you
have to manually select your region every time you play a Mega Drive
game, thus you have "NTSC-J", "NTSC-U", and "PAL". The same goes for the
Master System for the same reason, but there's only "NTSC" and "PAL"
here. I'm not sure if games have a way to detect domestic vs
international consoles.

And for now ... the Famicom is the same as well, with no auto-detection.
I'd sincerely hope iNES has a header bit for the region, but I didn't
bother with updating icarus to support that yet.

The way to pass these parameters on the command-line is to prefix the
game path with "option:", so for example:

    higan "PAL:/path/to/Sonic the Hedgehog (USA, Europe).md"

If you don't provide a prefix, it uses the default (NTSC-J, NTSC, or
Auto.) Obviously, it's not possible to pass parameters with
drag-and-drop, so you will always get the default option in said case.
2017-06-20 22:34:50 +10:00
Tim Allen
e7806dd6e8 Update to v102r27 release.
byuu says:

Changelog:

  - processor/gsu: minor code cleanup
  - processor/hg51b: renamed reg(Read,Write) to register(Read,Write)
  - processor/lr35902: minor code cleanup
  - processor/spc700: completed code cleanup (sans disassembler)
      - no longer uses internal global state inside instructions
  - processor/spc700: will no longer hang the emulator if stuck in a WAI
    (SLEEP) or STP (STOP) instruction
  - processor/spc700: fixed bug in handling of OR1 and AND1 instructions
  - processor/z80: minor code cleanup
  - sfc/dsp: revert to initializing registers to 0x00; save for
    ENDX=random(), FLG=0xe0 [Jonas Quinn]

Major testing of the SNES game library would be appreciated, now that
its CPU cores have all been revised.

We know the DSP registers read back as randomized data ... mostly, but
there are apparently internal latches, which we can't emulate with the
current DSP design. So until we know which registers have separate
internal state that actually *is* initialized, I'm going to play it safe
and not break more games.

Thanks again to Jonas Quinn for the continued research into this issue.

EDIT: that said ... `MD works if((ENDX&0x30) > 0)` is only a 3:4 chance
that the game will work. That seems pretty unlikely that the odds of it
working are that low, given hardware testing by others in the past :/ I
thought if worked if `PITCH != 0` before, which would have been way more
likely.

The two remaining CPU cores that need major cleanup efforts are the
LR35902 and ARM cores. Both are very large, complicated, annoying cores
that will probably be better off as full rewrites from scratch. I don't
think I want to delay v103 in trying to accomplish that, however.

So I think it'll be best to focus on allowing the Mega Drive core to not
lock when processors are frozen waiting on a response from other
processors during a save state operation. Then we should be good for a
new release.
2017-06-19 12:07:54 +10:00
Tim Allen
50411a17d1 Update to v102r26 release.
byuu says:

Changelog:

  - md/ym2612: initialize DAC sample to center volume [Cydrak]
  - processor/arm: add accumulate mode extra cycle to mlal [Jonas
    Quinn]
  - processor/huc6280: split off algorithms, improve naming of functions
  - processor/mos6502: split off algorithms
  - processor/spc700: major revamp of entire core (~50% completed)
  - processor/wdc65816: fixed several bugs introduced by rewrite

For the SPC700, this turns out to be very old code as well, with global
object state variables, those annoying `{Boolean,Natural}BitField` types,
`under_case` naming conventions, heavily abbreviated function names, etc.
I'm working to get the code to be in the same design as the MOS6502,
HuC6280, WDC65816 cores, since they're all extremely similar in terms of
architectural design (the SPC700 is more of an off-label
reimplementation of a 6502 core, but still.)

The main thing left is that about 90% of the actual instructions still
need to be adapted to not use the internal state (`aa`, `rd`, `dp`,
`sp`, `bit` variables.) I wanted to finish this today, but ran out of
time before work.

I wouldn't suggest too much testing just yet. We should wait until the
SPC700 core is finished for that. However, if some does want to and
spots regressions, please let me know.
2017-06-16 10:06:17 +10:00
Tim Allen
b73d918776 Update to v102r25 release.
byuu says:

Changelog:

  - processor/arm: corrected MUL instruction timings [Jonas Quinn]
  - processor/wdc65816: finished phase two of the rewrite

I'm really pleased with the visual results of the wdc65816 core rewrite.
I was able to eliminate all of the weird `{Boolean,Natural}BitRange`
templates, as well as the need to use unions/structs. Registers are now
just simple `uint24` or `uint16` types (technically they're `Natural<T>`
types, but then all of higan uses those), flags are now just bool types.
I also eliminated all of the implicit object state inside of the core
(aa, rd, dp, sp) and instead do all computations on the stack frame with
local variables. Through using macros to reference the registers and
individual parts of them, I was able to reduce the visual tensity of all
of the instructions. And by using normal types without implicit states,
I was able to eliminate about 15% of the instructions necessary, instead
reusing existing ones.

The final third phase of the rewrite will be to recode the disassembler.
That code is probably the oldest code in all of higan right now, still
using sprintf to generate the output. So it is very long overdue for a
cleanup.

And now for the bad news ... as with any large code cleanup, regression
errors have seeped in. Currently, no games are running at all. I've left
the old disassembler in for this reason: we can compare trace logs of
v102r23 against trace logs of v102r25. The second there's any
difference, we've spotted a buggy instruction and can correct it.

With any luck, this will be the last time I ever rewrite the wdc65816
core. My style has changed wildly over the ~10 years since I wrote this
core, but it's really solidifed in recent years.
2017-06-15 01:55:55 +10:00
Tim Allen
6e8406291c Update to v102r24 release.
byuu says

Changelog:

  - FC: fixed three MOS6502 regressions [hex\_usr]
  - GBA: return fetched instruction instead of 0 for unmapped MMIO
    (passes all of endrift's I/O tests)
  - MD: fix VDP control port read Vblank bit to test screen height
    instead of hard-code 240 (fixes Phantasy Star IV)
  - MD: swap USP,SSP when executing an exception (allows Super Street
    Fighter II to run; but no sprites visible yet)
  - MD: grant 68K access to Z80 bus on reset (fixes vdpdoc demo ROM from
    freezing immediately)
  - SFC: reads from $00-3f,80-bf:4000-43ff no longer update MDR
    [p4plus2]
  - SFC: massive, eight-hour cleanup of WDC65816 CPU core ... still not
    complete

The big change this time around is the SFC CPU core. I've renamed
everything from R65816 to WDC65816, and then went through and tried to
clean up the code as much as possible. This core is so much larger than
the 6502 core that I chose cleaning up the code to rewriting it.

First off, I really don't care for the BitRange style functionality. It
was an interesting experiment, but its fatal flaw are that the types are
just bizarre, which makes them hard to pass around generically to other
functions as arguments. So I went back to the list of bools for flags,
and union/struct blocks for the registers.

Next, I renamed all of the functions to be more descriptive: eg
`op_read_idpx_w` becomes `instructionIndexedIndirectRead16`. `op_adc_b`
becomes `algorithmADC8`. And so forth.

I eliminated about ten instructions because they were functionally
identical sans the index, so I just added a uint index=0 parameter to
said functions. I added a few new ones (adjust→INC,DEC;
pflag→REP,SEP) where it seemed appropriate.

I cleaned up the disaster of the instruction switch table into something
a whole lot more elegant without all the weird argument decoding
nonsense (still need M vs X variants to avoid having to have 4-5
separate switch tables, but all the F/I flags are gone now); and made
some things saner, like the flag clear/set and branch conditions, now
that I have normal types for flags and registers once again.

I renamed all of the memory access functions to be more descriptive to
what they're doing: eg writeSP→push, readPC→fetch,
writeDP→writeDirect, etc. Eliminated some of the special read/write
modes that were only used in one single instruction.

I started to clean up some of the actual instructions themselves, but
haven't really accomplished much here. The big thing I want to do is get
rid of the global state (aa, rd, iaddr, etc) and instead use local
variables like I am doing with my other 65xx CPU cores now. But this
will take some time ... the algorithm functions depend on rd to be set
to work on them, rather than taking arguments. So I'll need to rework
that.

And then lastly, the disassembler is still a mess. I want to finish the
CPU cleanups, and then post a new WIP, and then rewrite the disassembler
after that. The reason being ... I want a WIP that can generate
identical trace logs to older versions, in case the CPU cleanup causes
any regressions. That way I can more easily spot the errors.

Oh ... and a bit of good news. v102 was running at ~140fps on the SNES
core. With the new support to suspend/resume WAI/STP, plus the internal
CPU registers not updating the MDR, the framerate dropped to ~132fps.
But with the CPU cleanups, performance went back to ~140fps. So, hooray.
Of course, without those two other improvements, we'd have ended up at
possibly ~146-148fps, but oh well.
2017-06-13 11:42:31 +10:00
Tim Allen
cea64b9991 Update to v102r23 release.
byuu says:

Changelog:
  - rewrote the 6502 CPU core from scratch. Now called MOS6502,
    supported BCD mode
      - Famicom core disables BCD mode via MOS6502::BCD = 0;
  - renamed r65816 folder to wdc65816 (still need to rename the actual
    class, though ...)

Note: need to remove build rules for the now renamed r6502, r65816
objects from processor/GNUmakefile.

So this'll seem like a small WIP, but it was a solid five hours to
rewrite the entire 6502 core. The reason I wanted to do this was because
the old 6502 core was pretty sloppy. My coding style improved a lot, and
I really liked how the HuC6280 CPU core came out, so I wanted the 6502
core to be like that one.

The core can now support BCD mode, so hopefully that will prove useful
to hex\_usr and allow one core to run both the NES and his Atari 2600
cores at some point.

Note that right now, the core doesn't support any illegal instructions.
The old core supported a small number of them, but were mostly the no
operation ones. The goal is support all of the illegal instructions at
some point.

It's very possible the rewrite introduced some regressions, so thorough
testing of the NES core would be appreciated if anyone were up for it.
2017-06-11 11:51:53 +10:00
Tim Allen
8af3e4a6e2 Update to v102r22 release.
byuu says:

Changelog:

  - higan: Emulator::Interface::videoSize() renamed to videoResolution()
  - higan: Emulator::Interface::rtcsync() renamed to rtcSynchronize()
  - higan: added video display rotation support to Video
  - GBA: substantially improved audio mixing
      - fixed bug with FIFO 50%/100% volume setting
      - now properly using SOUNDBIAS amplitude to control output
        frequencies
      - reduced quantization noise
      - corrected relative volumes between PSG and FIFO channels
      - both PSG and FIFO values cached based on amplitude; resulting in
        cleaner PCM samples
      - treating PSG volume=3 as 200% volume instead of 0% volume now
        (unverified: to match mGBA)
  - GBA: properly initialize ALL CPU state; including the vital
    prefetch.wait=1 (fixes Classic NES series games)
  - GBA: added video rotation with automatic key translation support
  - PCE: reduced output resolution scalar from 285x242 to 285x240
      - the extra two scanlines won't be visible on most TVs; and they
        make all other cores look worse
      - this is because all other cores output at 240p or less; so they
        were all receiving black bars in windowed mode
  - tomoko: added "Rotate Display" hotkey setting
  - tomoko: changed hotkey multi-key logic to OR instead of AND
      - left support for flipping it back inside the core; for those so
        inclined; by uncommenting one line in input.hpp
  - tomoko: when choosing Settings→Configuration, it will
    automatically select the currently loaded system
      - for instance, if you're playing a Game Gear game, it'll take you
        to the Game Gear input settings
      - if no games are loaded, it will take you to the hotkeys panel
        instead
  - WS(C): merged "Hardware-Vertical", "Hardware-Horizontal" controls
    into combined "Hardware"
  - WS(C): converted rotation support from being inside the core to
    using Emulator::Video
      - this lets WS(C) video content scale larger now that it's not
        bounded by a 224x224 square box
  - WS(C): added automatic key rotation support
  - WS(C): removed emulator "Rotate" key (use the general hotkey
    instead; I recommend F8 for this)
  - nall: added serializer support for nall::Boolean (boolean) types
      - although I will probably prefer the usage of uint1 in most cases
2017-06-09 00:08:02 +10:00
Tim Allen
a4629e1f64 Update to v102r21 release.
byuu says:

Changelog:

  - GBA: fixed WININ2 reads, BG3PB writes [Jonas Quinn]
  - R65816: added support for yielding/resuming from WAI/STP¹
  - SFC: removed status.dmaCounter functionality (also fixes possible
    TAS desync issue)
  - tomoko: added support for combinatorial inputs [hex\_usr\]²
  - nall: fixed missing return value from Arithmetic::operator--
    [Hendricks266]

Now would be the time to start looking for major regressions with the
new GBA PPU renderer, I suppose ...

¹: this doesn't matter for the master thread (SNES CPU), but is
important for slave threads (SNES SA1). If you try to save a state and
the SA1 is inside of a WAI instruction, it will get stuck there forever.
This was causing attempts to create a save state in Super Bomberman
- Panic Bomber W to deadlock the emulator and crash it. This is now
finally fixed.

Note that I still need to implement similar functionality into the Mega
Drive 68K and Z80 cores. They still have the possibility of deadlocking.
The SNES implementation was more a dry-run test for this new
functionality. This possible crashing bug in the Mega Drive core is the
major blocking bug for a new official release.

²: many, many thanks to hex\_usr for coming up with a really nice
design. I mostly implemented it the exact same way, but with a few tiny
differences that don't really matter (display " and ", " or " instead of
" & ", " | " in the input settings windows; append → bind;
assignmentName changed to displayName.)

The actual functionality is identical to the old higan v094 and earlier
builds. Emulated digital inputs let you combine multiple possible keys
to trigger the buttons. This is OR logic, so you can map to eg
keyboard.up OR gamepad.up for instance. Emulated analog inputs always
sum together. Emulated rumble outputs will cause all mapped devices to
rumble, which is probably not at all useful but whatever. Hotkeys use
AND logic, so you have to press every key mapped to trigger them. Useful
for eg Ctrl+F to trigger fullscreen.

Obviously, there are cases where OR logic would be nice for hotkeys,
too. Eg if you want both F11 and your gamepad's guide button to trigger
the fullscreen toggle. Unfortunately, this isn't supported, and likely
won't ever be in tomoko. Something I might consider is a throw switch in
the configuration file to swap between AND or OR logic for hotkeys, but
I'm not going to allow construction of mappings like "(Keyboard.Ctrl and
Keyboard.F) or Gamepad.Guide", as that's just too complicated to code,
and too complicated to make a nice GUI to set up the mappings for.
2017-06-06 23:44:40 +10:00
Tim Allen
3bcf3c24c9 Update to v102r20 release.
byuu says:

Changelog:

  - nall: `#undef OUT` on Windows platform
  - GBA: add missing CPU prefetch state to serialization (this was
    breaking serialization in games using ROM prefetch)
  - GBA: reset all PPU data in the power() function (some things were
    missing before, causing issues on reset)
  - GBA: restored horizontal mosaic emulation to the new pixel-based
    renderer
  - GBA: fixed tilemap background horizontal flipping (Legend of Spyro -
    warning screen)
  - GBA: fixed d8 bits of scroll registers (ATV - Thunder Ridge Racers -
    menu screen)
  - SFC: DRAM refresh ticks the ALU MUL/DIV registers five steps forward
    [reported by kevtris]
  - SFC: merged dmaCounter and autoJoypadCounter into new shared
    clockCounter
      - left stub for old dmaCounter so that I can do some traces to
        ensure the new code's 100% identical

GBA save states would have been broken since whenever I emulated ROM
prefetch. I guess not many people are using the GBA core ...
2017-06-06 11:39:27 +10:00
Tim Allen
2461293ff0 Update to v102r19 release.
byuu says:

Note: add `#undef OUT` to the top of higan/gba/ppu/ppu.hpp to compile on
Windows (ugh ...) Now to await posts about this in four more threads
again ;)

Changelog:

  - GBA: rewrote PPU from a scanline-based renderer to a pixel-based
    renderer
  - ruby: fixed video/gdi bugs

Note that there's an approximately 21% speed penalty compared to v102r18
for the pixel-based renderer.

Also, horizontal mosaic effects are not yet implemented. But they should
be prior to v103. This one is a little tricky as it currently works on
fully rendered scanlines. I need to roll the mosaic into the background
renderers, and then for sprites, well ... see below.

The trickiest part by far of this new renderer is the object (sprite)
system. Unlike every other system I emulate, the GBA supports affine
rendering of its sprites. Or in other words, rotation effects. And it
also has a very complex priority system.

Right now, I can't see any way that the GBA PPU could render pixels in
real-time like this. My belief is that there's a 240-entry buffer that
fills up the next scanline's row of pixels. Which means it probably also
runs on the last scanline of Vblank so that the first scanline has
sprite data.

However, I didn't design my object renderer like this just yet. For now,
it creates a buffer of all 240 pixels right away at the start of the
scanline. I know\!\! That's technically scanline-based. But it's only
for fetching object tiledata, and it's only temporary.

What needs to happen is I need a way to run something like a "mini libco
thread" inside of the main thread, so that the object renderer can run
in parallel with the rest of the PPU, yet not be a hideous abomination
of a state machine, yet also not be horrendously slow as a full libco
thread would be.

I'm envisioning some kind of stackless yielding coroutine. But I'll need
to think through how to design that, given the absence of coroutines
even in C++17.
2017-06-04 13:16:44 +10:00
Tim Allen
1ca4609079 Update to v102r18 release.
byuu says:

This WIP fixes all the critical pending issues I had open. I'm sure
there's many more that simply didn't make their way into said list. So
by all means, please report important issues you're aware of so they can
get fixed.

Changelog:

  - ruby: add variable texture support to GDI video driver [bug
    reported by Cydrak]
  - ruby: minor cleanups to XShm video driver
  - ruby: fix handling of up+down, left+right hat cases for XInput
    driver [bug reported by Cydrak]
  - nall: fixed vector class so that compilation with GCC 7.1 should
    succeed [SuperMikeMan]
  - sfc: initialize most DSP registers to random values to fix Magical
    Drop [Jonas Quinn]
  - sfc: lower PPU brightness when luma=0 from 50% scale to 25% scale;
    helps scenes like Final Fantasy III's intro
2017-05-30 17:48:41 +10:00
Tim Allen
82c58527c3 Update to v102r17 release.
byuu says:

Changelog:

  - GBA: process audio at 2MHz instead of 32KHz¹
  - MD: do not allow the 68K to stop the Z80, unless it has been granted
    bus access first
  - MD: do not reset bus requested/granted signals when the 68K resets
    the Z80
      - the above two fix The Lost Vikings
  - MD: clean up the bus address decoding to be more readable
  - MD: add support for a13000-a130ff (#TIME) region; pass to cartridge
    I/O²
  - MD: emulate SRAM mapping used by >16mbit games; bank mapping used
    by >32mbit games³
  - MD: add 'reset pending' flag so that loading save states won't
    reload 68K PC, SP registers
      - this fixes save state support ... mostly⁴
  - MD: if DMA is not enabled, do not allow CD5 to be set [Cydrak]
      - this fixes in-game graphics for Ristar. Title screen still
        corrupted on first run
  - MD: detect and break sprite lists that form an infinite loop
    [Cydrak]
      - this fixes the emulator from dead-locking on certain games
  - MD: add DC offset to sign DAC PCM samples [Cydrak]
      - this improves audio in Sonic 3
  - MD: 68K TAS has a hardware bug that prevents writing the result back
    to RAM
      - this fixes Gargoyles
  - MD: 68K TRAP should not change CPU interrupt level
      - this fixes Shining Force II, Shining in the Darkness, etc
  - icarus: better SRAM heuristics for Mega Drive games

Todo:

  - need to serialize the new cartridge ramEnable, ramWritable, bank
    variables

¹: so technically, the GBA has its FIFO queue (raw PCM), plus a GB
chipset. The GB audio runs at 2MHz. However, I was being lazy and
running the sequencer 64 times in a row, thus decimating the audio to
32KHz. But simply discarding 63 out of every 64 samples resorts in
muddier sound with more static in it.

However ... increasing the audio thread processing intensity 64-fold,
and requiring heavy-duty three-chain lowpass and highpass filters is not
cheap. For this bump in sound quality, we're eating a loss of about 30%
of previous performance.

Also note that the GB audio emulation in the GBA core still lacks many
of the improvements made to the GB core. I was hoping to complete the GB
enhancements, but it seems like I'm never going to pass blargg's
psychotic edge case tests. So, first I want to clean up the GB audio to
my current coding standards, and then I'll port that over to the GBA,
which should further increase sound quality. At that point, it sound
exceed mGBA's audio quality (due to the ridiculously high sampling rate
and strong-attenuation audio filtering.)

²: word writes are probably not handled correctly ... but games are
only supposed to do byte writes here.

³: the SRAM mapping is used by games like "Story of Thor" and
"Phantasy Star IV." Unfortunately, the former wasn't released in the US
and is region protected. So you'll need to change the NTSU to NTSCJ in
md/system/system.cpp in order to boot it. But it does work nicely now.
The write protection bit is cleared in the game, and then it fails to
write to SRAM (soooooooo many games with SRAM write protection do this),
so for now I've had to disable checking that bit. Phantasy Star IV has a
US release, but sadly the game doesn't boot yet. Hitting some other bug.

The bank mapping is pretty much just for the 40mbit Super Street Fighter
game. It shows the Sega and Capcom logos now, but is hitting yet another
bug and deadlocking.

For now, I emulate the SRAM/bank mapping registers on all cartridges,
and set sane defaults. So long as games don't write to $a130XX, they
should all continue to work. But obviously, we need to get to a point
where higan/icarus can selectively enable these registers on a per-game
basis.

⁴: so, the Mega Drive has various ways to lock a chip until another
chip releases it. The VDP can lock the 68K, the 68K can lock the Z80,
etc. If this happens when you save a state, it'll dead-lock the
emulator. So that's obviously a problem that needs to be fixed. The fix
will be nasty ... basically, bypassing the dead-lock, creating a
miniature, one-instruction-long race condition. Extremely unlikely to
cause any issues in practice (it's only a little worse than the SNES
CPU/SMP desync), but ... there's nothing I can do about it. So you'll
have to take it or leave it. But yeah, for now, save states may lock up
the emulator. I need to add code to break the loops when in the process
of creating a save state still.
2017-03-10 21:23:29 +11:00
Tim Allen
04072b278b Update to v102r16 release.
byuu says:

Changelog:

  - Emulator::Stream now allows adding low-pass and high-pass filters
    dynamically
      - also accepts a pass# count; each pass is a second-order biquad
        butterworth IIR filter
  - Emulator::Stream no longer automatically filters out >20KHz
    frequencies for all streams
  - FC: added 20Hz high-pass filter; 20KHz low-pass filter
  - GB: removed simple 'magic constant' high-pass filter of unknown
    cutoff frequency (missed this one in the last WIP)
  - GB,SGB,GBC: added 20Hz high-pass filter; 20KHz low-pass filter
  - MS,GG,MD/PSG: added 20Hz high-pass filter; 20KHz low-pass filter
  - MD: added save state support (but it's completely broken for now;
    sorry)
  - MD/YM2612: fixed Voice#3 per-operator pitch support (fixes sound
    effects in Streets of Rage, etc)
  - PCE: added 20Hz high-pass filter; 20KHz low-pass filter
  - WS,WSC: added 20Hz high-pass filter; 20KHz low-pass filter

So, the point of the low-pass filters is to remove frequencies above
human hearing. If we don't do this, then resampling will introduce
aliasing that results in sounds that are audible to the human ear. Which
basically an annoying buzzing sound. You'll definitely hear the
improvement from these in games like Mega Man 2 on the NES. Of course,
these already existed before, so this WIP won't sound better than
previous WIPs.

The high-pass filters are a little more complicated. Their main role is
to remove DC bias and help to center the audio stream. I don't
understand how they do this at all, but ... that's what everyone who
knows what they're talking about says, thus ... so be it.

I have set all of the high-pass filters to 20Hz, which is below the
limit of human hearing. Now this is where it gets really interesting ...
technically, some of these systems actually cut off a lot of range. For
instance, the GBA should technically use an 800Hz high-pass filter when
output is done through the system's speakers. But of course, if you plug
in headphones, you can hear the lower frequencies.

Now 800Hz ... you definitely can hear. At that level, nearly all of the
bass is stripped out and the audio is very tinny. Just like the real
system. But for now, I don't want to emulate the audio being crushed
that badly.

I'm sticking with 20Hz everywhere since it won't negatively affect audio
quality. In fact, you should not be able to hear any difference between
this WIP and the previous WIP. But theoretically, DC bias should mostly
be removed as a result of these new filters. It may be that we need to
raise the values on some cores in the future, but I don't want to do
that until we know for certain that we have to.

What I can say is that compared to even older WIPs than r15 ... the
removal of the simple one-pole low-pass and high-pass filters with the
newer three-pass, second-order filters should result in much better
attenuation (less distortion of audible frequencies.) Probably not
enough to be noticeable in a blind test, though.
2017-03-09 07:20:40 +11:00
Tim Allen
7e7003fd29 Update to v102r15 release.
byuu says:

Changelog:

  - nall: added DSP::IIR::OnePole (which is a first-order IIR filter)
  - FC/APU: removed strong highpass, weak hipass filters (and the
    dummied out lowpass filter)
  - MS,GG,MD/PSG: removed lowpass filter
  - MS,GG,MD/PSG: audio was not being centered properly; removed
    centering for now
  - MD/YM2612: fixed clipping of accumulator from 18 signed bits to 14
    signed bits (-0x2000 to +0x1fff) [Cydrak]
  - MD/YM2612: removed lowpass filter
  - PCE/PSG: audio was not being centered properly; removed centering
    for now

First thing is that I've removed all of the ad-hoc audio filtering.
Emulator::Stream intrinsically provides a three-pass, second-order
biquad IIR butterworth lowpass filter that clips frequencies above 20KHz
with very good attenuation (as good as IIR gets, anyway.)

It doesn't really make sense to have the various cores running
additional lowpass filters. If we want to filter frequencies below
20KHz, then I can adapt Emulator::Audio::createStream() to take a cutoff
frequency value, and we can do it all at once, with much better quality.

Right now, I don't know what frequencies are best to cut off the various
other audio cores, so they're just gone for now.

As for the highpass filters for the Famicom core, well ... you don't get
aliasing from resampling low frequencies. And generally speaking, too
low a frequency will be inaudible anyway. All these were doing was
killing possible bass (if they were too strong.) We can add them again,
but only if someone can convert Ryphecha's ad-hoc magic integers into a
frequency cutoff. In which case, I'll use my biquad IIR filter to do it
even better. On this note, it may prove useful to do this for the MD PSG
as well, to try and head off unnecessary clamping when mixing with the
YM2612.

Finally, there was the audio centering issue that affected the
MS,GG,MD,PCE,SG cores. It was flooring the "silent" audio level, which
was resulting in extremely heavy distortion if you tried listening to
higan and, say, audacious at the same time. Without the botched
centering, this distortion is completely gone now.

However, without any centering, we've halved the potential volume range.
This means the audio slider in higan's audio settings panel will start
clamping twice as quickly. So ultimately, we need to figure out how to
fix the centering. This isn't as simple as just subtracting less. We
will probably have to center every individual audio channel before
summing them to do this properly.

Results:

On the Mega Drive, Altered Beast sounds quite a bit better, a lot less
distortion now. But it's still not perfect, especially sound effects.
Further, Bare Knuckle / Streets of Rage still has really bad sound
effects. It looks like I broke something in Cydrak's code when trying to
adapt it to my style =(
2017-03-07 07:23:22 +11:00
Tim Allen
89d47914b9 Update to v102r14 release.
byuu says:

Changelog:

  - (MS,GG,MD)/PSG: flip output bit from noise channel [TmEE]
  - MD/YM2612: rewrite YM2612::Channel functions to
    YM2612::Channel::Operator functions¹
  - MD/YM2612: pitch/octave I/O registers should set reload, not value
    (fixes sound in most games)
  - MD/YM2612: don't try to sign-extend raw PCM values (fixes Shining
    Force opening music)
  - MD/YM2612: various algorithm simplifications; conversions from
    `*`, `/`, `%` to `<<`, `>>`; etc.

Overall ... Sonic the Hedgehog sounds really, really great. Almost
perfect, but there's a bit of clamping going on in the special zones.
Langrisser II sounds really great. Shining Force sounds pretty much
perfect. Bare Knucles (Streets of Rage) does pretty badly ... punches
sound more like dinging a salad fork on a wine glass, heh. Altered Beast
is extremely broken ... no music at the title screen, very distorted
in-game music. I suspect a bug outside of the YM2612 is affecting this
game.

So, the YM2612 emulation isn't perfect, but it's a really good start to
the most complex sound chip in all of higan. Hopefully the VRC7 and
YM2413 will prove to be less ferocious ... not that I'm in any rush to
work on either. The former is going to need the NES mapper rewrite to be
done first, and the latter is cool but not very necessary since all
those games have fallbacks to the inferior PSG audio.

But really ... I can't thank Cydrak enough for doing this for me. It
would have probably taken me months to parse through all of the
documentation on this chip (most of which is in a 55-page thread on
spritesmind that is filled with wrong/outdated information at the start,
and corrections as you go deeper.) Not to mention, learning about what
the hell detuning, low-frequency oscillation, tremolo, vibrato, etc were
all about. Or how those algorithms to compute the final output work. Or
the dozens of special cases littered in there to make everything sound
good. Fierce, nasty chip that.

Now the last real problem is save states ... the Mega Drive is going to
be the trickiest of all to implement with libco. There are lots of areas
where one chip will deadlock another chip while it completes some
operation. We don't have a choice but to force those stalls to abort
anyway, in order to let libco reach the start of its entry point once
again. I don't know what kind of impact that'll have on states ... I
suspect they'll work almost as reliably as the SNES does, but I can't
know that until I implement it. It's going to be pretty nasty, though.

¹: this basically removes a lot of unnecessary op. prefixes and the
need to capture `auto& op = operators[index]` at the start of every
function.

I wanted to have subfunctions like
`YM2612::Channel::Operator::Envelope::run()`, etc but unfortunately,
pretty much all of the envelope, phase, pitch, level functions need to
access each other's state.
2017-03-03 21:45:07 +11:00
Tim Allen
0bf2c9d4e1 Update to v102r13 release.
byuu says:

Changelog:

  - removed Emulator::Interface::videoFrequency(), audioFrequency()¹
  - (MS,GG,MD)/PSG: removed inversion on noise channel LFSR update
    [mic_]
  - MD/PSG: lowered volume to match YM2612 volume
  - MD/YM2612: added Cydrak's emulation of FM channels and LFO²

¹: These were no longer used by the UI. The video frequency is
adaptive on many systems. And the audio frequency is meaningless due to
Emulator::Audio always outputting a consistent frequency specified by
the UI. Plus, take the Genesis where there's two sound chips running at
different frequencies. So, these had to go.

²: Due to some lurking bugs, the audio is completely broken
unfortunately. Will need to be debugged :(

First pass looking for any typos didn't yield any obvious results.
2017-03-02 07:40:55 +11:00
Tim Allen
4c3f9b93e7 Update to v102r12 release.
byuu says:

Changelog:

  - MD/PSG: fixed 68K bus Z80 status read address location
  - MS, GG, MD/PSG: channels post-decrement their counters, not
    pre-decrement [Cydrak]¹
  - MD/VDP: cache screen width registers once per scanline; screen
    height registers once per frame
  - MD/VDP: support 256-width display mode (used in Shining Force, etc)
  - MD/YM2612: implemented timers²
  - MD/YM2612: implemented 8-bit PCM DAC²
  - 68000: TRAP instruction should index the vector location by 32 (eg
    by 128 bytes), fixes Shining Force
  - nall: updated hex(), octal(), binary() functions to take uintmax
    instead of template<typename T> parameter³

¹: this one makes an incredible difference. Sie noticed that lots of
games set a period of 0, which would end up being a really long period
with pre-decrement. By fixing this, noise shows up in many more games,
and sounds way better in games even where it did before. You can hear
extra sound on Lunar - Sanposuru Gakuen's title screen, the noise in
Sonic The Hedgehog (Mega Drive) sounds better, etc.

²: this also really helps sound. The timers allow PSG music to play
back at the correct speed instead of playing back way too quickly. And
the PCM DAC lets you hear a lot of drum effects, as well as the
"Sega!!" sound at the start of Sonic the Hedgehog, and the infamous,
"Rise from your grave!" line from Altered Beast.

Still, most music on the Mega Drive comes from the FM channels, so
there's still not a whole lot to listen to.

I didn't implement Cydrak's $02c test register just yet. Sie wasn't 100%
certain on how the extended DAC bit worked, so I'd like to play it a
little conservative and get sound working, then I'll go back and add a
toggle or something to enable undocumented registers, that way we can
use that to detect any potential problems they might be causing.

³: unfortunately we lose support for using hex() on nall/arithmetic
types. If I have a const Pair& version of the function, then the
compiler gets confused on whether Natural<32> should use uintmax or
const Pair&, because compilers are stupid, and you can't have explicit
arguments in overloaded functions. So even though either function would
work, it just decides to error out instead >_>

This is actually really annoying, because I want hex() to be useful for
printing out nall/crypto keys and hashes directly.

But ... this change had to be made. Negative signed integers would crash
programs, and that was taking out my 68000 disassembler.
2017-02-27 19:45:51 +11:00
Tim Allen
1cab2dfeb8 Update to v102r11 release.
byuu says:

Changelog:

  - MD: connected 32KB cartridge RAM up to every Genesis game under 2MB
    loaded¹
  - MS, GG, MD: improved PSG noise channel emulation, hopefully²
  - MS, GG, MD: lowered PSG volume so that the lowpass doesn't clamp
    samples³
  - MD: added read/write handlers for VRAM, VSRAM, CRAM
  - MD: block VRAM copy when CD4 is clear⁴
  - MD: rewrote VRAM fill, VRAM copy to be byte-based⁵
  - MD: VRAM fill byte set should fall through to regular data port
    write handler⁶

¹: the header parsing for backup RAM is really weird. It's spaces
when not used, and seems to be 0x02000001-0x02003fff for the Shining
games. I don't understand why it starts at 0x02000001 instead of
0x02000000. So I'm just forcing every game to have 32KB of RAM for now.
There's also special handling for ROMs > 2MB that also have RAM
(Phantasy Star IV, etc) where there's a toggle to switch between ROM and
RAM. For now, that's not emulated.

I was hoping the Shining games would run after this, but they're still
dead-locking on me :(

²: Cydrak pointed out some flaws in my attempt to implement what he
had. I was having trouble understanding what he meant, so I went back
and read the docs on the sound chip and tried implementing the counter
the way the docs describe. Hopefully I have this right, but I don't know
of any good test ROMs to make sure my noise emulation is correct. The
docs say the shifted-out value goes to the output instead of the low bit
of the LFSR, so I made that change as well.

I think I hear the noise I'm supposed to in Sonic Marble Zone now, but
it seems like it's not correct in Green Hill Zone, adding a bit of an
annoying buzz to the background music. Maybe it sounds better with the
YM2612, but more likely, I still screwed something up :/

³: it's set to 50% range for both cores right now. For the MD, it
will need to be 25% once YM2612 emulation is in.

⁴: technically, this deadlocks the VDP until a hard reset. I could
emulate this, but for now I just don't do the VRAM copy in this case.

⁵: VSRAM fill and CRAM fill not supported in this new mode. They're
technically undocumented, and I don't have good notes on how they work.
I've been seeing conflicting notes on whether the VRAM fill buffer is
8-bits or 16-bits (I chose 8-bits), and on whether you write the low
byte and then high byte of each words, or the high byte and then low
byte (I chose the latter.)

The VRAM copy improvements fix the opening text in Langrisser II, so
that's great.

⁶: Langrisser II sets the transfer length to one less than needed to
fill the background letter tile on the scenario overview screen. After
moving to byte-sized transfers, a black pixel was getting stuck there.
So effectively, VRAM fill length becomes DMA length + 1, and the first
byte uses the data port so it writes a word value instead of just a byte
value. Hopefully this is all correct, although it probably gets way more
complicated with the VDP FIFO.
2017-02-25 22:11:46 +11:00
Tim Allen
68f04c3bb8 Update to v102r10 release.
byuu says:

Changelog:

  - removed Emulator::Interface::Capabilities¹
  - MS: improved the PSG emulation a bit
  - MS: added cheat code support
  - MS: added save state support²
  - MD: emulated the PSG³

¹: there's really no point to it anymore. I intend to add cheat codes
to the GBA core, as well as both cheat codes and save states to the Mega
Drive core. I no longer intend to emulate any new systems, so these
values will always be true. Further, the GUI doesn't respond to these
values to disable those features anymore ever since the hiro rewrite, so
they're double useless.

²: right now, the Z80 core is using a pointer for HL-\>(IX,IY)
overrides. But I can't reliably serialize pointers, so I need to convert
the Z80 core to use an integer here. The save states still appear to
work fine, but there's the potential for an instruction to execute
incorrectly if you're incredibly unlucky, so this needs to be fixed as
soon as possible. Further, I still need a way to serialize
array<T, Size> objects, and I should also add nall::Boolean
serialization support.

³: I don't have a system in place to share identical sound chips. But
this chip is so incredibly simple that it's not really much trouble to
duplicate it. Further, I can strip out the stereo sound support code
from the Game Gear portion, so it's even tinier.

Note that the Mega Drive only just barely uses the PSG. Not at all in
Altered Beast, and only for a tiny part of the BGM music on Sonic 1,
plus his jump sound effect.
2017-02-23 08:25:01 +11:00
Tim Allen
8071da4c6a Update to v102r09 release.
byuu says:

Changelog:

  - MD: restructured DMA to a subclass of VDP
  - MD: implemented VRAM copy mode (fixes Langrisser II ... mostly)
  - MS: implemened PSG support [Cydrak]
  - GG: implemented PSG stereo sound support
  - MS: use the new struct Model {} design that other cores use

The MS/GG PSG should be feature complete, but I don't have good tests
for Game Gear stereo mode, nor for the noise channel. There's also a
really weird behavior with when to reload the channel counters on volume
register writes. I can confirm what Cydrak observed in that following
the docs and reloading always creates serious audio distortion problems.
So, more research is needed there.

To get the correct sound out of the PSG, I have to run it at 3.58MHz /
16, which seems really weird to me. The docs make it sound like it's
supposed to run at the full 3.58MHz. If we can really run it at
223.7KHz, then that's help reduce the overhead of PSG emulation, which
will definitely come in handy for Mega Drive, and possibly later Mega
CD, emulation.

I have not implemented the PSG into the Mega Drive just yet. Nor have I
implemented save states or cheat code support into the MS/GG cores yet.
The latter is next on my list.
2017-02-21 22:07:33 +11:00
Tim Allen
d76c0c7e82 Update to v102r08 release.
byuu says:

Changelog:

  - PCE: restructured VCE, VDCs to run one scanline at a time
  - PCE: bound VDCs to 1365x262 timing (in order to decouple the VDCs
    from the VCE)
  - PCE: the two changes above allow save states to function; also
    grants a minor speed boost
  - PCE: added cheat code support (uses 21-bit bus addressing; compare
    byte will be useful here)
  - 68K: fixed `mov *,ccr` to read two bytes instead of one [Cydrak]
  - Z80: emulated /BUSREQ, /BUSACK; allows 68K to suspend the Z80
    [Cydrak]
  - MD: emulated the Z80 executing instructions [Cydrak]
  - MD: emulated Z80 interrupts (triggered during each Vblank period)
    [Cydrak]
  - MD: emulated Z80 memory map [Cydrak]
  - MD: added stubs for PSG, YM2612 accesses [Cydrak]
  - MD: improved bus emulation [Cydrak]

The PCE core is pretty much ready to go. The only major feature missing
is FM modulation.

The Mega Drive improvements let us start to see the splash screens for
Langrisser II, Shining Force, Shining in the Darkness. I was hoping I
could get them in-game, but no such luck. My Z80 implementation is
probably flawed in some way ... now that I think about it, I believe I
missed the BusAPU::reset() check for having been granted access to the
Z80 first. But I doubt that's the problem.

Next step is to implement Cydrak's PSG core into the Master System
emulator. Once that's in, I'm going to add save states and cheat code
support to the Master System core.

Next, I'll add the PSG core into the Mega Drive. Then I'll add the
'easy' PCM part of the YM2612. Then the rest of the beastly YM2612 core.
Then finally, cap things off with save state and cheat code support.

Should be nearing a new release at that point.
2017-02-20 19:13:10 +11:00
Tim Allen
7c9b78b7bb Update to v102r07 release.
byuu says:

Changelog:

  - PCE: emulated PSG volume controls (vastly enhances audio quality)
  - PCE: emulated PSG noise as a square wave (somewhat enhances audio
    quality)
  - PCE: added save state support (currently broken and deadlocks the
    emulator though)

Thankfully, MAME had some rather easy to read code on how the volume
adjustment works, which they apparently ripped out of expired patents.
Hooray!

The two remaining sound issues are:

1. the random number generator for the noise channel is definitely not
hardware accurate. But it won't affect the sound quality at all. You'd
only be able to tell the difference by looking at hex bytes of a stream
rip.
2. I have no clue how to emulate the LFO (frequency modulation). A comment
in MAME's code (they also don't emulate it) advises that they aren't
aware of any games that even use it. But I'm there has to be at least one?

Given LFO not being used, and the RNG not really mattering all that much
... the sound's pretty close to perfect now.
2017-02-13 10:09:03 +11:00
Tim Allen
fa6cbac251 Update to v102r06 release.
byuu says:

Changelog:

  - added higan/emulator/platform.hpp (moved out Emulator::Platform from
    emulator/interface.hpp)
  - moved gmake build paramter to nall/GNUmakefile; both higan and
    icarus use it now
  - added build=profile mode
  - MD: added the region select I/O register
  - MD: started to add region selection support internally (still no
    external select or PAL support)
  - PCE: added cycle stealing when reading/writing to the VDC or VCE;
    and when using ST# instructions
  - PCE: cleaned up PSG to match the behavior of Mednafen (doesn't
    improve sound at all ;_;)
      - note: need to remove loadWaveSample, loadWavePeriod
  - HuC6280: ADC/SBC decimal mode consumes an extra cycle; does not set
    V flag
  - HuC6280: block transfer instructions were taking one cycle too many
  - icarus: added code to strip out PC Engine ROM headers
  - hiro: added options support to BrowserDialog

The last one sure ended in failure. The plan was to put a region
dropdown directly onto hiro::BrowserDialog, and I had all the code for
it working. But I forgot one important detail: the system loads
cartridges AFTER powering on, so even though I could technically change
the system region post-boot, I'd rather not do so.

So that means we have to know what region we want before we even select
a game. Shit.
2017-02-11 10:56:42 +11:00
Tim Allen
bf70044edc Update to v102r05 release.
byuu says:

Changelog:

  - higan: added Makefile option,
    `build=(release|debug|instrument|optimize)` , defaults to release
  - PCE: added preliminary PSG (sound) emulation

The Makefile thing is just to make it easier to build debug releases
without having to hand-edit the Makefile. Just say "gmake build=debug"
and you'll get -g, otherwise you'll get -O3 -s. I'll probably start
adding these build= blocks to my other projects. Or maybe I'll put it
into nall, in which case release will need a different name ... a stable
-01, and a fast -03 mode. I also want to add a mode to generate
profiling information (via gprof.)

Unfortunately, the existing documentation on the PCE's PSG is as
barebones as humanly possible.

Right now, I support waveform mode, direct D/A mode, and noise
generation mode. However for noise, I'm not actually generating a proper
square wave, and I don't know the PRNG algorithm used for choosing the
random values. So for now, I'm just feeding in nall::random() values to
it.

I'm also not sure about the noise mode's frequency counter. Magic Kit is
implying it's 64*~frequency, but that results in an 11-bit period. It
seems only logical that we'd want a 12-bit period. So my guess is that
it's actually 12-bit, and halfway through it alternates between two
randomly generated values every 32 samples, and the two values are
generated every time the period hits zero.

Next up, it's not clear when the period counter is reloaded, either for
the waveform or the noise mode. So for now, when enabling the channel, I
reload the waveform period. And when enabling noise mode, I reload the
noise period. I don't know if you need to do it when writing to the
frequency registers or not.

Next, it's not clear whether the period is a decrement-and-compare, or a
compare-and-decrement, and whether we reload with frequency,
frequency-1, or frequency+1. There's this cryptic note in
pcetext.txt:

> The PSG channel frequency is 12 bits, $001 is the highest frequency,
> $FFF is the next to lowest frequency, and $000 is the lowest frequency.

As best I can tell, he's trying to say that it's decrement-and-compare.

Whatever the case, there's periodic popping noises every few seconds. I
thought it might be because this is the first system with a fractional
sampling rate (~3.57MHz), but rounding the frequency to a whole number
doesn't help at all, and emulator/audio should be able to handle
fractional resampling rates anyway.

The popping noises could also be due to PSG writes being cycle-timed,
and my HuC6280 cycle timings not being very great yet. The PSG has no
kind of interrupts, so I think careful timing is the only way to do
certain things, especially D/A mode.

Next up, I really don't understand the frequency modulation mode at all.
I don't have any idea whatsoever how to support that. It also has a
frequency value that we'll need to understand how the period works and
reloads. Basic idea though is the channel 1 output turns into a value to
modulate channel 0's frequency by, and channel 1's output gets muted.

Next up, I don't know how the volume controls work at all. There's a
master volume left+right, per-channel volume left+right, and per-channel
overall volume. The documentation lists their effects in terms of
decibels. I have no fucking clue how to turn decibels into multiply-by
values. Let alone how to stack THREE levels of audio volume controls
>_>

Next, it looks like the output is always 5-bit unsigned per-channel, but
there's also all the volume adjustments. So I don't know the final
bit-depth of the final output to normalize the value into a signed
floating point value between -1.0 and +1.0. So for now, half the
potential speaker range (anything below zero) isn't used in the
generated output.

As bad as all this sounds, and it is indeed bad ... the audio's about
~75% correct, so you can definitely play games like this, it just won't
be all that much fun.
2017-02-10 08:56:59 +11:00
Tim Allen
ee7662a8be Update to v102r04 release.
byuu says:

Changelog:
  - Super Game Boy support is functional once again
  - new GameBoy::SuperGameBoyInterface class
  - system.(dmg,cgb,sgb) is now Model::(Super)GameBoy(Color) ala the PC
    Engine
  - merged WonderSwanInterface, WonderSwanColorInterface shared
    functions to WonderSwan::Interface
  - merged GameBoyInterface, GameBoyColorInterface shared functions to
    GameBoy::Interface
  - Interface::unload() now calls Interface::save() for Master System,
    Game Gear, Mega Drive, PC Engine, SuperGrafx
  - PCE: emulated PCE-CD backup RAM; stored per-game as save.ram (2KiB
    file)
      - this means you can now save your progress in games like Neutopia
      - the PCE-CD I/O registers like BRAM write protect are not
        emulated yet
  - PCE: IRQ sources now hold the IRQ line state, instead of the CPU
    holding it
      - this fixes most SuperGrafx games, which were fighting over the
        VDC IRQ line previously
  - PCE: CPU I/O $14xx should return the pending IRQ bits even if IRQs
    are disabled
  - PCE: VCE and the VDCs now synchronize to each other; fixes pixel
    widths in all games
  - PCE: greatly increased the accuracy of the VPC priority selection
    code (windows may be buggy still)
  - HuC6280: PLA, PLX, PLY should set Z, N flags; fixes many game bugs
    [Jonas Quinn]

The big thing I wanted to do was enslave the VDC(s) to the VCE. But
unfortunately, I forgot about the asynchronous DMA channels that each
VDC supports, so this isn't going to be possible I'm afraid.

In the most demanding case, Daimakaimura in-game, we're looking at 85fps
on my Xeon E3 1276v3. So ... not great, and we don't even have sound
connected yet.

We are going to have to profile and optimize this code once sound
emulation and save states are in.

Basically, think of it like this: the VCE, VDC0, and VDC1 all have the
same overhead, scheduling wise (which is the bulk of the performance
loss) as the dot-renderer for the SNES core. So it's like there's three
bsnes-accuracy PPU threads running just for video.

-----

Oh, just a fair warning ... the hooks for the SGB are a work in
progress.

If anyone is working on higan or a fork and want to do something similar
to it, don't use it as a template, at least not yet.

Right now, higan looks like this:

  - Emulator::Video handles the platform→videoRefresh calls
  - Emulator::Audio handles the platform→audioSample calls
  - each core hard-codes the platform→inputPoll, inputRumble calls
  - each core hard-codes calls to path, open, load to process files
  - dipSettings and notify are specialty hacks, neither are even hooked
    up right now to anything

With the SGB, it's an emulation core inside an emulation core, so
ideally you want to hook all of those functions. Emulator::Video and
Emulator::Audio aren't really abstractions over that, as the GB core
calls them and we have to special case not calling them in SGB mode.

The path, open, load can be implemented without hooks, thanks to the UI
only using one instance of Emulator::Platform for all cores. All we have
to do is override the folder path ID for the "Game Boy.sys" folder, so
that it picks "Super Game Boy.sfc/" and loads its boot ROM instead.
That's just a simple argument to GameBoy::System::load() and we're done.

dipSettings, notify and inputRumble don't matter. But we do also have to
hook inputPoll as well.

The nice idea would be for SuperFamicom::ICD2 to inherit from
Emulator::Platform and provide the desired functions that we need to
overload. After that, we'd just need the GB core to keep an abstraction
over the global Emulator::platform\* handle, to select between the UI
version and the SFC::ICD2 version.

However ... that doesn't work because of Emulator::Video and
Emulator::Audio. They would also have to gain an abstraction over
Emulator::platform\*, and even worse ... you'd have to constantly swap
between the two so that the SFC core uses the UI, and the GB core uses
the ICD2.

And so, for right now, I'm checking Model::SuperGameBoy() -> bool
everywhere, and choosing between the UI and ICD2 targets that way. And
as such, the ICD2 doesn't really need Emulator::Platform inheritance,
although it certainly could do that and just use the functions it needs.

But the SGB is even weirder, because we need additional new signals
beyond just Emulator::Platform, like joypWrite(), etc.

I'd also like to work on the Emulator::Stream for the SGB core. I don't
see why we can't have the GB core create its own stream, and let the
ICD2 just use that instead. We just have to be careful about the ICD2's
CPU soft reset function, to make sure the GB core's Stream object
remains valid. What I think that needs is a way to release an
Emulator::Stream individually, rather than calling
Emulator::Audio::reset() to do it. They are shared\_pointer objects, so
I think if I added a destructor function to remove it from
Emulator::Audio::streams, then that should work.
2017-01-26 12:06:06 +11:00
Tim Allen
186f008574 Update to v102r03 release.
byuu says:

Changelog:

  - PCE: split VCE from VDC
  - HuC6280: changed bus from (uint21 addr) to (uint8 bank, uint13 addr)
  - added SuperGrafx emulation (adds secondary VDC, plus new VPC)

The VDC now has no concept of the actual display raster timing, and
instead is driven by Vpulse (start of frame) and Hpulse (start of
scanline) signals from the VCE. One still can't render the start of the
next scanline onto the current scanline through overly aggressive
timings, but it shouldn't be too much more difficult to allow that to
occur now. This process incurs quite a major speed hit, so low-end
systems with Atom CPUs can't run things at 60fps anymore.

The timing needs a lot of work. The pixels end up very jagged if the VCE
doesn't output batches of 2-4 pixels at a time. But this should not be a
requirement at all, so I'm not sure what's going wrong there.

Yo, Bro and the 512-width mode of TV Sports Basketball is now broken as
a result of these changes, and I'm not sure why.

To load SuperGrafx games, you're going to have to change the .pce
extensions to .sg or .sgx. Or you can manually move the games from the
PC Engine folder to the SuperGrafx folder and change the game folder
extensions. I have no way to tell the games apart. Mednafen uses CRC32
comparisons, and I may consider that since there's only five games, but
I'm not sure yet.

The only SuperGrafx game that's playable right now is Aldynes. And the
priorities are all screwed up. I don't understand how the windows or the
priorities work at all from sgxtech.txt, so ... yeah. It's pretty
broken, but it's a start.

I could really use some help with this, as I'm very lost right now with
rendering :/

-----

Note that the SuperGrafx is technically its own system, it's not an
add-on.

As such, I'm giving it a separate .sys folder, and a separate library.

There's debate over how to name this thing. "SuperGrafx" appears more
popular than "Super Grafx". And you might also call it the "PC Engine
SuperGrafx", but I decided to leave off the prefix so it appears more
distinct.
2017-01-24 08:18:54 +11:00
Tim Allen
bdc100e123 Update to v102r02 release.
byuu says:

Changelog:

  - I caved on the `samples[] = {0.0}` thing, but I'm very unhappy about it
      - if it's really invalid C++, then GCC needs to stop accepting it
        in strict `-std=c++14` mode
  - Emulator::Interface::Information::resettable is gone
  - Emulator::Interface::reset() is gone
  - FC, SFC, MD cores updated to remove soft reset behavior
  - split GameBoy::Interface into GameBoyInterface,
    GameBoyColorInterface
  - split WonderSwan::Interface into WonderSwanInterface,
    WonderSwanColorInterface
  - PCE: fixed off-by-one scanline error [hex_usr]
  - PCE: temporary hack to prevent crashing when VDS is set to < 2
  - hiro: Cocoa: removed (u)int(#) constants; converted (u)int(#)
    types to (u)int_(#)t types
  - icarus: replaced usage of unique with strip instead (so we don't
    mess up frameworks on macOS)
  - libco: added macOS-specific section marker [Ryphecha]

So ... the major news this time is the removal of the soft reset
behavior. This is a major!! change that results in a 100KiB diff file,
and it's very prone to accidental mistakes!! If anyone is up for
testing, or even better -- looking over the code changes between v102r01
and v102r02 and looking for any issues, please do so. Ideally we'll want
to test every NES mapper type and every SNES coprocessor type by loading
said games and power cycling to make sure the games are all cleanly
resetting. It's too big of a change for me to cover there not being any
issues on my own, but this is truly critical code, so yeah ... please
help if you can.

We technically lose a bit of hardware documentation here. The soft reset
events do all kinds of interesting things in all kinds of different
chips -- or at least they do on the SNES. This is obviously not ideal.
But in the process of removing these portions of code, I found a few
mistakes I had made previously. It simplifies resetting the system state
a lot when not trying to have all the power() functions call the reset()
functions to share partial functionality.

In the future, the goal will be to come up with a way to add back in the
soft reset behavior via keyboard binding as with the Master System core.
What's going to have to happen is that the key binding will have to send
a "reset pulse" to every emulated chip, and those chips are going to
have to act independently to power() instead of reusing functionality.
We'll get there eventually, but there's many things of vastly greater
importance to work on right now, so it'll be a while. The information
isn't lost ... we'll just have to pull it out of v102 when we are ready.

Note that I left the SNES reset vector simulation code in, even though
it's not possible to trigger, for the time being.

Also ... the Super Game Boy core is still disconnected. To be honest, it
totally slipped my mind when I released v102 that it wasn't connected
again yet. This one's going to be pretty tricky to be honest. I'm
thinking about making a third GameBoy::Interface class just for SGB, and
coming up with some way of bypassing platform-> calls when in this
mode.
2017-01-23 08:04:26 +11:00
Tim Allen
c40e9754bc Update to v102r01 release.
byuu says:

Changelog:

  - MS, MD, PCE: remove controllers from scheduler in destructor
    [hex_usr]
  - PCE: no controller should return all bits set (still causing errant
    key presses when swapping gamepads)
  - PCE: emulate MDR for hardware I/O $0800-$17ff
  - PCE: change video resolution to 1140x242
  - PCE: added tertiary background Vscroll register (secondary cache)
  - PCE: create classes out of VDC VRAM, SATB, CRAM for cleaner access
    and I/O registers
  - PCE: high bits of CRAM read should be set
  - PCE: partially emulated VCE display registers: color frequency, HDS,
    HDW, VDS, VDW
  - PCE: 32-width sprites now split to two 16-width sprites to handle
    overflow properly
  - PCE: hopefully emulated sprite zero hit correctly (it's not well
    documented, and not often used)
  - PCE: trigger line coincidence interrupts during the previous
    scanline's Hblank period
  - tomoko: raise viewport from 320x240 to 326x242 to accommodate PC
    Engine's max resolution
  - nall: workaround for Clang compilation bug that can't figure out
    that a char is an integral data type
2017-01-22 11:33:36 +11:00
Tim Allen
ae5968cfeb Update to v102 release.
byuu says (in the public announcement):

This release adds very preliminary emulation of the Sega Master System
(Mark III), Sega Game Gear, Sega Mega Drive (Genesis), and NEC PC Engine
(Turbografx-16). These cores do not yet offer sound emulation, save
states or cheat codes.

I'm always very hesitant to release a new emulation core in its alpha
stages, as in the past this has resulted in lasting bad impressions
of cores that have since improved greatly. For instance, the Game Boy
Advance emulation offered today is easily the second most accurate around,
yet it is still widely judged by its much older alpha implementation.

However, it's always been tradition with higan to not hold onto code
in secret. Rather than delay future releases for another year or two,
I'll put my faith in you all to understand that the emulation of these
systems will improve over time.

I hope that by releasing things as they are now, I might be able to
receive some much needed assistance in improving these cores, as the
documentation for these new systems is very much less than ideal.

byuu says (in the WIP forum):

Changelog:

  - PCE: latch background scroll registers (fixes Neutopia scrolling)
  - PCE: clip background attribute table scrolling (fixes Blazing Lazers
    scrolling)
  - PCE: support background/sprite enable/disable bits
  - PCE: fix large sprite indexing (fixes Blazing Lazers title screen
    sprites)
  - HuC6280: wrap zeropage accesses to never go beyond $20xx
  - HuC6280: fix alternating addresses for block move instructions
    (fixes Neutopia II)
  - HuC6280: block move instructions save and restore A,X,Y registers
  - HuC6280: emulate BCD mode (may not be 100% correct, based on SNES
    BCD) (fixes Blazing Lazers scoring)
2017-01-20 08:01:15 +11:00
Tim Allen
b03563426f Update to v101r35 release.
byuu says:

Changelog:
  - PCE: added 384KB HuCard ROM mirroring mode
  - PCE: corrected D-pad polling order
  - PCE: corrected palette color ordering (GRB, not RGB -- yes,
    seriously)
  - PCE: corrected SATB DMA -- should write to SATB, not to VRAM
  - PCE: broke out Background, Sprite VDC settings to separate
    subclasses
  - PCE: emulated VDC backgrounds
  - PCE: emulated VDC sprites
  - PCE: emulated VDC sprite overflow, collision interrupts
  - HuC6280: fixed disassembler output for STi instructions
  - HuC6280: added missing LastCycle check to interrupt()
  - HuC6280: fixed BIT, CMP, CPX, CPY, TRB, TSB, TST flag testing and
    result
  - HuC6280: added extra cycle delays to the block move instructions
  - HuC6280: fixed ordering for flag set/clear instructions (happens
    after LastCycle check)
  - HuC6280: removed extra cycle from immediate instructions
  - HuC6280: fixed indirectLoad, indirectYStore absolute addressing
  - HuC6280: fixed BBR, BBS zeropage value testing
  - HuC6280: fixed stack push/pull direction

Neutopia looks okay until the main title screen, then there's some
gibberish on the bottom. The game also locks up with some gibberish once
you actually start a new game. So, still not playable just yet =(
2017-01-19 19:38:57 +11:00
Tim Allen
f500426158 Update to v101r34 release.
byuu says:

Changelog:

  - PCE: emulated gamepad polling
  - PCE: emulated CPU interrupt sources
  - PCE: emulated timer
  - PCE: smarter emulation of ST0,ST1,ST2 instructions
  - PCE: better structuring of CPU, VDP IO registers
  - PCE: connected palette generation to the interface
  - PCE: emulated basic VDC timing
  - PCE: emulated VDC Vblank, Coincidence, and DMA completion IRQs
  - PCE: emulated VRAM, SATB DMA transfers
  - PCE: emulated VDC I/O registers

Everything I've implemented today likely has lots of bugs, and is
untested for obvious reasons.

So basically, after I fix many horrendous bugs, it should now be
possible to implement the VDC and start getting graphical output.
2017-01-17 08:02:56 +11:00
Tim Allen
8499c64756 Update to v101r33 release.
byuu says:

Changelog:

  - PCE: HuC6280 core completed

There's bound to be a countless stream of bugs, and the cycle counts are
almost certainly not exact yet, but ... all instructions are implemented.

So at this point, I can start comparing trace logs against Mednafen's
debugger output.

Of course, we're very likely to immediately slam into a wall of needing
I/O registers implemented for the VDC in order to proceed further.
2017-01-15 11:58:47 +11:00
Tim Allen
26bd7590ad Update to v101r32 release.
byuu says:

Changelog:

  - SMS: fixed controller connection bug
  - SMS: fixed Z80 reset bug
  - PCE: emulated HuC6280 MMU
  - PCE: emulated HuC6280 RAM
  - PCE: emulated HuCard ROM reading
  - PCE: implemented 178 instructions
  - tomoko: removed "soft reset" functionality
  - tomoko: moved "power cycle" to just above "unload" option

I'm not sure of the exact number of HuC6280 instructions, but it's less
than 260.

Many of the ones I skipped are HuC6280-originals that I don't know how
to emulate just yet.

I'm also really unsure about the zero page stuff. I believe we should be
adding 0x2000 to the addresses to hit page 1, which is supposed to be
mapped to the zero page (RAM). But when I look at turboEMU's source, I
have no clue how the hell it could possibly be doing that. It looks to
be reading from page 0, which is almost always ROM, which would be ...
really weird.

I also don't know if I've emulated the T mode opcodes correctly or not.
The documentation on them is really confusing.
2017-01-14 10:59:38 +11:00
Tim Allen
21ee597aae Add a .gitlab-ci.yml to automate WIP builds. 2017-01-13 12:18:25 +11:00
Tim Allen
bf90bdfcc8 Update to v101r31 release.
byuu says:

Changelog:

  - converted Emulator::Interface::Bind to Emulator::Platform
  - temporarily disabled SGB hooks
  - SMS: emulated Game Gear palette (latching word-write behavior not
    implemented yet)
  - SMS: emulated Master System 'Reset' button, Game Gear 'Start' button
  - SMS: removed reset() functionality, driven by the mappable input now
    instead
  - SMS: split interface class in two: one for Master System, one for
    Game Gear
  - SMS: emulated Game Gear video cropping to 160x144
  - PCE: started on HuC6280 CPU core—so far only registers, NOP
    instruction has been implemented

Errata:

  - Super Game Boy support is broken and thus disabled
  - if you switch between Master System and Game Gear without
    restarting, bad things happen:
      - SMS→GG, no video output on the GG
      - GG→SMS, no input on the SMS

I'm not sure what's causing the SMS\<-\>GG switch bug, having a hard
time debugging it. Help would be very much appreciated, if anyone's up
for it. Otherwise I'll keep trying to track it down on my end.
2017-01-13 12:15:45 +11:00
Tim Allen
0ad70a30f8 Update to v101r30 release.
byuu says:

Changelog:

  - SMS: added cartridge ROM/RAM mirroring (fixes Alex Kidd)
  - SMS: fixed 8x16 sprite mode (fixes Wonder Boy, Ys graphics)
  - Z80: emulated "ex (sp),hl" instruction
  - Z80: fixed INx NF (should be set instead of cleared)
  - Z80: fixed loop condition check for CPxR, INxR, LDxR, OTxR (fixes
    walking in Wonder Boy)
  - SFC: removed Debugger and sfc/debugger.hpp
  - icarus: connected MS, GG, MD importing to the scan dialog
  - PCE: added emulation skeleton to higan and icarus

At this point, Master System games are fairly highly compatible, sans
audio. Game Gear games are running, but I need to crop the resolution
and support the higher color palette that they can utilize. It's really
something else the way they handled the resolution shrink on that thing.

The last change is obviously going to be the biggest news.

I'm very well aware it's not an ideal time to start on a new emulation
core, with the MS and MD cores only just now coming to life with no
audio support.

But, for whatever reason, my heart's really set on working on the PC
Engine. I wanted to write the final higan skeleton core, and get things
ready so that whenever I'm in the mood to work on the PCE, I can do so.

The skeleton is far and away the most tedious and obnoxious part of the
emulator development, because it's basically all just lots of
boilerplate templated code, lots of new files to create, etc.

I really don't know how things are going to proceed ... but I can say
with 99.9% certainty that this will be the final brand new core ever
added to higan -- at least one written by me, that is. This was
basically the last system from my childhood that I ever cared about.
It's the last 2D system with games that I really enjoy playing. No other
system is worth dividing my efforts and reducing the quality and amount
of time to work on the systems I have.

In the future, there will be potential for FDS, Mega CD and PCE-CD
support. But those will all be add-ons, and they'll all be really
difficult and challenge the entire design of higan's UI (it's entirely
cartridge-driven at this time.) None of them will be entirely new cores
like this one.
2017-01-12 07:27:30 +11:00
Tim Allen
79c83ade70 Update to v101r29 release.
byuu says:

Changelog:

  - SMS: background VDP clips partial tiles on the left (math may not be
    right ... it's hard to reason about)
  - SMS: fix background VDP scroll locks
  - SMS: fix VDP sprite coordinates
  - SMS: paint black after the end of the visible display
      - todo: shouldn't be a brute force at the end of the main VDP
        loop, should happen in each rendering unit
  - higan: removed emulator/debugger.hpp
  - higan: removed privileged: access specifier
  - SFC: removed debugger hooks
      - todo: remove sfc/debugger.hpp
  - Z80: fixed disassembly of (fd,dd) cb (displacement) (opcode)
    instructions
  - Z80: fix to prevent interrupts from firing between ix/iy prefixes
    and opcodes
      - todo: this is a rather hacky fix that could, if exploited, crash
        the stack frame
  - Z80: fix BIT flags
  - Z80: fix ADD hl,reg flags
  - Z80: fix CPD, CPI flags
  - Z80: fix IND, INI flags
  - Z80: fix INDR, INIT loop flag check
  - Z80: fix OUTD, OUTI flags
  - Z80: fix OTDR, OTIR loop flag check
2017-01-10 08:27:13 +11:00
Tim Allen
a3aea95e6b Update to v101r28 release.
byuu says:

Changelog:

  - SMS: emulated the remaining 240 instructions in the (0xfd, 0xdd)
    0xcb (displacement) (opcode) set
      - 1/8th of these were "legal" instructions, and apparently games
        use them a lot
  - SMS: emulated the standard gamepad controllers
      - reset button not emulated yet

The reset button is tricky. In every other case, reset is a hardware
thing that instantly reboots the entire machine.

But on the SMS, it's more like a gamepad button that's attached to the
front of the device. When you press it, it fires off a reset vector
interrupt and the gamepad polling routine lets you query the status of
the button.

Just having a reset option in the "Master System" hardware menu is not
sufficient to fully emulate the behavior. Even more annoying is that the
Game Gear doesn't have such a button, yet the core information structs
aren't flexible enough for the Master System to have it, and the Game
Gear to not have it, in the main menu. But that doesn't matter anyway,
since it won't work having it in the menu for the Master System.

So as a result, I'm going to have to have a new "input device" called
"Hardware" that has the "Reset" button listed under there. And for the
sake of consistency, I'm not sure if we should treat the other systems
the same way or not :/
2017-01-09 07:55:02 +11:00
Tim Allen
569f5abc28 Update to v101r27 release.
byuu says:

Changelog:

  - SMS: emulated the generic Sega memory mapper (none of the more
    limited forms of it yet)
      - (missing ROM shift, ROM write enable emulation -- no commercial
        games use either, though)
  - SMS: bus I/O returns 0xff instead of 0x00 so games don't think every
    key is being pressed at once
      - (this is a hack until I implement proper controller pad reading)
  - SMS: very limited protection against reading/writing past the end of
    ROM/RAM (todo: should mirror)
  - SMS: VDP background HSCROLL subtracts, rather than adds, to the
    offset (unlike VSCROLL)
  - SMS: VDP VSCROLL is 9-bit, modulates voffset+vscroll to 224 in
    192-line mode (32x28 tilemap)
  - SMS: VDP tiledata for backgrounds and sprites use `7-(x&7)` rather
    than `(x&7)`
  - SMS: fix output color to be 6-bit rather than 5-bit
  - SMS: left clip uses register `#7`, not palette color `#7`
      - (todo: do we want `color[reg7]` or `color[16 + reg7]`?)
  - SMS: refined handling of 0xcb, 0xed prefixes in the Z80 core and its
    disassembler
  - SMS: emulated (0xfd, 0xdd) 0xcb opcodes 0x00-0x0f (still missing
    0x10-0xff)
  - SMS: fixed 0xcb 0b-----110 opcodes to use direct HL and never allow
    (IX,IY)+d
  - SMS: fixed major logic bug in (IX,IY)+d displacement
      - (was using `read(x)` instead of `operand()` for the displacement
        byte fetch before)
  - icarus: fake there always being 32KiB of RAM in all SMS cartridges
    for the time being
      - (not sure how to detect this stuff yet; although I've read it's
        not even really possible `>_>`)

TODO: remove processor/z80/dissassembler.cpp code block at line 396 (as it's unnecessary.)

Lots of commercial games are starting to show trashed graphical output now.
2017-01-06 19:11:38 +11:00
Tim Allen
5bdf55f08f Update to v101r25 release.
byuu says:

Changelog:

  - SMS: emulated VDP mode 4 graphical output (background, sprites)
  - added $(windres) to icarus as well

I'm sure the VDP emulation is still really, really buggy, but
essentially I handle:

  - mode 4 rendering
  - background scrolling
  - background hscroll lock
  - background vscroll lock
  - background nametable relocation
  - sprite nametable relocation
  - sprite tiledata relocation
  - sprite 192-line y=0xd0 edge case (end sprite rendering)
  - sprite 8-pixel x-coordinate displacement
  - sprite extended size (height only in mode 4)
  - sprite overflow
  - sprite collision
  - left column masking
  - display disable
  - backdrop color
  - 192, 224, 240 height

I do not support:

  - mode 2 rendering
  - sprite zoom
  - disallowing 240 height in NTSC mode
  - PAL mode
  - probably lots more
2016-12-30 18:24:35 +11:00
Tim Allen
e30780bb72 Update to v101r25 release.
byuu says:

Changelog:

  - Makefile: added $(windres), -lpthread to Windows port
  - GBA: WAITCNT.prefetch is not writable (should fix Donkey Kong: King
    of Swing) \[endrift\]
  - SMS: fixed hcounter shift value \[hex\_usr\]
  - SMS: emulated interrupts (reset button isn't hooked up anywhere, not
    sure where to put it yet)

This WIP actually took a really long time because the documentation on
SMS interrupts was all over the place. I'm hoping I've emulated them
correctly, but I honestly have no idea. It's based off my best
understanding from four or five different sources. So it's probably
quite buggy.

However, a few interrupts fire in Sonic the Hedgehog, so that's
something to start with. Now I just have to hope I've gotten some games
far enough in that I can start seeing some data in the VDP VRAM. I need
that before I can start emulating graphics mode 4 to get some actual
screen output.

Or I can just say to hell with it and use a "Hello World" test ROM.
That'd probably be smarter.
2016-12-26 23:11:08 +11:00
Tim Allen
bab2ac812a Update to v101r24 release.
byuu says:

Changelog:

  - SMS: extended bus mapping of in/out ports: now decoding them fully
    inside ms/bus
  - SMS: moved Z80 disassembly code from processor/z80 to ms/cpu
    (cosmetic)
  - SMS: hooked up non-functional silent PSG sample generation, so I can
    cap the framerate at 60fps
  - SMS: hooked up the VDP main loop: 684 clocks/scanline, 262
    scanlines/frame (no PAL support yet)
  - SMS: emulated the VDP Vcounter and Hcounter polling ... hopefully
    it's right, as it's very bizarre
  - SMS: emulated VDP in/out ports (data read, data write, status read,
    control write, register write)
  - SMS: decoding and caching all VDP register flags (variable names
    will probably change)
  - nall: \#undef IN on Windows port (prevent compilation warning on
    processor/z80)

Watching Sonic the Hedgehog, I can definitely see some VDP register
writes going through, which is a good sign.

Probably the big thing that's needed before I can get enough into the
VDP to start showing graphics is interrupt support. And interrupts are
never fun to figure out :/

What really sucks on this front is I'm flying blind on the Z80 CPU core.
Without a working VDP, I can't run any Z80 test ROMs to look for CPU
bugs. And the CPU is certainly too buggy still to run said test ROM
anyway. I can't find any SMS emulators with trace logging from reset.
Such logs vastly accelerate tracking down CPU logic bugs, so without
them, it's going to take a lot longer.
2016-12-17 22:31:34 +11:00
Tim Allen
1d7b674dd4 Update to v101r23 release.
byuu says:

This is a really tiny WIP. Just wanted to add the known fixes before I start debugging it against Mednafen in a fork.

Changelog:

  - Z80: fixed flag calculations on 8-bit ADC, ADD, SBC, SUB
  - Z80: fixed flag calculations on 16-bit ADD
  - Z80: simplified DAA logic \[AWJ\]
  - Z80: RETI sets IFF1=IFF2 (same as RETN)
2016-11-15 18:20:42 +11:00
Tim Allen
c2c957a9da Update to v101r22 release.
byuu says:

Changelog:
- Z80: all 25 remaining instructions implemented

Now onto the debugging ... :/
2016-11-01 22:42:25 +11:00
Tim Allen
8cf20dabbf Update to v101r21 release.
byuu says:

Changelog:

- Z80: emulated 83 new instructions
- Z80: timing improvements

DAA is a skeleton implementation to complete the normal opcode set. Also
worth noting that I don't know exactly what the hell RETI is doing,
so for now it acts like RET. RETN probably needs some special handling
besides just setting IFF1=IFF2 as well.

I'm now missing 24 ED-prefix instructions, plus DAA, for a total of 25
opcodes remaining. And then, of course, several weeks worth of debugging
all of the inevitable bugs in the core.
2016-11-01 08:10:33 +11:00
Tim Allen
2707c5316d Update to v101r20 release.
byuu says:

Changelog:
- Z80: emulated 272 new instructions
- hiro/GTK: fixed v101r19 Linux regression [thanks, SuperMikeMan!]
2016-10-29 11:33:30 +11:00
Tim Allen
f3e67da937 Update to v101r19 release.
byuu says:

Changelog:

-   added \~130 new PAL games to icarus (courtesy of Smarthuman
    and aquaman)
-   added all three Korean-localized games to icarus
-   sfc: removed SuperDisc emulation (it was going nowhere)
-   sfc: fixed MSU1 regression where the play/repeat flags were not
    being cleared on track select
-   nall: cryptography support added; will be used to sign future
    databases (validation will always be optional)
-   minor shims to fix compilation issues due to nall changes

The real magic is that we now have 25-30% of the PAL SNES library in
icarus!

Signing will be tricky. Obviously if I put the public key inside the
higan archive, then all anyone has to do is change that public key for
their own releases. And if you download from my site (which is now over
HTTPS), then you don't need the signing to verify integrity. I may just
put the public key on my site on my site and leave it at that, we'll
see.
2016-10-28 08:16:58 +11:00
Tim Allen
c6fc15f8d2 Update to v101r18 release.
byuu says:

Changelog:

  - added 30 new PAL games to icarus (courtesy of Mikerochip)
  - new version of libco no longer requires mprotect nor W|X permissions
  - nall: default C compiler to -std=c11 instead of -std=c99
  - nall: use `-fno-strict-aliasing` during compilation
  - updated nall/certificates (hopefully for the last time)
  - updated nall/http to newer coding conventions
  - nall: improve handling of range() function

I didn't really work on higan at all, this is mostly just a release
because lots of other things have changed.

The most interesting is `-fno-strict-aliasing` ... basically, it joins
`-fwrapv` as being "stop the GCC developers from doing *really* evil
shit that could lead to security vulnerabilities or instabilities."

For the most part, it's a ~2% speed penalty for higan. Except for the
Sega Genesis, where it's a ~10% speedup. I have no idea how that's
possible, but clearly something's going very wrong with strict aliasing
on the Genesis core.

So ... it is what it is. If you need the performance for the non-Genesis
cores, you can turn it off in your builds. But I'm getting quite sick of
C++'s "surprises" and clever compiler developers, so I'm keeping it on
in all of my software going forward.
2016-09-14 21:55:53 +10:00
Tim Allen
d6e9d94ec3 Update to v101r17 release.
byuu says:

Changelog:

  - Z80: added most opcodes between 0x00 and 0x3f (two or three hard
    ones missing still)
  - Z80: redid register declaration *again* to handle AF', BC', DE',
    HL' (ugggggh, the fuck? Alternate registers??)
      - basically, using `#define <register name>` values to get around
        horrendously awful naming syntax
  - Z80: improved handling of displace() so that it won't ever trigger
    on (BC) or (DE)
2016-09-06 23:53:14 +10:00
Tim Allen
2fbbccf985 Update to v101r16 release.
byuu says:

Changelog:

  - Z80: implemented 113 new instructions (all the easy
    LD/ADC/ADD/AND/OR/SBC/SUB/XOR ones)
  - Z80: used alternative to castable<To, With> type (manual cast inside
    instruction() register macros)
  - Z80: debugger: used register macros to reduce typing and increase
    readability
  - Z80: debugger: smarter way of handling multiple DD/FD prefixes
    (using gotos, yay!)
  - ruby: fixed crash with Windows input driver on exit (from SuperMikeMan)

I have no idea how the P/V flag is supposed to work on AND/OR/XOR, so
that's probably wrong for now. HALT is also mostly a dummy function for
now. But I typically implement those inside instruction(), so it
probably won't need to be changed? We'll see.
2016-09-06 10:09:33 +10:00
Tim Allen
4c3f58150c Update to v101r15 release.
byuu says:

Changelog:

  - added (poorly-named) castable<To, With> template
  - Z80 debugger rewritten to make declaring instructions much simpler
  - Z80 has more instructions implemented; supports displacement on
    (IX), (IY) now
  - added `Processor::M68K::Bus` to mirror `Processor::Z80::Bus`
      - it does add a pointer indirection; so I'm not sure if I want to
        do this for all of my emulator cores ...
2016-09-04 23:51:27 +10:00
Tim Allen
d91f3999cc Update to v101r14 release.
byuu says:
Changelog:

  - rewrote the Z80 core to properly handle 0xDD (IX0 and 0xFD (IY)
    prefixes
  - added Processor::Z80::Bus as a new type of abstraction
  - all of the instructions implemented have their proper T-cycle counts
    now
  - added nall/certificates for my public keys

The goal of `Processor::Z80::Bus` is to simulate the opcode fetches being
2-read + 2-wait states; operand+regular reads/writes being 3-read. For
now, this puts the cycle counts inside the CPU core. At the moment, I
can't think of any CPU core where this wouldn't be appropriate. But it's
certainly possible that such a case exists. So this may not be the
perfect solution.

The reason for having it be a subclass of Processor::Z80 instead of
virtual functions for the MasterSystem::CPU core to define is due to
naming conflicts. I wanted the core to say `in(addr)` and have it take
the four clocks. But I also wanted a version of the function that didn't
consume time when called. One way to do that would be for the core to
call `Z80::in(addr)`, which then calls the regular `in(addr)` that goes to
`MasterSystem::CPU::in(addr)`. But I don't want to put the `Z80::`
prefix on all of the opcodes. Very easy to forget it, and then end up not
consuming any time. Another is to use uglier names in the
`MasterSystem::CPU` core, like `read_`, `write_`, `in_`, `out_`, etc. But,
yuck.

So ... yeah, this is an experiment. We'll see how it goes.
2016-09-03 21:26:04 +10:00
Tim Allen
7c96826eb0 Update to v101r13 release.
byuu says:

Changelog:

  - MS: added ms/bus
  - Z80: implemented JP/JR/CP/DI/IM/IN instructions
  - MD/VDP: added window layer emulation
  - MD/controller/gamepad: fixed d2,d3 bits (Altered Beast requires
    this)

The Z80 is definitely a lot nastier than the LR35902. There's a lot of
table duplication with HL→IX→IY; and two of them nest two levels deep
(eg FD CB xx xx), so the design may change as I implement more.
2016-08-27 14:48:21 +10:00
Tim Allen
5df717ff2a Update to v101r12 release.
byuu says:

Changelog:

  - new md/bus/ module for bus reads/writes
      - abstracts byte/word accesses wherever possible (everything but
        RAM; forces all but I/O to word, I/O to byte)
      - holds the system RAM since that's technically not part of the
        CPU anyway
  - added md/controller and md/system/peripherals
  - added emulation of gamepads
  - added stub PSG audio output (silent) to cap the framerate at 60fps
    with audio sync enabled
  - fixed VSRAM reads for plane vertical scrolling (two bugs here: add
    instead of sub; interlave plane A/B)
  - mask nametable read offsets (can't exceed 8192-byte nametables
    apparently)
  - emulated VRAM/VSRAM/CRAM reads from VDP data port
  - fixed sprite width/height size calculations
  - added partial emulation of 40-tile per scanline limitation (enough
    to fix Sonic's title screen)
  - fixed off-by-one sprite range testing
  - fixed sprite tile indexing
  - Vblank happens at Y=224 with overscan disabled
      - unsure what happens when you toggle it between Y=224 and Y=240
        ... probably bad things
  - fixed reading of address register for ADDA, CMPA, SUBA
  - fixed sign extension for MOVEA effect address reads
  - updated MOVEM to increment the read addresses (but not writeback)
    for (aN) mode

With all of that out of the way, we finally have Sonic the Hedgehog
(fully?) playable. I played to stage 1-2 and through the special stage,
at least. EDIT: yeah, we probably need HIRQs for Labyrinth Zone.

Not much else works, of course. Most games hang waiting on the Z80, and
those that don't (like Altered Beast) are still royally screwed. Tons of
features still missing; including all of the Z80/PSG/YM2612.

A note on the perihperals this time around: the Mega Drive EXT port is
basically identical to the regular controller ports. So unlike with the
Famicom and Super Famicom, I'm inheriting the exension port from the
controller class.
2016-08-22 08:11:24 +10:00
Tim Allen
f7ddbfc462 Update to v101r11 release.
byuu says:

Changelog:

  - 68K: fixed NEG/NEGX operand order
  - 68K: fixed bug in disassembler that was breaking trace logging
  - VDP: improved sprite rendering (still 100% broken)
  - VDP: added horizontal/vertical scrolling (90% broken)

Forgot:

  - 68K: fix extension word sign bit on indexed modes for disassembler
    as well
  - 68K: emulate STOP properly (use r.stop flag; clear on IRQs firing)

I'm really wearing out fast here. The Genesis documentation is somehow
even worse than Game Boy documentation, but this is a far more complex
system.

It's a massive time sink to sit here banging away at every possible
combination of how things could work, only to see no positive
improvements. Nothing I do seems to get sprites to do a goddamn thing.

squee says the sprite Y field is 10-bits, X field is 9-bits. genvdp says
they're both 10-bits. BlastEm treats them like they're both 10-bits,
then masks off the upper bit so it's effectively 9-bits anyway.

Nothing ever bothers to tell you whether the horizontal scroll values
are supposed to add or subtract from the current X position. Probably
the most basic detail you could imagine for explaining horizontal
scrolling and yet ... nope. Nothing.

I can't even begin to understand how the VDP FIFO functionality works,
or what the fuck is meant by "slots".

I'm completely at a loss as how how in the holy hell the 68K works with
8-bit accesses. I don't know whether I need byte/word handlers for every
device, or if I can just hook it right into the 68K core itself. This
one's probably the most major design detail. I need to know this before
I go and implement the PSG/YM2612/IO ports-\>gamepads/Z80/etc.

Trying to debug the 68K is murder because basically every game likes to
start with a 20,000,000-instruction reset phase of checksumming entire
games, and clearing out the memory as agonizingly slowly as humanly
possible. And like the ARM, there's too many registers so I'd need three
widescreen monitors to comfortably view the entire debugger output lines
onscreen.

I can't get any test ROMs to debug functionality outside of full games
because every **goddamned** test ROM coder thinks it's acceptable to tell
people to go fetch some toolchain from a link that died in the late '90s
and only works on MS-DOS 6.22 to build their fucking shit, because god
forbid you include a 32KiB assembled ROM image in your fucking archives.

... I may have to take a break for a while. We'll see.
2016-08-21 12:50:05 +10:00
Tim Allen
0b70a01b47 Update to v101r10 release.
byuu says:
Changelog:

  - 68K: MOVEQ is 8-bit signed
  - 68K: disassembler was print EOR for OR instructions
  - 68K: address/program-counter indexed mode had the signed-word/long
    bit backward
  - 68K: ADDQ/SUBQ #n,aN always works in long mode; regardless of size
  - 68K→VDP DMA needs to use `mode.bit(0)<<22|dmaSource`; increment by
    one instead of two
  - Z80: added registers and initial two instructions
  - MS: hooked up enough to load and start running games
      - Sonic the Hedgehog can execute exactly one instruction... whoo.
2016-08-20 00:11:26 +10:00
Tim Allen
4d2e17f9c0 Update to v101r09 release.
byuu says:

Sorry, two WIPs in one day. Got excited and couldn't wait.

Changelog:

  - ADDQ, SUBQ shouldn't update flags when targeting an address register
  - ADDA should sign extend effective address reads
  - JSR was pushing the PC too early
  - some improvements to 8-bit register reads on the VDP (still needs
    work)
  - added H/V counter reads to the VDP IO port region
  - icarus: added support for importing Master System and Game Gear ROMs
  - tomoko: added library sub-menus for each manufacturer
      - still need to sort Game Gear after Mega Drive somehow ...

The sub-menu system actually isn't all that bad. It is indeed a bit more
annoying, but not as annoying as I thought it was going to be. However,
it looks a hell of a lot nicer now.
2016-08-18 08:05:50 +10:00
Tim Allen
043f6a8b33 Update to v101r08 release.
byuu says:

Changelog:

  - 68K: fixed read-modify-write instructions
  - 68K: fixed ADDX bug (using wrong target)
  - 68K: fixed major bug with SUB using wrong argument ordering
  - 68K: fixed sign extension when reading address registers from
    effective addressing
  - 68K: fixed sign extension on CMPA, SUBA instructions
  - VDP: improved OAM sprite attribute table caching behavior
  - VDP: improved DMA fill operation behavior
  - added Master System / Game Gear stubs (needed for developing the Z80
    core)
2016-08-17 22:31:22 +10:00
Tim Allen
ffd150735b Update to v101r07 release.
byuu says:

Added VDP sprite rendering. Can't get any games far enough in to see if
it actually works. So in other words, it doesn't work at all and is 100%
completely broken.

Also added 68K exceptions and interrupts. So far only the VDP interrupt
is present. It definitely seems to be firing in commercial games, so
that's promising. But the implementation is almost certainly completely
wrong. There is fuck all of nothing for documentation on how interrupts
actually work. I had to find out the interrupt vector numbers from
reading the comments from the Sonic the Hedgehog disassembly. I have
literally no fucking clue what I0-I2 (3-bit integer priority value in
the status register) is supposed to do. I know that Vblank=6, Hblank=4,
Ext(gamepad)=2. I know that at reset, SR.I=7. I don't know if I'm
supposed to block interrupts when I is >, >=, <, <= to the interrupt
level. I don't know what level CPU exceptions are supposed to be.

Also implemented VDP regular DMA. No idea if it works correctly since
none of the commercial games run far enough to use it. So again, it's
horribly broken for usre.

Also improved VDP fill mode. But I don't understand how it takes
byte-lengths when the bus is 16-bit. The transfer times indicate it's
actually transferring at the same speed as the 68K->VDP copy, strongly
suggesting it's actually doing 16-bit transfers at a time. In which case,
what happens when you set an odd transfer length?

Also, both DMA modes can now target VRAM, VSRAM, CRAM. Supposedly there's
all kinds of weird shit going on when you target VSRAM, CRAM with VDP
fill/copy modes, but whatever. Get to that later.

Also implemented a very lazy preliminary wait mechanism to to stall out
a processor while another processor exerts control over the bus. This
one's going to be a major work in progress. For one, it totally breaks
the model I use to do save states with libco. For another, I don't
know if a 68K->VDP DMA instantly locks the CPU, or if it the CPU could
actually keep running if it was executing out of RAM when it started
the DMA transfer from ROM (eg it's a bus busy stall, not a hard chip
stall.) That'll greatly change how I handle the waiting.

Also, the OSS driver now supports Audio::Latency. Sound should be
even lower latency now. On FreeBSD when set to 0ms, it's absolutely
incredible. Cannot detect latency whatsoever. The Mario jump sound seems
to happen at the very instant I hear my cherry blue keyswitch activate.
2016-08-15 14:56:38 +10:00
Tim Allen
427bac3011 Update to v101r06 release.
byuu says:

I reworked the video sizing code. Ended up wasting five fucking hours
fighting GTK. When you call `gtk_widget_set_size_request`, it doesn't
actually happen then. This is kind of a big deal because when I then go
to draw onto the viewport, the actual viewport child window is still the
old size, so the image gets distorted. It recovers in a frame or so with
emulation, but if we were to put a still image on there, it would stay
distorted.

The first thought is, `while(gtk_events_pending())
gtk_main_iteration_do(false);` right after the `set_size_request`. But
nope, it tells you there's no events pending. So then you think, go
deeper, use `XPending()` instead. Same thing, GTK hasn't actually issued
the command to Xlib yet. So then you think, if the widget is realized,
just call a blocking `gtk_main_iteration`. One call does nothing, two
calls results in a deadlock on the second one ... do it before program
startup, and the main window will never appear. Great.

Oh, and it's not just the viewport. It's also the widget container area
of the windows, as well as the window itself, as well as the fullscreen
mode toggle effect. They all do this.

For the latter three, I couldn't find anything that worked, so I just
added 20ms loops of constantly calling `gtk_main_iteration_do(false)`
after each one of those things. The downside here is toggling the status
bar takes 40ms, so you'll see it and it'll feel a tiny bit sluggish.

But I can't have a 20ms wait on each widget resize, that would be
catastrophic to performance on windows with lots of widgets.

I tried hooking configure-event and size-allocate, but they were very
unreliable. So instead I ended up with a loop that waits up to a maximm
of 20ms that inspects the `widget->allocation.(width,height)` values
directly and waits for them to be what we asked for with
`set_size_request`.

There was some extreme ugliness in GTK with calling
`gtk_main_iteration_do` recursively (`hiro::Widget::setGeometry` is
called recursively), so I had to lock it to only happen on the top level
widgets (the child ones should get resized while waiting on the
top-level ones, so it should be fine in practice), and also only run it
on realized widgets.

Even still, I'm getting ~3 timeouts when opening the settings dialog in
higan, but no other windows. But, this is the best I can do for now.

And the reason for all of this pain? Yeah, updated the video code.

So the Emulator::Interface now has this:

    struct VideoSize { uint width, height; };  //or requiem for a tuple
    auto videoSize() -> VideoSize;
    auto videoSize(uint width, uint height, bool arc) -> VideoSize;

The first function, for now, is just returning the literal surface size.
I may remove this ... one thing I want to allow for is cores that send
different texture sizes based on interlace/hires/overscan/etc settings.

The second function is more interesting. Instead of having the UI trying
to figure out sizing, I figure the emulation cores can do a better job
and we can customize it per-core now. So it gets the window's width and
height, and whether the user asked for aspect correction, and then
computes the best width/height ratio possible. For now they're all just
doing multiples of a 1x scale to the UI 2x,3x,4x modes.

We still need a third function, which will probably be what I repurpose
videoSize() for: to return the 'effective' size for pixel shaders, to
then feed into ruby, to then feed into quark, to then feed into our
shaders. Since shaders use normalized coordinates for pixel fetching,
this should work out just fine. The real texture size will be exposed to
quark shaders as well, of course.

Now for the main window ... it's just hard-coded to be 640x480, 960x720,
1280x960 for now. It works nicely for some cores on some modes, not so
much for others. Work in progress I guess.

I also took the opportunity to draw the about dialog box logo on the
main window. Got a bit fancy and used the old spherical gradient and
impose functionality of nall/image on it. Very minor highlight, nothing
garish. Just something nicer than a solid black window.

If you guys want to mess around with sizes, placements, and gradient
styles/colors/shapes ... feel free. If you come up with something nicer,
do share.

That's what led to all the GTK hell ... the logo wasn't drawing right as
you resized the window. But now it is, though I am not at all happy with
the hacking I had to do.

I also had to improve the video update code as a result of this:

  - when you unload a game, it blacks out the screen
      - if you are not quitting the emulator, it'll draw the logo; if
        you are, it won't
  - when you load a game, it black out the logo

These options prevent any unsightliness from resizing the viewport with
image data on it already

I need to redraw the logo when toggling fullscreen with no game loaded
as well for Windows, it seems.
2016-08-15 14:52:05 +10:00
Tim Allen
ac2d0ba1cf Update to v101r05 release.
byuu says:

Changelog:

  - 68K: fixed bug that affected BSR return address
  - VDP: added very preliminary emulation of planes A, B, W (W is
    entirely broken though)
  - VDP: added command/address stuff so you can write to VRAM, CRAM,
    VSRAM
  - VDP: added VRAM fill DMA

I would be really surprised if any commercial games showed anything at
all, so I'd probably recommend against wasting your time trying, unless
you're really bored :P

Also, I wanted to add: I am accepting patches\! So if anyone wants to
look over the 68K core for bugs, that would save me untold amounts of
time in the near future :D
2016-08-13 09:47:30 +10:00
Tim Allen
1df2549d18 Update to v101r04 release.
byuu says:

Changelog:

  - pulled the (u)intN type aliases into higan instead of leaving them
    in nall
  - added 68K LINEA, LINEF hooks for illegal instructions
  - filled the rest of the 68K lambda table with generic instance of
    ILLEGAL
  - completed the 68K disassembler effective addressing modes
      - still unsure whether I should use An to decode absolute
        addresses or not
      - pro: way easier to read where accesses are taking place
      - con: requires An to be valid; so as a disassembler it does a
        poor job
      - making it optional: too much work; ick
  - added I/O decoding for the VDP command-port registers
  - added skeleton timing to all five processor cores
  - output at 1280x480 (needed for mixed 256/320 widths; and to handle
    interlace modes)

The VDP, PSG, Z80, YM2612 are all stepping one clock at a time and
syncing; which is the pathological worst case for libco. But they also
have no logic inside of them. With all the above, I'm averaging around
250fps with just the 68K core actually functional, and the VDP doing a
dumb "draw white pixels" loop. Still way too early to tell how this
emulator is going to perform.

Also, the 320x240 mode of the Genesis means that we don't need an aspect
correction ratio. But we do need to ensure the output window is a
multiple 320x240 so that the scale values work correctly. I was
hard-coding aspect correction to stretch the window an additional \*8/7.
But that won't work anymore so ... the main higan window is now 640x480,
960x720, or 1280x960. Toggling aspect correction only changes the video
width inside the window.

It's a bit jarring ... the window is a lot wider, more black space now
for most modes. But for now, it is what it is.
2016-08-12 11:07:04 +10:00
Tim Allen
9b8c3ff8c0 Update to v101r03 release.
byuu says:

The 68K core now implements all 88 instructions. It ended up being 111
instructions in my core due to splitting up opcodes with the same name
but different addressing modes or directions (removes conditions at the
expense of more code.)

Technically, I don't have exceptions actually implemented yet, and
RESET/STOP don't do anything but set flags. So there's still more to
go. But ... close enough for statistics time!

The M68K core source code is 124,712 bytes in size. The next largest
core is the ARM7 core at 70,203 bytes in size.

The M68K object size is 942KiB; with the next largest being the V30MZ
core at 173KiB.

There are a total of 19,656 invalid opcodes in the 68000 revision (unless
of course I've made mistakes in my mappings, which is very probably.)

Now the fun part ... figuring out how to fix bugs in this core without
VDP emulation :/
2016-08-11 08:02:02 +10:00
Tim Allen
0a57cac70c Update to v101r02 release.
byuu says:

Changelog:

  - Emulator: use `(uintmax)-1 >> 1` for the units of time
  - MD: implemented 13 new 68K instructions (basically all of the
    remaining easy ones); 21 remain
  - nall: replaced `(u)intmax_t` (64-bit) with *actual* `(u)intmax` type
    (128-bit where available)
      - this extends to everything: atoi, string, etc. You can even
        print 128-bit variables if you like

22,552 opcodes still don't exist in the 68K map. Looking like quite a
few entries will be blank once I finish.
2016-08-09 21:07:18 +10:00
Tim Allen
8bdf8f2a55 Update to v101r01 release.
byuu says:

Changelog:

  - added eight more 68K instructions
  - split ADD(direction) into two separate ADD functions

I now have 54 out of 88 instructions implemented (thus, 34 remaining.)
The map is missing 25,182 entries out of 65,536. Down from 32,680 for
v101.00

Aside: this version number feels really silly. r10 and r11 surely will
as well ...
2016-08-08 20:12:03 +10:00
Tim Allen
e39987a3e3 Update to v101 release.
byuu says (in the public announcement):

Not a large changelog this time, sorry. This release is mostly to fix
the SA-1 issue, and to get some real-world testing of the new scheduler
model. Most of the work in the past month has gone into writing a 68000
CPU core; yet it's still only about half-way finished.

Changelog (since the previous release):

  - fixed SNES SA-1 IRQ regression (fixes Super Mario RPG level-up
    screen)
  - new scheduler for all emulator cores (precision of 2^-127)
  - icarus database adds nine new SNES games
  - added Input/Frequency to settings file (allows simulation of
    latency)

byuu says (in the WIP forum):

Changelog:

  - in 32-bit mode, Thread uses uint64\_t with 2^-63 time units (10^-7
    precision in the worst case)
      - nearly ten times the precision of an attosecond
  - in 64-bit mode, Thread uses uint128\_t with 2^-127 time units
    (10^-26 precision in the worst case)
      - far more accurate than yoctoseconds; almost closing in on planck
        time

Note: a quartz crystal is accurate to 10^-4 or 10^-5. A cesium fountain
atomic clock is accurate to 10^-15. So ... yeah. 2^-63 was perfectly
fine; but there was no speed penalty whatsoever for using uint128\_t in
64-bit mode, so why not?
2016-08-08 20:04:15 +10:00
Tim Allen
f5e5bf1772 Update to v100r16 release.
byuu says:

(Windows users may need to include <sys/time.h> at the top of
nall/chrono.hpp, not sure.)

Unchangelog:
- forgot to add the Scheduler clock=0 fix because I have the memory of
  a goldfish

Changelog:
- new icarus database with nine additional games
- hiro(GTK,Qt) won't constantly write its settings.bml file to disk
  anymore
- added latency simulator for fun (settings.bml => Input/Latency in
  milliseconds)

So the last one ... I wanted to test out nall::chrono, and I was also
thinking that by polling every emulated frame, it's pretty wasteful when
you are using Fast Forward and hitting 200+fps. As I've said before,
calls to ruby::input::poll are not cheap.

So to get around this, I added a limiter so that if you called the
hardware poll function within N milliseconds, it'll return without
doing any actual work. And indeed, that increases my framerate of Zelda
3 uncapped from 133fps to 142fps. Yay. But it's not a "real" speedup,
as it only helps you when you exceed 100% speed (theoretically, you'd
need to crack 300% speed since the game itself will poll at 16ms at 100%
speed, but yet it sped up Zelda 3, so who am I to complain?)

I threw the latency value into the settings file. It should be 16,
but I set it to 5 since that was the lowest before it started negatively
impacting uncapped speeds. You're wasting your time and CPU cycles setting
it lower than 5, but if people like placebo effects it might work. Maybe
I should let it be a signed integer so people can set it to -16 and think
it's actually faster :P (I'm only joking. I took out the 96000hz audio
placebo effect as well. Not really into psychological tricks anymore.)

But yeah seriously, I didn't do this to start this discussion again for
the billionth time. Please don't go there. And please don't tell me this
WIP has higher/lower latency than before. I don't want to hear it.

The only reason I bring it up is for the fun part that is worth
discussing: put up or shut up time on how sensitive you are to
latency! You can set the value above 5 to see how games feel.

I personally can't really tell a difference until about 50. And I can't
be 100% confident it's worse until about 75. But ... when I set it to
150, games become "extra difficult" ... the higher it goes, the worse
it gets :D

For this WIP, I've left no upper limit cap. I'll probably set a cap of
something like 500ms or 1000ms for the official release. Need to balance
user error/trolling with enjoyability. I'll think about it.

[...]

Now, what I worry about is stupid people seeing it and thinking it's an
"added latency" setting, as if anyone would intentionally make things
worse by default. This is a limiter. So if 5ms have passed since the
game last polled, and that will be the case 99.9% of the time in games,
the next poll will happen just in time, immediately when the game polls
the inputs. Thus, a value below 1/<framerate>ms is not only pointless,
if you go too low it will ruin your fast forward max speeds.

I did say I didn't want to resort to placebo tricks, but I also don't
want to spark up public discussion on this again either. So it might
be best to default Input/Latency to 0ms, and internally have a max(5,
latency) wrapper around the value.
2016-08-03 22:32:40 +10:00
Tim Allen
c50723ef61 Update to v100r15 release.
byuu wrote:

Aforementioned scheduler changes added. Longer explanation of why here:
http://hastebin.com/raw/toxedenece

Again, we really need to test this as thoroughly as possible for
regressions :/
This is a really major change that affects absolutely everything: all
emulation cores, all coprocessors, etc.

Also added ADDX and SUB to the 68K core, which brings us just barely
above 50% of the instruction encoding space completed.

[Editor's note: The "aformentioned scheduler changes" were described in
a previous forum post:

    Unfortunately, 64-bits just wasn't enough precision (we were
    getting misalignments ~230 times a second on 21/24MHz clocks), so
    I had to move to 128-bit counters. This of course doesn't exist on
    32-bit architectures (and probably not on all 64-bit ones either),
    so for now ... higan's only going to compile on 64-bit machines
    until we figure something out. Maybe we offer a "lower precision"
    fallback for machines that lack uint128_t or something. Using the
    booth algorithm would be way too slow.

    Anyway, the precision is now 2^-96, which is roughly 10^-29. That
    puts us far beyond the yoctosecond. Suck it, MAME :P I'm jokingly
    referring to it as the byuusecond. The other 32-bits of precision
    allows a 1Hz clock to run up to one full second before all clocks
    need to be normalized to prevent overflow.

    I fixed a serious wobbling issue where I was using clock > other.clock
    for synchronization instead of clock >= other.clock; and also another
    aliasing issue when two threads share a common frequency, but don't
    run in lock-step. The latter I don't even fully understand, but I
    did observe it in testing.

    nall/serialization.hpp has been extended to support 128-bit integers,
    but without explicitly naming them (yay generic code), so nall will
    still compile on 32-bit platforms for all other applications.

    Speed is basically a wash now. FC's a bit slower, SFC's a bit faster.

The "longer explanation" in the linked hastebin is:

    Okay, so the idea is that we can have an arbitrary number of
    oscillators. Take the SNES:

    - CPU/PPU clock = 21477272.727272hz
    - SMP/DSP clock = 24576000hz
    - Cartridge DSP1 clock = 8000000hz
    - Cartridge MSU1 clock = 44100hz
    - Controller Port 1 modem controller clock = 57600hz
    - Controller Port 2 barcode battler clock = 115200hz
    - Expansion Port exercise bike clock = 192000hz

    Is this a pathological case? Of course it is, but it's possible. The
    first four do exist in the wild already: see Rockman X2 MSU1
    patch. Manifest files with higan let you specify any frequency you
    want for any component.

    The old trick higan used was to hold an int64 counter for each
    thread:thread synchronization, and adjust it like so:

    - if thread A steps X clocks; then clock += X * threadB.frequency
      - if clock >= 0; switch to threadB
    - if thread B steps X clocks; then clock -= X * threadA.frequency
      - if clock <  0; switch to threadA

    But there are also system configurations where one processor has to
    synchronize with more than one other processor. Take the Genesis:

    - the 68K has to sync with the Z80 and PSG and YM2612 and VDP
    - the Z80 has to sync with the 68K and PSG and YM2612
    - the PSG has to sync with the 68K and Z80 and YM2612

    Now I could do this by having an int64 clock value for every
    association. But these clock values would have to be outside the
    individual Thread class objects, and we would have to update every
    relationship's clock value. So the 68K would have to update the Z80,
    PSG, YM2612 and VDP clocks. That's four expensive 64-bit multiply-adds
    per clock step event instead of one.

    As such, we have to account for both possibilities. The only way to
    do this is with a single time base. We do this like so:

    - setup: scalar = timeBase / frequency
    - step: clock += scalar * clocks

    Once per second, we look at every thread, find the smallest clock
    value. Then subtract that value from all threads. This prevents the
    clock counters from overflowing.

    Unfortunately, these oscillator values are psychotic, unpredictable,
    and often times repeating fractions. Even with a timeBase of
    1,000,000,000,000,000,000 (one attosecond); we get rounding errors
    every ~16,300 synchronizations. Specifically, this happens with a CPU
    running at 21477273hz (rounded) and SMP running at 24576000hz. That
    may be good enough for most emulators, but ... you know how I am.

    Plus, even at the attosecond level, we're really pushing against the
    limits of 64-bit integers. Given the reciprocal inverse, a frequency
    of 1Hz (which does exist in higan!) would have a scalar that consumes
    1/18th of the entire range of a uint64 on every single step. Yes, I
    could raise the frequency, and then step by that amount, I know. But
    I don't want to have weird gotchas like that in the scheduler core.

    Until I increase the accuracy to about 100 times greater than a
    yoctosecond, the rounding errors are too great. And since the only
    choice above 64-bit values is 128-bit values; we might as well use
    all the extra headroom. 2^-96 as a timebase gives me the ability to
    have both a 1Hz and 4GHz clock; and run them both for a full second;
    before an overflow event would occur.

Another hastebin includes demonstration code:

    #include <libco/libco.h>

    #include <nall/nall.hpp>
    using namespace nall;

    //

    cothread_t mainThread = nullptr;
    const uint iterations = 100'000'000;
    const uint cpuFreq = 21477272.727272 + 0.5;
    const uint smpFreq = 24576000.000000 + 0.5;
    const uint cpuStep = 4;
    const uint smpStep = 5;

    //

    struct ThreadA {
      cothread_t handle = nullptr;
      uint64 frequency = 0;
      int64 clock = 0;

      auto create(auto (*entrypoint)() -> void, uint frequency) {
        this->handle = co_create(65536, entrypoint);
        this->frequency = frequency;
        this->clock = 0;
      }
    };

    struct CPUA : ThreadA {
      static auto Enter() -> void;
      auto main() -> void;
      CPUA() { create(&CPUA::Enter, cpuFreq); }
    } cpuA;

    struct SMPA : ThreadA {
      static auto Enter() -> void;
      auto main() -> void;
      SMPA() { create(&SMPA::Enter, smpFreq); }
    } smpA;

    uint8 queueA[iterations];
    uint offsetA;
    cothread_t resumeA = cpuA.handle;

    auto EnterA() -> void {
      offsetA = 0;
      co_switch(resumeA);
    }

    auto QueueA(uint value) -> void {
      queueA[offsetA++] = value;
      if(offsetA >= iterations) {
        resumeA = co_active();
        co_switch(mainThread);
      }
    }

    auto CPUA::Enter() -> void { while(true) cpuA.main(); }

    auto CPUA::main() -> void {
      QueueA(1);
      smpA.clock -= cpuStep * smpA.frequency;
      if(smpA.clock < 0) co_switch(smpA.handle);
    }

    auto SMPA::Enter() -> void { while(true) smpA.main(); }

    auto SMPA::main() -> void {
      QueueA(2);
      smpA.clock += smpStep * cpuA.frequency;
      if(smpA.clock >= 0) co_switch(cpuA.handle);
    }

    //

    struct ThreadB {
      cothread_t handle = nullptr;
      uint128_t scalar = 0;
      uint128_t clock = 0;

      auto print128(uint128_t value) {
        string s;
        while(value) {
          s.append((char)('0' + value % 10));
          value /= 10;
        }
        s.reverse();
        print(s, "\n");
      }

      //femtosecond (10^15) =    16306
      //attosecond  (10^18) =   688838
      //zeptosecond (10^21) = 13712691
      //yoctosecond (10^24) = 13712691 (hitting a dead-end on a rounding error causing a wobble)
      //byuusecond? ( 2^96) = (perfect? 79,228 times more precise than a yoctosecond)

      auto create(auto (*entrypoint)() -> void, uint128_t frequency) {
        this->handle = co_create(65536, entrypoint);

        uint128_t unitOfTime = 1;
      //for(uint n : range(29)) unitOfTime *= 10;
        unitOfTime <<= 96;  //2^96 time units ...

        this->scalar = unitOfTime / frequency;
        print128(this->scalar);
        this->clock = 0;
      }

      auto step(uint128_t clocks) -> void { clock += clocks * scalar; }
      auto synchronize(ThreadB& thread) -> void { if(clock >= thread.clock) co_switch(thread.handle); }
    };

    struct CPUB : ThreadB {
      static auto Enter() -> void;
      auto main() -> void;
      CPUB() { create(&CPUB::Enter, cpuFreq); }
    } cpuB;

    struct SMPB : ThreadB {
      static auto Enter() -> void;
      auto main() -> void;
      SMPB() { create(&SMPB::Enter, smpFreq); clock = 1; }
    } smpB;

    auto correct() -> void {
      auto minimum = min(cpuB.clock, smpB.clock);
      cpuB.clock -= minimum;
      smpB.clock -= minimum;
    }

    uint8 queueB[iterations];
    uint offsetB;
    cothread_t resumeB = cpuB.handle;

    auto EnterB() -> void {
      correct();
      offsetB = 0;
      co_switch(resumeB);
    }

    auto QueueB(uint value) -> void {
      queueB[offsetB++] = value;
      if(offsetB >= iterations) {
        resumeB = co_active();
        co_switch(mainThread);
      }
    }

    auto CPUB::Enter() -> void { while(true) cpuB.main(); }

    auto CPUB::main() -> void {
      QueueB(1);
      step(cpuStep);
      synchronize(smpB);
    }

    auto SMPB::Enter() -> void { while(true) smpB.main(); }

    auto SMPB::main() -> void {
      QueueB(2);
      step(smpStep);
      synchronize(cpuB);
    }

    //

    #include <nall/main.hpp>
    auto nall::main(string_vector) -> void {
      mainThread = co_active();

      uint masterCounter = 0;
      while(true) {
        print(masterCounter++, " ...\n");

        auto A = clock();
        EnterA();
        auto B = clock();
        print((double)(B - A) / CLOCKS_PER_SEC, "s\n");

        auto C = clock();
        EnterB();
        auto D = clock();
        print((double)(D - C) / CLOCKS_PER_SEC, "s\n");

        for(uint n : range(iterations)) {
          if(queueA[n] != queueB[n]) return print("fail at ", n, "\n");
        }
      }
    }

...and that's everything.]
2016-07-31 12:11:20 +10:00
Tim Allen
ca277cd5e8 Update to v100r14 release.
byuu says:

(Windows: compile with -fpermissive to silence an annoying error. I'll
fix it in the next WIP.)

I completely replaced the time management system in higan and overhauled
the scheduler.

Before, processor threads would have "int64 clock"; and there would
be a 1:1 relationship between two threads. When thread A ran for X
cycles, it'd subtract X * B.Frequency from clock; and when thread B ran
for Y cycles, it'd add Y * A.Frequency from clock. This worked well
and allowed perfect precision; but it doesn't work when you have more
complicated relationships: eg the 68K can sync to the Z80 and PSG; the
Z80 to the 68K and PSG; so the PSG needs two counters.

The new system instead uses a "uint64 clock" variable that represents
time in attoseconds. Every time the scheduler exits, it subtracts
the smallest clock count from all threads, to prevent an overflow
scenario. The only real downside is that rounding errors mean that
roughly every 20 minutes, we have a rounding error of one clock cycle
(one 20,000,000th of a second.) However, this only applies to systems
with multiple oscillators, like the SNES. And when you're in that
situation ... there's no such thing as a perfect oscillator anyway. A
real SNES will be thousands of times less out of spec than 1hz per 20
minutes.

The advantages are pretty immense. First, we obviously can now support
more complex relationships between threads. Second, we can build a
much more abstracted scheduler. All of libco is now abstracted away
completely, which may permit a state-machine / coroutine version of
Thread in the future. We've basically gone from this:

    auto SMP::step(uint clocks) -> void {
      clock += clocks * (uint64)cpu.frequency;
      dsp.clock -= clocks;
      if(dsp.clock < 0 && !scheduler.synchronizing()) co_switch(dsp.thread);
      if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
    }

To this:

    auto SMP::step(uint clocks) -> void {
      Thread::step(clocks);
      synchronize(dsp);
      synchronize(cpu);
    }

As you can see, we don't have to do multiple clock adjustments anymore.
This is a huge win for the SNES CPU that had to update the SMP, DSP, all
peripherals and all coprocessors. Likewise, we don't have to synchronize
all coprocessors when one runs, now we can just synchronize the active
one to the CPU.

Third, when changing the frequencies of threads (think SGB speed setting
modes, GBC double-speed mode, etc), it no longer causes the "int64
clock" value to be erroneous.

Fourth, this results in a fairly decent speedup, mostly across the
board. Aside from the GBA being mostly a wash (for unknown reasons),
it's about an 8% - 12% speedup in every other emulation core.

Now, all of this said ... this was an unbelievably massive change, so
... you know what that means >_> If anyone can help test all types of
SNES coprocessors, and some other system games, it'd be appreciated.

----

Lastly, we have a bitchin' new about screen. It unfortunately adds
~200KiB onto the binary size, because the PNG->C++ header file
transformation doesn't compress very well, and I want to keep the
original resource files in with the higan archive. I might try some
things to work around this file size increase in the future, but for now
... yeah, slightly larger archive sizes, sorry.

The logo's a bit busted on Windows (the Label control's background
transparency and alignment settings aren't working), but works well on
GTK. I'll have to fix Windows before the next official release. For now,
look on my Twitter feed if you want to see what it's supposed to look
like.

----

EDIT: forgot about ICD2::Enter. It's doing some weird inverse
run-to-save thing that I need to implement support for somehow. So, save
states on the SGB core probably won't work with this WIP.
2016-07-30 13:56:12 +10:00
Tim Allen
306cac2b54 Update to v100r13 release.
byuu says:

Changelog: M68K improvements, new instructions added.
2016-07-26 20:46:43 +10:00
Tim Allen
f230d144b5 Update to v100r12 release.
byuu says:

All of the above fixes, plus I added all 24 variations on the shift
opcodes, plus SUBQ, plus fixes to the BCC instruction.

I can now run 851,767 instructions into Sonic the Hedgehog before hitting
an unimplemented instruction (SUB).

The 68K core is probably only ~35% complete, and yet it's already within
4KiB of being the largest CPU core, code size wise, in all of higan. Fuck
this chip.
2016-07-25 23:15:54 +10:00
Tim Allen
7ccfbe0206 Update to v100r11 release.
byuu says:

I split the Register class and read/write handlers into DataRegister and
AddressRegister, given that they have different behaviors on byte/word
accesses (data tends to preserve the upper bits; address tends to
sign-extend things.)

I expanded EA to EffectiveAddress. No sense in abbreviating things
to death.

I've now implemented 26 instructions. But the new ones are just all the
stupid from/to ccr/sr instructions.

Ryphecha confirmed that you can't set the undefined bits, so I don't
think the BitField concept is appropriate for the CCR/SR. Instead, I'm
just storing direct flags and have (read,write)(CCR,SR) instead. This
isn't like the 65816 where you have subroutines that push and pop the
flag register. It's much more common to access individual flags. Doesn't
match the consistency angle of the other CPU cores, but ... I think this
is the right thing to for the 68K specifically.
2016-07-23 12:32:35 +10:00
Tim Allen
4b897ba791 Update to v100r10 release.
byuu says:

Redesigned the handling of reading/writing registers to be about eight
times faster than the old system. More work may be needed ... it seems
data registers tend to preserve their upper bits upon assignment; whereas
address registers tend to sign-extend values into them. It may make
sense to have DataRegister and AddressRegister classes with separate
read/write handlers. I'd have to hold two Register objects inside the
EffectiveAddress (EA) class if we do that.

Implemented 19 opcodes now (out of somewhere between 60 and 90.) That gets
the first ~530,000 instructions in Sonic the Hedgehog running (though
probably wrong. But we can run a lot thanks to large initialization
loops.)

If I force the core to loop back to the reset vector on an invalid opcode,
I'm getting about 1500fps with a dumb 320x240 blit 60 times a second and
just the 68K running alone (no Z80, PSG, VDP, YM2612.) I don't know if
that's good or not. I guess we'll find out.

I had to stop tonight because the final opcode I execute is an RTS
(return from subroutine) that's branching back to address 0; which is
invalid ... meaning something went terribly wrong and the system crashed.
2016-07-22 22:03:25 +10:00
Tim Allen
be3f6ac0d5 Update to v100r09 release.
byuu says:

Another six hours in ...

I have all of the opcodes, memory access functions, disassembler mnemonics
and table building converted over to the new template<uint Size> format.

Certainly, it would be quite easy for this nightmare chip to throw me
another curveball, but so far I can handle:

- MOVE (EA to, EA from) case
  - read(from) has to update register index for +/-(aN) mode
- MOVEM (EA from) case
  - when using +/-(aN), RA can't actually be updated until the transfer
    is completed
- LEA (EA from) case
  - doesn't actually perform the final read; just returns the address
    to be read from
- ANDI (EA from-and-to) case
  - same EA has to be read from and written to
  - for -(aN), the read has to come from aN-2, but can't update aN yet;
    so that the write also goes to aN-2
- no opcode can ever fetch the extension words more than once
- manually control the order of extension word fetching order for proper
  opcode decoding

To do all of that without a whole lot of duplicated code (or really
bloating out every single instruction with red tape), I had to bring
back the "bool valid / uint32 address" variables inside the EA struct =(

If weird exceptions creep in like timing constraints only on certain
opcodes, I can use template flags to the EA read/write functions to
handle that.
2016-07-19 19:12:05 +10:00
Tim Allen
92fe5b0813 Update to v100r08 release.
byuu says:

Six and a half hours this time ... one new opcode, and all old opcodes
now in a deprecated format. Hooray, progress!

For building the table, I've decided to move from:

    for(uint opcode : range(65536)) {
      if(match(...)) bind(opNAME, ...);
    }

To instead having separate for loops for each supported opcode. This
lets me specialize parts I want with templates.

And to this aim, I'm moving to replace all of the
(read,write)(size, ...) functions with (read,write)<Size>(...) functions.

This will amount to the ~70ish instructions being triplicated ot ~210ish
instructions; but I think this is really important.

When I was getting into flag calculations, a ton of conditionals
were needed to mask sizes to byte/word/long. There was also lots of
conditionals in all the memory access handlers.

The template code is ugly, but we eliminate a huge amount of branch
conditions this way.
2016-07-18 08:11:29 +10:00
Tim Allen
059347e575 Update to v100r07 release.
byuu says:

Four and a half hours of work and ... zero new opcodes implemented.

This was the best job I could do refining the effective address
computations. Should have all twelve 68000 modes implemented now. Still
have a billion questions about when and how I'm supposed to perform
certain edge case operations, though.
2016-07-17 13:24:28 +10:00
Tim Allen
0d6a09f9f8 Update to v100r06 release.
byuu says:

Up to ten 68K instructions out of somewhere between 61 and 88, depending
upon which PDF you look at. Of course, some of them aren't 100% completed
yet, either. Lots of craziness with MOVEM, and BCC has a BSR variant
that needs stack push/pop functions.

This WIP actually took over eight hours to make, going through every
possible permutation on how to design the core itself. The updated design
now builds both the instruction decoder+dispatcher and the disassembler
decoder into the same main loop during M68K's constructor.

The special cases are also really psychotic on this processor, and
I'm afraid of missing something via the fallthrough cases. So instead,
I'm ordering the instructions alphabetically, and including exclusion
cases to ignore binding invalid cases. If I end up remapping an existing
register, then it'll throw a run-time assertion at program startup.

I wanted very much to get rid of struct EA (EffectiveAddress), but
it's too difficult to keep track of the internal effective address
without it. So I split out the size to a separate parameter, since
every opcode only has one size parameter, and otherwise it was getting
duplicated in opcodes that take two EAs, and was also awkward with the
flag testing. It's a bit more typing, but I feel it's more clean this way.

Overall, I'm really worried this is going to be too slow. I don't want
to turn the EA stuff into templates, because that will massively bloat
out compilation times and object sizes, and will also need a special DSL
preprocessor since C++ doesn't have a static for loop. I can definitely
optimize a lot of EA's address/read/write functions away once the core
is completed, but it's never going to hold a candle to a templatized
68K core.

----

Forgot to include the SA-1 regression fix. I always remember immediately
after I upload and archive the WIP. Will try to get that in next time,
I guess.
2016-07-16 18:39:44 +10:00
Tim Allen
b72f35a13e Update to v100r05 release.
byuu says:

Alright, I'm definitely going to need to find some people willing to
tolerate my questions on this chip, so I'm going to go ahead and announce
I'm working on this I guess.

This core is way too big for a surprise like the NES and WS cores
were. It'll probably even span multiple v10x releases before it's
even ready.
2016-07-13 08:47:04 +10:00
Tim Allen
1c0ef793fe Update to v100r04 release.
byuu says:

I now have enough of three instructions implemented to get through the
first four instructions in Sonic the Hedgehog.

But they're far from complete. The very first instruction uses EA
addressing, which is similar to x86's ModRM in terms of how disgustingly
complex it is. And it also accesses Z80 control registers, which obviously
isn't going to do anything yet.

The slow speed was me being stupid again. It's not 7.6MHz per frame,
it's 7.67MHz per second. So yeah, speed is so far acceptable again. But
we'll see how things go as I keep emulating more. The 68K decode is not
pretty at all.
2016-07-12 20:19:31 +10:00
Tim Allen
76a8ecd32a Update to v100r03 release.
byuu says:

Changelog:
- moved Thread, Scheduler, Cheat functionality into emulator/ for
  all cores
- start of actual Mega Drive emulation (two 68K instructions)

I'm going to be rather terse on MD emulation, as it's too early for any
meaningful dialogue here.
2016-07-10 15:28:26 +10:00
Tim Allen
3dd1aa9c1b Update to v100r02 release.
byuu says:

Sigh ... I'm really not a good person. I'm inherently selfish.

My responsibility and obligation right now is to work on loki, and
then on the Tengai Makyou Zero translation, and then on improving the
Famicom emulation.

And yet ... it's not what I really want to do. That shouldn't matter;
I should work on my responsibilities first.

Instead, I'm going to be a greedy, self-centered asshole, and work on
what I really want to instead.

I'm really sorry, guys. I'm sure this will make a few people happy,
and probably upset even more people.

I'm also making zero guarantees that this ever gets finished. As always,
I wish I could keep these things secret, so if I fail / give up, I could
just drop it with no shame. But I would have to cut everyone out of the
WIP process completely to make it happen. So, here goes ...

This WIP adds the initial skeleton for Sega Mega Drive / Genesis
emulation. God help us.

(minor note: apparently the new extension for Mega Drive games is .md,
neat. That's what I chose for the folders too. I thought it was .smd,
so that'll be fixed in icarus for the next WIP.)

(aside: this is why I wanted to get v100 out. I didn't want this code in
a skeleton state in v100's source. Nor did I want really broken emulation,
which the first release is sure to be, tarring said release.)

...

So, basically, I've been ruminating on the legacy I want to leave behind
with higan. 3D systems are just plain out. I'm never going to support
them. They're too complex for my abilities, and they would run too slowly
with my design style. I'm not willing to compromise my design ideals. And
I would never want to play a 3D game system at native 240p/480i resolution
... but 1080p+ upscaling is not accurate, so that's a conflict I want
to avoid entirely. It's also never going to emulate computer systems
(X68K, PC-98, FM-Towns, etc) because holy shit that would completely
destroy me. It's also never going emulate arcade machines.

So I think of higan as a collection of 2D emulators for consoles
and handhelds. I've gone over every major 2D gaming system there is,
looking for ones with games I actually care about and enjoy. And I
basically have five of those systems supported already. Looking at the
remaining list, I see only three systems left that I have any interest
in whatsoever: PC-Engine, Master System, Mega Drive. Again, I'm not in
any way committing to emulating any of these, but ... if I had all of
those in higan, I think I'd be content to really, truly, finally stop
writing more emulators for the rest of my life.

And so I decided to tackle the most difficult system first. If I'm
successful, the Z80 core should cover a lot of the work on the SMS. And
the HuC6280 should land somewhere between the NES and SNES in terms of
difficulty ... closer to the NES.

The systems that just don't appeal to me at all, which I will never touch,
include, but are not limited to:
* Atari 2600/5200/7800
* Lynx
* Jaguar
* Vectrex
* Colecovision
* Commodore 64
* Neo-Geo
* Neo-Geo Pocket / Color
* Virtual Boy
* Super A'can
* 32X
* CD-i
* etc, etc, etc.

And really, even if something were mildly interesting in there ... we
have to stop. I can't scale infinitely. I'm already way past my limit,
but I'm doing this anyway. Too many cores bloats everything and kills
quality on everything. I don't want higan to become MESS v2.

I don't know what I'll do about the Famicom Disk System, PC-Engine CD,
and Mega CD. I don't think I'll be able to achieve 60fps emulating the
Mega CD, even if I tried to.

I don't know what's going to happen here with even the Mega Drive. Maybe
I'll get driven crazy with the documentation and quit. Maybe it'll end
up being too complicated and I'll quit. Maybe the emulation will end up
way too slow and I'll give up. Maybe it'll take me seven years to get
any games playable at all. Maybe Steve Snake, AamirM and Mike Pavone
will pool money to hire a hitman to come after me. Who knows.

But this is what I want to do, so ... here goes nothing.
2016-07-09 14:21:37 +10:00
Tim Allen
88c79e56a0 Update to v100r01 release.
[This version, with the internal version number changed back to "v100",
replaced the original v100 source archive on byuu.org soon after v100's
release, because it fixes important bugs in that version. --Ed]

byuu says:

Changelog:
- fixed default paths for Sufami Turbo slotted games
- moved WonderSwan orientation controls to the port rather than the device
  - I do like hex_usr's idea here; but that'll need more consideration;
    so this is a temporary fix
- added new debugger interface (see the public topic for more on that)
2016-07-08 22:31:35 +10:00
Tim Allen
07995c05a5 Update to v100 release.
byuu says:

higan has finally reached v100!

I feel it's important to stress right away that this is not "version
1.00", nor is it a major milestone release. Rather than arbitrary version
numbers, all of my software simply bumps version numbers by one for each
official release. As such, higan v100 is simply higan's 100th release.

That said, the primary focus of this release has been code
clean-ups. These are always somewhat dangerous in that regressions are
possible. We've tested through sixteen WIP revisions, one of which was
open to the public, to try and minimize any regressions. But all the same,
please report any regressions if you discover any.

Changelog (since v099):
FC: render during pixels 1-256 instead of 0-255 [hex_usr]
FC: rewrote controller emulation code
SFC: 8% speedup over the previous release thanks to PPU optimizations
SFC: fixed nasty DB address wrapping regression from v099
SFC: USART developer controller removed; superseded by 21fx
SFC: Super Multitap option removed from controller port 1; ports
    renamed 2-5
SFC: hidden option to experiment with 128KB VRAM (strictly for novelty)
higan: audio volume no longer divided by number of audio streams
higan: updated controller polling code to fix possible future mapping
    issues
higan: replaced nall/stream with nall/vfs for file-loading subsystem
tomoko: can now load multi-slotted games via command-line
tomoko: synchronize video removed from UI; still available in the
    settings file
tomoko, icarus: can navigate to root drive selection on Windows
all: major code cleanups and refactoring (~1MB diff against v099)

Note 1: the audio volume change means that SGB and MSU1 games won't
lose half the volume on the SNES sounds anymore. However, if one goes
overboard and drives the sound all the way to max volume with the MSU1,
clamping may occur. The obvious solution is not to drive volume that high
(it will vastly overpower the SNES audio, which usually never exceeds
25% volume.) Another option is to lower the volume in the audio settings
panel to 50%. In general, neither is likely to ever be necessary.

Note 2: the synchronize video option was hidden from the UI because it
is no longer useful. With the advent of compositors, the loss of the
complicated timing settings panel, support for the WonderSwan and its
75hz display, the need to emulate variable refresh rate behaviors in the
Game Boy, the unfortunate latency spike and audio distortion caused by
long Vsync pauses, and the arrival of adaptive sync technology ... it
no longer makes sense to present this option. However, as stated, you
can edit settings.bml to enable this option anyway if you insist and
understand the aforementioned risks.

Changelog (since v099r16 open beta):

- fixed MSU1 audio sign extension
- fixed compilation with SGB support disabled
- icarus can now navigate to root directory
- fixed compilation issues with OS X port
- (hopefully) fixed label height issue with hiro that affected icarus
  import dialog
- (mostly) fixed BS Memory, Sufami Turbo slot loading

Errata:

- forgot to remove the " - Slot A", " - Slot B" suffixes for Sufami
  Turbo slot loading
  - this means you have to navigate up one folder and then into Sufami
    Turbo/ to load games for this system
- moving WonderSwan orientation controls to the device slot is causing
  some nastiness
  - can now select orientation from the main menu, but it doesn't rotate
    the display
2016-07-08 22:04:59 +10:00
Tim Allen
13ad9644a2 Update to v099r16 release (public beta).
byuu says:

Changelog:
- hiro: BrowserDialog can navigate up to drive selection on Windows
- nall: (file,path,dir,base,prefix,suffix)name =>
  Location::(file,path,dir,base,prefix,suffix)
- higan/tomoko: rename audio filter label from "Sinc" to "IIR - Biquad"
- higan/tomoko: allow loading files via icarus on the command-line
  once again
- higan/tomoko: (begrudging) quick hack to fix presentation window focus
  on startup
- higan/audio: don't divide output audio volume by number of streams
- processor/r65816: fix a regression in (read,write)DB; fixes Taz-Mania
- fixed compilation regressions on Windows and Linux

I'm happy with where we are at with code cleanups and stability, so I'd
like to release v100. But even though I'm not assigning any special
significance to this version, we should probably test it more thoroughly
first.
2016-07-04 21:53:24 +10:00
Tim Allen
8d5cc0c35e Update to v099r15 release.
byuu says:

Changelog:
- nall::lstring -> nall::string_vector
- added IntegerBitField<type, lo, hi> -- hopefully it works correctly...
- Multitap 1-4 -> Super Multitap 2-5
- fixed SFC PPU CGRAM read regression
- huge amounts of SFC PPU IO register cleanups -- .bits really is lovely
- re-added the read/write(VRAM,OAM,CGRAM) helpers for the SFC PPU
  - but they're now optimized to the realities of the PPU (16-bit data
    sizes / no address parameter / where appropriate)
  - basically used to get the active-display overrides in a unified place;
    but also reduces duplicate code in (read,write)IO
2016-07-04 21:48:17 +10:00
Tim Allen
82293c95ae Update to v099r14 release.
byuu says:

Changelog:
- (u)int(max,ptr) abbreviations removed; use _t suffix now [didn't feel
  like they were contributing enough to be worth it]
- cleaned up nall::integer,natural,real functionality
  - toInteger, toNatural, toReal for parsing strings to numbers
  - fromInteger, fromNatural, fromReal for creating strings from numbers
  - (string,Markup::Node,SQL-based-classes)::(integer,natural,real)
    left unchanged
  - template<typename T> numeral(T value, long padding, char padchar)
    -> string for print() formatting
    - deduces integer,natural,real based on T ... cast the value if you
      want to override
    - there still exists binary,octal,hex,pointer for explicit print()
      formatting
- lstring -> string_vector [but using lstring = string_vector; is
  declared]
  - would be nice to remove the using lstring eventually ... but that'd
    probably require 10,000 lines of changes >_>
- format -> string_format [no using here; format was too ambiguous]
- using integer = Integer<sizeof(int)*8>; and using natural =
  Natural<sizeof(uint)*8>; declared
  - for consistency with boolean. These three are meant for creating
    zero-initialized values implicitly (various uses)
- R65816::io() -> idle() and SPC700::io() -> idle() [more clear; frees
  up struct IO {} io; naming]
- SFC CPU, PPU, SMP use struct IO {} io; over struct (Status,Registers) {}
  (status,registers); now
  - still some CPU::Status status values ... they didn't really fit into
    IO functionality ... will have to think about this more
- SFC CPU, PPU, SMP now use step() exclusively instead of addClocks()
  calling into step()
- SFC CPU joypad1_bits, joypad2_bits were unused; killed them
- SFC PPU CGRAM moved into PPU::Screen; since nothing else uses it
- SFC PPU OAM moved into PPU::Object; since nothing else uses it
  - the raw uint8[544] array is gone. OAM::read() constructs values from
    the OAM::Object[512] table now
  - this avoids having to determine how we want to sub-divide the two
    OAM memory sections
  - this also eliminates the OAM::synchronize() functionality
- probably more I'm forgetting

The FPS fluctuations are driving me insane. This WIP went from 128fps to
137fps. Settled on 133.5fps for the final build. But nothing I changed
should have affected performance at all. This level of fluctuation makes
it damn near impossible to know whether I'm speeding things up or slowing
things down with changes.
2016-07-01 21:50:32 +10:00
Tim Allen
67457fade4 Update to v099r13 release.
byuu says:

Changelog:
- GB core code cleanup completed
- GBA core code cleanup completed
- some more cleanup on missed processor/arm functions/variables
- fixed FC loading icarus bug
- "Load ROM File" icarus functionality restored
- minor code unification efforts all around (not perfect yet)
  - MMIO->IO
  - mmio.cpp->io.cpp
  - read,write->readIO,writeIO

It's been a very long work in progress ... starting all the way back with
v094r09, but the major part of the higan code cleanup is now completed! Of
course, it's very important to note that this is only for the basic style:

- under_score functions and variables are now camelCase
- return-type function-name() are now auto function-name() -> return-type
- Natural<T>/Integer<T> replace (u)intT_n types where possible
- signed/unsigned are now int/uint
- most of the x==true,x==false tests changed to x,!x

A lot of spot improvements to consistency, simplicity and quality have
gone in along the way, of course. But we'll probably never fully finishing
beautifying every last line of code in the entire codebase. Still,
this is a really great start. Going forward, WIP diffs should start
being smaller and of higher quality once again.

I know the joke is, "until my coding style changes again", but ... this
was way too stressful, way too time consuming, and way too risky. I'm
too old and tired now for extreme upheavel like this again. The only
major change I'm slowly mulling over would be renaming the using
Natural<T>/Integer<T> = (u)intT; shorthand to something that isn't as
easily confused with the (u)int_t types ... but we'll see. I'll definitely
continue to change small things all the time, but for the larger picture,
I need to just accept the style I have and live with it.
2016-06-29 21:10:28 +10:00
Tim Allen
7a68059f78 Update to v099r12 release.
byuu says:

Changelog:
- fixed FC AxROM / VRC7 regression
- BitField split to BooleanBitField/NaturalBitField (in preparation
  for IntegerBitField)
- BitFieldReference removed
- GB CPU cleaned up
- GB Cartridge + Mappers cleaned up
- SFC CGRAM is now emulated as uint15[256] instead of uint[512]
- sfc/ppu/memory.cpp no longer needed; removed
- purged SFC Debugger hooks for now (some of the operator[] calls were
  bypassing them anyway)

Unfortunately, for reasons that defy all semblance of logic, the CGRAM
change caused a slight speed hit. As have the last few changes. We're
now down to around 129.5fps compared to 123.fps for v099 and 134.5fps
at our peak (v099r01-r02).

I really like the style I came up with for the Game Boy mappers to settle
the purpose(ROM,RAM) vs (rom,ram)Purpose naming convention. If I ever get
around to redoing the NES mappers, that's likely the approach I'll take.
2016-06-28 20:43:47 +10:00
Tim Allen
3e807946b8 Update to v099r11 release.
byuu says:

Changelog:
- NES PPU core updated to use BitFields (absolutely massive improvement
  in code readability)
- NES APU core updated to new coding style
- NES cartridge/board and cartridge/chip updated to new coding style
- pushed NES PPU rendering one dot forward (doesn't fix King's Quest V
  yet, sadly)
- fixed SNES PPU BG tilemask for 128KiB VRAM mode (doesn't fix Yoshi's
  Island, though)

So ... I kind of went overboard with the fc/cartridge changes. This WIP
diff is 185KiB >_>
I didn't realize it was going to be as big a task as it was, but once
I started everything broke in a chain reaction, so I had to do it all
at once.

There's a massive chance we've broken a bunch of NES things. Any typos
in this WIP are going to be absolutely insidious to track down =(

But ... supposing I pulled it off, this means the Famicom core is now
fully converted to the new coding style as well. That leaves only the
GB and GBA cores. Once those are finished, then we'll finally be free
of these gigantic hellspawn diffs.
2016-06-27 23:07:57 +10:00
Tim Allen
a816998122 Update to v099r10 release.
byuu says:

Changelog:
- higan/profile/ => higan/systems/ [temporary; unless we can't think of
  a better base folder name]
- god-damn-better-have fixed the input polling bug
- re-added command-line and drag-and-drop loading
  - command-line loading can now load multiple folders at once (SGB+GB
    game; Sufami Turbo+Slot A+Slot B; etc)
  - if you load just the base cart, it'll present you with a dialog to
    optionally load slotted cart(s)
- MSU1 now goes through nall/vfs instead of directly accessing the
  filesystem
- Famicom Cartridge, PPU cores updated to newer programming style
  - there's countless opportunity for BitField and .bits() in the PPU
    ... but I'm worried about breaking things

If anyone has a working MSU1 game and can test the changes out, that'd
be appreciated. I still don't have a test ROM on my dev box.

I wouldn't worry too much about extensively testing the Famicom PPU
changes just yet ... I'm still struggling with what to name the structs
inside the classes between all of my emulators, and the BitField/.bits()
changes will be much more important to test at a later date.

The only use case left for Emulator::Interface::path(uint id) is for
21fx emulation. This peripheral loads a DLL/SO via LoadLibrary/dlopen,
which do not have any official ways to open a file in RAM. I'm
very hesitant to use the portable trick of writing the memory to a
temporary file, loading it, and deleting the temporary file once done
... it's a real waste of disk activity. I might make something like
vfs::file::isVirtual->bool,path()->string to get around this. But even
once I do, the underlying LoadLibrary/dlopen call is still going to be
direct disk access.
2016-06-26 18:54:12 +10:00
Tim Allen
3a9c7c6843 Update to v099r09 release.
byuu says:

Changelog:
- Emulator::Interface::Medium::bootable removed
- Emulator::Interface::load(bool required) argument removed
  [File::Required makes no sense on a folder]
- Super Famicom.sys now has user-configurable properties (CPU,PPU1,PPU2
  version; PPU1 VRAM size, Region override)
- old nall/property removed completely
- volatile flags supported on coprocessor RAM files now (still not in
  icarus, though)
- (hopefully) fixed SNES Multitap support (needs testing)
- fixed an OAM tiledata range clipping limit in 128KiB VRAM mode (doesn't
  fix Yoshi's Island, sadly)
- (hopefully, again) fixed the input polling bug hex_usr reported
- re-added dialog box for when File::Required files are missing
  - really cool: if you're missing a boot ROM, BIOS ROM, or IPL ROM,
    it warns you immediately
  - you don't have to select a game before seeing the error message
    anymore
- fixed cheats.bml load/save location
2016-06-25 18:53:11 +10:00
Tim Allen
f48b332c83 Update to v099r08 release.
byuu says:

Changelog:
- nall/vfs work 100% completed; even SGB games load now
- emulation cores now call load() for the base cartridges as well
- updated port/device handling; portmask is gone; device ID bug should
  be resolved now
- SNES controller port 1 multitap option was removed
- added support for 128KiB SNES PPU VRAM (for now, edit sfc/ppu/ppu.hpp
  VRAM::size=0x10000; to enable)

Overall, nall/vfs was a huge success!! We've substantially reduced
the amount of boilerplate code everywhere, while still allowing (even
easier than before) support for RAM-based game loading/saving. All of
nall/stream is dead and buried.

I am considering removing Emulator::Interface::Medium::id and/or
bootable flag. Or at least, doing something different with it. The
values for the non-bootable GB/BS/ST entries duplicate the ID that is
supposed to be unique. They are for GB/GBC and WS/WSC. Maybe I'll use
this as the hardware revision selection ID, and then gut non-bootable
options. There's really no reason for that to be there. I think at one
point I was using it to generate library tabs for non-bootable systems,
but we don't do that anymore anyway.

Emulator::Interface::load() may not need the required flag anymore ... it
doesn't really do anything right now anyway.

I have a few reasons for having the cores load the base cartridge. Most
importantly, it is going to enable a special mode for the WonderSwan /
WonderSwan Color in the future. If we ever get the IPLROMs dumped ... it's
possible to boot these systems with no games inserted to set user profile
information and such. There are also other systems that may accept being
booted without a cartridge. To reach this state, you would load a game and
then cancel the load dialog. Right now, this results in games not loading.

The second reason is this prevents nasty crashes when loading fails. So
if you're missing a required manifest, the emulator won't die a violent
death anymore. It's able to back out at any point.

The third reason is consistency: loading the base cartridge works the
same as the slot cartridges.

The fourth reason is Emulator::Interface::open(uint pathID)
values. Before, the GB, SB, GBC modes were IDs 1,2,3 respectively. This
complicated things because you had to pass the correct ID. But now
instead, Emulator::Interface::load() returns maybe<uint> that is nothing
when no game is selected, and a pathID for a valid game. And now open()
can take this ID to access this game's folder contents.

The downside, which is temporary, is that command-line loading is
currently broken. But I do intend on restoring it. In fact, I want to do
better than before and allow multi-cart booting from the command-line by
specifying the base cartridge and then slot cartridges. The idea should
be pretty simple: keep a queue of pending filenames that we fill from
the command-line and/or drag-and-drop operations on the main window,
and then empty out the queue or prompt for load dialogs from the UI
when booting a system. This also might be a bit more unorthodox compared
to the traditional emulator design of "loadGame(filename)", but ... oh
well. It's easy enough still.

The port/device changes are fun. We simplified things quite a bit. The
portmask stuff is gone entirely. While ports and devices keep IDs,
this is really just sugar-coating so UIs can use for(auto& port :
emulator->ports) and access port.id; rather than having to use for(auto
n : range(emulator->ports)) { auto& port = emulator->ports[n]; ... };
but they should otherwise generally be identical to the order they appear
in their respective ranges. Still, don't rely on that.

Input::id is gone. There was no point since we also got rid of the nasty
Input::order vector. Since I was in here, I went ahead and caved on the
pedantics and renamed Input::guid to Input::userData.

I removed the SNES controller port 1 multitap option. Basically, the only
game that uses this is N-warp Daisakusen and, no offense to d4s, it's
not really a good game anyway. It's just a quick demo to show 8-players
on the SNES. But in the UI, all it does is confuse people into wasting
time mapping a controller they're never going to use, and they're going
to wonder which port to use. If more compelling use cases for 8-players
comes about, we can reconsider this. I left all the code to support this
in place, so all you have to do is uncomment one line to enable it again.

We now have dsnes emulation! :D
If you change PPU::VRAM::size to 0x10000 (words), then you should now
have 128KiB of VRAM. Even better, it serializes the used-VRAM size,
so your save states shouldn't crash on you if you swap between the two
(though if you try this, you're nuts.)

Note that this option does break commercial software. Yoshi's Island in
particular. This game is setting A15 on some PPU register writes, but
not on others. The end result of this is things break horribly in-game.

Also, this option is causing a very tiny speed hit for obvious reasons
with the variable masking value (I'm even using size-1 for now.) Given
how niche this is, I may just leave it a compile-time constant to avoid
the overhead cost. Otherwise, if we keep the option, then it'll go into
Super Famicom.sys/manifest.bml ... I'll flesh that out in the near-future.

----

Finally, some fun for my OCD ... my monitor suddenly cut out on me
in the middle of working on this WIP, about six hours in of non-stop
work. Had to hit a bunch of ctrl+alt+fN commands (among other things)
and trying to log in headless on another TTY to do issue commands,
trying to recover the display. Finally power cycled the monitor and it
came back up. So all my typing ended up going to who knows where.

Usually this sort of thing terrifies me enough that I scrap a WIP and
start over to ensure I didn't screw anything up during the crashed screen
when hitting keys randomly.

Obviously, everything compiles and appears to work fine. And I know
it's extremely paranoid, but OCD isn't logical, so ... I'm going
to go over every line of the 100KiB r07->r08 diff looking for any
corruption/errors/whatever.

----

Review finished.

r08 diff review notes:
- fc/controller/gamepad/gamepad.cpp:
  use uint device = ID::Device::Gamepad; not id = ...;
- gb/cartridge/cartridge.hpp:
  remove redundant uint _pathID; (in Information::pathID already)
- gb/cartridge/cartridge.hpp:
  pull sha256 inside Information
- sfc/cartridge/load/cpp:
  add " - Slot (A,B)" to interface->load("Sufami Turbo"); to be more
  descriptive
- sfc/controller/gamepad/gamepad.cpp:
  use uint device = ID::Device::Gamepad; not id = ...;
- sfc/interface/interface.cpp:
  remove n variable from the Multitap device input generation loop
  (now unused)
- sfc/interface/interface.hpp:
  put struct Port above struct Device like the other classes
- ui-tomoko:
  cheats.bml is reading from/writing to mediumPaths(0) [system folder
  instead of game folder]
- ui-tomoko:
  instead of mediumPaths(1) - call emulator->metadataPathID() or something
  like that
2016-06-24 22:16:53 +10:00
Tim Allen
ccd8878d75 Update to v099r07 release.
byuu says:

Changelog:
- (hopefully) fixed BS Memory and Sufami Turbo slot loading
- ported GB, GBA, WS cores to use nall/vfs
- completely removed loadRequest, saveRequest functionality from
  Emulator::Interface and ui-tomoko
  - loadRequest(folder) is now load(folder)
- save states now use a shared Emulator::SerializerVersion string
  - whenever this is bumped, all older states will break; but this makes
    bumping state versions way easier
  - also, the version string makes it a lot easier to identify
    compatibility windows for save states
- SNES PPU now uses uint16 vram[32768] for memory accesses [hex_usr]

NOTE: Super Game Boy loading is currently broken, and I'm not entirely
sure how to fix it :/
The file loading handoff was -really- complicated, and so I'm kind of
at a loss ... so for now, don't try it.
Everything else should theoretically work, so please report any bugs
you find.

So, this is pretty much it. I'd be very curious to hear feedback from
people who objected to the old nall/stream design, whether they are
happy with the new file loading system or think it could use further
improvements.

The 16-bit VRAM turned out to be a wash on performance (roughly the same
as before. 1fps slower on Zelda 3, 1fps faster on Yoshi's Island.) The
main reason for this was because Yoshi's Island was breaking horribly
until I changed the vramRead, vramWrite functions to take uint15 instead
of uint16.

I suspect the issue is we're using uint16s in some areas now that need
to be uint15, and this game is setting the VRAM address to 0x8000+,
causing us to go out of bounds on memory accesses.

But ... I want to go ahead and do something cute for fun, and just because
we can ... and this new interface is so incredibly perfect for it!! I
want to support an SNES unit with 128KiB of VRAM. Not out of the box,
but as a fun little tweakable thing. The SNES was clearly designed to
support that, they just didn't use big enough VRAM chips, and left one
of the lines disconnected. So ... let's connect it anyway!

In the end, if we design it right, the only code difference should be
one area where we mask by 15-bits instead of by 16-bits.
2016-06-24 22:09:30 +10:00
Tim Allen
875f031182 Update to v099r06 release.
byuu says:

Changelog:
- Super Famicom core converted to use nall/vfs
  - excludes Super Game Boy; since that's invoked from inside the GB core

This was definitely the major obstacle to test nall/vfs'
applicability. Things worked out pretty great in the end.

We went from 22.0KiB (cartridge) + 18.6KiB (interface) to 24.5KiB
(cartridge) + 11.4KiB (interface). Or 40.7KiB to 36.0KiB. This removes
a very large source of indirection. Before it was: "coprocessor <=>
cartridge <=> interface" for loading and saving data, and now it's just
"coprocessor <=> cartridge". And it may make sense to eventually turn
this into just "cartridge -> coprocessor" by making each coprocessor
class handle its own markup parsing.

It's nice to have all the manifest parsing in one location (well, sans
MSU1); but it's also nice for loading/unloading to be handled by each
coprocessor itself. So I'll have to think longer about that one.

I've also started handling Interface::save() differently. Instead of
keeping track of memory IDs and filenames, and iterating through that
vector of objects ... instead I now have a system that mirrors the markup
parsing on loading, but handles saving instead. This was actually the
reason the code size savings weren't more significant, but I like this
style more. As before, it removes an extra level of indirection.

So ... next up, I need to port over the GB, then GBA, then WS
cores. These shouldn't take too long since they're all very simple with
just ROM+RAM(+RTC) right now. Then get the SGB callbacks using vfs. Then
after that, gut all the old stream stuff from nall and higan. Kill the
(load,save)Request stuff, rename the load(Gamepak)Request to something
simpler, and then we should be good.

Anyway ... these are some huge changes.
2016-06-24 22:01:03 +10:00
Tim Allen
f04d9d58f5 Update to v099r05 release.
byuu says:

Changelog:
- added nall/vfs
- converted Famicom core to use nall/vfs interface instead of nall/stream
  interface
2016-06-20 21:00:32 +10:00
Tim Allen
40abcfc4a5 Update to v099r04 release.
byuu says:

Changelog:
- lots of code cleanups to processor/r6502 (the switch.cpp file is only
  halfway done ...)
- lots of code cleanups to fc/cpu
- removed fc/input
- implemented fc/controller

hex_usr, you may not like this, but I want to keep the controller port
and expansion port interface separate, like I do with the SNES. I realize
the NES' is used more for controllers, and the SNES' more for hardware
expansions, but ... they're not compatible pinouts and you can't really
connect one to the other.

Right now, I've only implemented the controller portion. I'll have to
get to the peripheral portion later.

Also, the gamepad implementation there now may be wrong. It's based off
the Super Famicom version obviously. I'm not sure if the Famicom has
different behavior with latching $4016 writes, or not. But, it works in
Mega Man II, so it's a start.

Everyone, be sure to remap your controls, and then set port 1 -> gamepad
after loading your first Famicom game with the new WIP.
2016-06-18 16:04:32 +10:00
Tim Allen
44a8c5a2b4 Update to v099r03 release.
byuu says:

Changelog:
- finished cleaning up the SFC core to my new coding conventions
- removed sfc/controller/usart (superseded by 21fx)
- hid Synchronize Video option from the menu (still in the configuration
  file)

Pretty much the only minor detail left is some variable names in the
SA-1 core that really won't look good at all if I move to camelCase,
so I'll have to rethink how I handle those. It's probably a good area
to attempt using BitFields, to see how it impacts performance. But I'll
do that in a test branch first.

But for the most part, this should be the end of the gigantic diffs (this
one was 174KiB), at least for the SFC/WS cores. Still have the FC/GB/GBA
cores to clean up more fully. Assuming we don't spot any new regressions,
we should be ~95% out of the woods on code cleanups breaking things.
2016-06-17 23:03:54 +10:00
Tim Allen
f1a80075fa Update to v099r02 release.
byuu says:

Changelog:
- renamed sfc/ppu/sprite (OAM oam;) to sfc/ppu/object (Object obj;) [hex_usr]
- renamed sfc/ppu's memory {vram, oam, cgram} to just vram, oam, cgram
- fixed addr&=~1 regression [hex_usr]
- fixed 8bpp tiledata regression [hex_usr]
2016-06-15 21:32:17 +10:00
Tim Allen
ae5b4c3bb3 Update to v099r01 release.
byuu says:

Changelog:
- massive cleanups and optimizations on the PPU core
- ~9% speedup over v099 official

This is pretty much it for the low-hanging fruit of speeding up higan. Any
more gains from this point will be extremely hard-fought, unfortunately.
2016-06-14 20:51:54 +10:00
Tim Allen
c074c6e064 Update to v099 release.
byuu says:

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

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

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

Changelog (since v098):

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

Off the bat, here are the known bugs:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

There was a minor SNES input regression spotted very shortly after
release.
2016-04-09 12:43:12 +10:00
Tim Allen
e846c83d47 Update to v098 release.
byuu says:

This release most notably adds WonderSwan and WonderSwan Color
emulation.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Absolutely major improvements to the WS/C emulation today.

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

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

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

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

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

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

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

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

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

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

EDIT: oops, bad typo in the code.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GunPey now runs, as long as you add:

    eeprom name=save.ram size=0x800

to the manifest after importing with icarus.

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

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

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

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

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

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

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

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

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

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

To compile on Windows:

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

    return slice(result, 0, 3);

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

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

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

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

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

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

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

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

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

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

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

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

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

This is a few days old, but oh well.

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

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

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

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

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

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

Nothing WS-related this time.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

More V30MZ implemented, a lot more to go.

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

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

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

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

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

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

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

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

Note: balanced/performance profiles still broken, sorry.

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

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

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

A minor WIP to get us started.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Installation Commands:

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

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

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

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

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

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

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

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

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

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

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

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

Changelog:

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

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

It's a boring WIP, I just wanted to push out the project structure
change now at the start of this WIP cycle.
2015-12-30 17:54:59 +11:00
1785 changed files with 87148 additions and 160878 deletions

5
.gitignore vendored
View File

@@ -1,2 +1,3 @@
ananke/libananke.so
icarus/icarus
higan/profile/WonderSwan.sys/internal.ram
higan/profile/WonderSwan Color.sys/internal.ram
docs_build/

39
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,39 @@
# 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
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++
- mkdir higan-nightly
- cp -a icarus/out/icarus higan-nightly/icarus
- cp -a icarus/Database higan-nightly/
- cp -a higan/out/higan higan-nightly/higan
- cp -a higan/systems/* higan-nightly/
- cp -a shaders "higan-nightly/Video Shaders"
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.
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"
- mkdir higan-nightly
- cp -a icarus/out/icarus higan-nightly/icarus.exe
- cp -a icarus/Database higan-nightly/
- cp -a higan/out/higan higan-nightly/higan.exe
- cp -a higan/systems/* higan-nightly/
- cp -a shaders "higan-nightly/Video Shaders"
artifacts:
paths:
- higan-nightly/*

9
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,9 @@
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,
and post your idea in a new topic there.
[f]: https://board.byuu.org/

View File

@@ -1,105 +0,0 @@
include nall/GNUmakefile
fc := fc
sfc := sfc
gb := gb
gba := gba
profile := accuracy
target := tomoko
# console := true
# compiler
flags += -I. -O3
objects := libco
# profile-guided optimization mode
# pgo := instrument
# pgo := optimize
ifeq ($(pgo),instrument)
flags += -fprofile-generate
link += -lgcov
else ifeq ($(pgo),optimize)
flags += -fprofile-use
endif
# platform
ifeq ($(platform),windows)
ifeq ($(console),true)
link += -mconsole
else
link += -mwindows
endif
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
link += -Wl,-enable-auto-import
link += -Wl,-enable-runtime-pseudo-reloc
else ifeq ($(platform),macosx)
flags += -march=native
else ifeq ($(platform),linux)
flags += -march=native -fopenmp
link += -fopenmp
link += -Wl,-export-dynamic
link += -lX11 -lXext
else ifeq ($(platform),bsd)
flags += -march=native -fopenmp
link += -fopenmp
link += -Wl,-export-dynamic
link += -lX11 -lXext
else
$(error unsupported platform.)
endif
ui := target-$(target)
# implicit rules
compile = \
$(strip \
$(if $(filter %.c,$<), \
$(compiler) $(cflags) $(flags) $1 -c $< -o $@, \
$(if $(filter %.cpp,$<), \
$(compiler) $(cppflags) $(flags) $1 -c $< -o $@ \
) \
) \
)
%.o: $<; $(call compile)
all: build;
obj/libco.o: libco/libco.c libco/*
include $(ui)/GNUmakefile
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
# targets
clean:
-@$(call delete,out/*)
-@$(call delete,obj/*.o)
-@$(call delete,obj/*.a)
-@$(call delete,obj/*.so)
-@$(call delete,obj/*.dylib)
-@$(call delete,obj/*.dll)
archive:
if [ -f higan.tar.xz ]; then rm higan.tar.xz; fi
tar -cJf higan.tar.xz `ls`
sync:
ifeq ($(shell id -un),byuu)
if [ -d ./libco ]; then rm -r ./libco; fi
if [ -d ./nall ]; then rm -r ./nall; fi
if [ -d ./ruby ]; then rm -r ./ruby; fi
if [ -d ./hiro ]; then rm -r ./hiro; fi
cp -r ../libco ./libco
cp -r ../nall ./nall
cp -r ../ruby ./ruby
cp -r ../hiro ./hiro
rm -r libco/doc
rm -r libco/-test
rm -r nall/-test
rm -r ruby/-test
rm -r hiro/-test
endif
help:;

48
README.md Normal file
View File

@@ -0,0 +1,48 @@
The unofficial higan repository
===============================
higan emulates a number of classic video-game consoles of the 1980s and 1990s,
allowing you to play classic games on a modern general-purpose computer.
This repository includes
the source-code for
stable and WIP releases of higan,
starting during the development of v068.
It also includes community-maintained documentation.
Basically,
apart from `.gitignore` files,
anything in the
[higan](higan/),
[hiro](hiro/),
[icarus](icarus/),
[libco](libco/),
[nall](nall/),
[ruby](ruby/),
or [shaders](shaders/)
directories should be exactly as it appeared in official releases.
Everything else has been added for various reasons.
Official higan resources
------------------------
- [Official homepage](https://byuu.org/emulation/higan/)
- [Official forum](https://board.byuu.org/viewforum.php?f=4)
Unofficial higan resources
--------------------------
- Documentation for
[the current stable version][stadocs]
- [Source code repository](https://gitlab.com/higan/higan/)
archives official higan releases
and WIP snapshots
since approximately v067r21
- [Latest WIP build for Windows][wipwin]
- Documentation for
[the latest WIP version][wipdocs]
[wipwin]: https://gitlab.com/higan/higan/-/jobs/artifacts/master/download?job=windows-x86_64-binaries
[stadocs]: https://higan.readthedocs.io/
[wipdocs]: https://higan.readthedocs.io/en/latest/

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

12
docs/checklinks.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
# This uses the official W3C link-checker tool:
#
# https://github.com/w3c/link-checker
#
checklink \
--summary \
--broken \
--location=http://127.0.0.1:8000/ \
--exclude 'github.com|board.byuu.org' \
http://127.0.0.1:8000/

View File

@@ -0,0 +1,146 @@
A game folder
is higan's way of grouping all
the information and resources required
to properly emulate a particular game.
For example,
to represent a Super Famicom game named `dkc3`,
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
+- Super Famicom
|
+- dkc3.sfc
|
+- program.rom
|
+- save.ram
```
In contrast,
other emulators typically
group resources related to a game
by requiring that every file has the same base name
but different file extensions.
For example,
if another emulator loaded the game `dkc3.sfc`
it might store the save data in `dkc3.srm`:
```text
+- Super Famicom
|
+- dkc3.sfc
|
+- dkc3.srm
```
Why game folders?
-----------------
A file extension doesn't offer
much room for description,
so the traditional name-based-grouping system
only really works when games use a small number of files,
Also,
since file extensions traditionally describe
the format of the file in question,
it also means a game can't use
two or more files in the same format.
Compared to other emulators,
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]
supports up to 99 audio tracks per game,
and higan supports up to 133 save-states per game.
Thus,
higan suffers from the limitations of name-based-grouping
more than most.
higan's game folders allow a game
to have unique, descriptive filenames
for all its resources,
and for each file to use the extension
that's most appropriate.
They also allow emulator-specific extras
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
[firmware]: ../guides/import.md#games-with-co-processor-firmware
For a more detailed motivation for game folders,
see [Game Paks on the higan website][gp].
[gp]: https://byuu.org/emulation/higan/game-paks
What's in a game folder?
------------------------
A game folder collects all the information relevant
to emulating a particular game.
Not all of the following files
are relevant for every console,
or even for every game on a console,
but they may be present under particular circumstances.
All the files directly in the game folder
are expected to be useful
to all emulators that support them:
- `manifest.bml`:
The [manifest](manifests.md) for this game folder.
- `program.rom`:
For most consoles,
this contains
the executable instructions and graphics data
from the cartridge's ROM chips.
For the Famicom,
this contains only the executable instructions.
- `character.rom`:
For the Famicom,
this contains only the graphics data
from the cartridge's ROM chips.
- `ines.rom`:
While other consoles typically include enough hints
in `program.rom` for icarus to guess a working manifest,
the Famicom does not.
Famicom games not stored in game folders
typically include an "iNES header" to store that information,
which icarus preserves after import as `ines.rom`.
- `save.ram`:
Games that include a save feature
will create this file.
Note that it is only written to disk
when higan exits gracefully;
if higan crashes or is forced to quit,
in-game saves may be lost.
Other emulators sometimes call this an "SRAM file",
even though the same filename is used
with games that included EEPROM or Flash storage,
not just battery-backed Static RAM.
- `rtc.ram`:
Games that include a calendar or real-time clock
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].
- `track-*.pcm`:
Holds streamable audio for [the MSU-1][msu1].
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)
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.

View File

@@ -0,0 +1,87 @@
The game library
is the folder where all the
[game folders](game-folders.md) go.
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),
higan shows you the games for that console
that are already in the library.
Inside the game library folder
there is a sub-folder for each system.
Inside each system folder
are the game folders
for each imported game.
Why a game library?
-------------------
higan [requires game folders](game-folders.md#why-game-folders),
but it also invented game folders,
so existing games need
to be converted to game folder format
before higan can play them.
The converted copy of a game needs to go *somewhere*.
Also,
for people who have a huge collection of games,
the game library only lists the games they actually play,
rather than every possible game,
making it easier to find the games they like.
Where is the game library?
--------------------------
In Windows,
the default location of
the game library is the `Emulation` folder
inside your profile folder
(To find your profile folder,
press `Win+R` to open the Run dialog,
then type `%USERPROFILE%` and press Enter).
In Linux,
the default location of
the game library is the `Emulation` directory
inside your home directory.
On all platforms,
the game library location can be configured.
See [Moving the Game Library](#moving-the-game-library)
below.
Moving the game library
-----------------------
Moving the game library is a little complicated,
because there are two parts to it:
telling icarus where to put imported games,
and telling higan where to find them.
1. Move your existing game library folder
to the new location,
or otherwise create the folder you want higan to use
as its game library.
1. Launch icarus,
then click the "Settings ..." button in the lower-right,
to open
[the Settings dialog][icsettings].
1. Click the "Change ..." button on the right.
A [filesystem browser][fsbrowser] window will appear,
allowing you to choose
where imported games will be stored.
1. Launch higan,
then from [the Settings menu][settingsmenu],
choose "Advanced ..."
to open [higan's Advanced settings][advsettings].
1. Click the "Change ..." button on the right.
A [filesystem browser][fsbrowser] will appear,
allowing you to choose the same folder again.
[icsettings]: ../interface/icarus.md#the-icarus-settings-dialog
[fsbrowser]: ../interface/common.md#the-filesystem-browser
[settingsmenu]: ../interface/higan.md#the-settings-menu
[advsettings]: ../interface/higan-settings.md#advanced

142
docs/concepts/manifests.md Normal file
View File

@@ -0,0 +1,142 @@
If a [game folder](game-folders.md) is
the emulation equivalent of
a physical game cartridge,
a manifest is like
the circuit board inside the cartridge:
it connects all the other parts together
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).
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,
or at least
to guess a *reasonable* configuration.
However,
relying on such heuristics often leads to problems:
- Inventing heuristics
that correctly guess the configuration
for a particular game is very difficult.
- If you change the rules,
you really need to re-test
the console's entire game library
to check you haven't broken anything.
- Some games accidentally do the wrong thing.
- The Mega Drive game *Warrior of Rome II*
follows the rule for a European game running at 50fps,
when it's really an American game designed for 60fps.
- Some games deliberately do the wrong thing.
- The Game Boy Advance game *Top Gun - Combat Zones*
follows the rule for
(several kinds of)
save-game storage.
If it detects any save-game storage,
it assumes it's running in an emulator
and disables all the main menu options.
- Heuristics are difficult to reason about.
- If a homebrew developer
takes a game that almost does what they want
and changes it slightly,
it may cause the emulator
to do something completely different
because the modified game
is now a closer match to some other game.
- Heuristics can make sensible configurations impossible.
- If a homebrew developer wants
a particular hardware configuration
that no official games happened to use,
it may not be possible
to trigger it heuristically.
- It's not hardware accurate.
- The original console did not use heuristics
to guess a hardware configuration,
it just used the actual configuration
of the actual hardware.
Manifests provide a way to describe
*exactly* what hardware configuration to use,
no guessing required,
avoiding all these problems entirely.
Where do manifests come from?
-----------------------------
Ideally,
everybody who extracts the data from a game cartridge would
also record the board configuration in a manifest file.
Unfortunately,
manifests were invented long after cartridge extracting,
so there are a lot of previously extracted games
with no manifest attached.
If a game doesn't come with a manifest,
it may be possible to look up the correct manifest.
The
[SNES Preservation Project](https://preservation.byuu.org/)
intends to re-dump every Super Famicom game
and record its board configuration at the same time.
Given a game's data,
you can take the SHA256 fingerprint
and look it up in the Project's database
to find the correct manifest to use.
Unfortunately,
this doesn't help for any other console.
Even for the Super Famicom,
there will always be games
too rare to acquire for re-dumping,
and homebrew that was never on a physical cartridge to begin with.
For these games,
heuristics will always be needed as a fallback,
but at least if the heuristics are wrong
they can be overridden.
Ignoring manifests
------------------
Occasionally,
a newly-dumped game will turn out to have
a configuration that can't be expressed
in the existing manifest file format,
and a new format must be designed.
If manifests were always written inside game folders,
games with old-format manifests
would break when played in emulators that supported the new format.
Therefore,
icarus [defaults](../interface/icarus.md#the-icarus-settings-dialog)
to not writing out manifests when it imports games,
and higan [defaults](../interface/higan-settings.md#advanced)
to ignoring manifests that are present.
Instead,
when higan loads a game,
it will ask icarus to generate a temporary manifest in the latest format,
based on the files present in the game folder
and how they are likely to go together.
If the manifest that icarus generates
for some particular game
is broken or buggy,
you'll need to switch the manifest machinery back on
so you can fix it:
- Turn on "Create manifests" in icarus' settings
- Re-import the game in question
to store the broken manifest
as `manifest.bml` in the game folder.
- Edit `manifest.bml` as you see fit
- Turn off "Ignore manifests" in higan's settings
- Load the game in higan to check that your changes fixed the problem
If the manifest format changes in the future,
you'll have to repeat this process
to create a fixed manifest in the new format.

View File

@@ -0,0 +1,129 @@
A real game console
is a complex piece of hardware,
with electricity flowing through it
in complex patterns that can't easily
be paused or recorded.
However,
an emulated console is pure software:
it only changes when the emulation software
deliberately updates it,
so the emulator software can save
the entire state of the emulated console
to disk,
and weeks or months later
bring it back to life
as though nothing had ever happened.
Save states versus in-game saves
--------------------------------
Some games include their own systems
for saving and restoring the player's progress.
Here are some of the differences
between save states and in-game saves:
<table>
<thead>
<tr>
<th>Save states</th>
<th>In-game saves</th>
</tr>
</thead>
<tbody>
<tr>
<td>
Work exactly the same way
in every game
</td>
<td>
Works differently in different games,
some games don't support it at all
</td>
</tr>
<tr>
<td>
Save at any time,
anywhere in the game
</td>
<td>
Save only at special save-points
</td>
</tr>
<tr>
<td>
Loading a save
puts the game back exactly how it was
when you pressed save
</td>
<td>
Loading a save
restores some things
(like the player's inventory
or remaining lives),
but may
forget others
(like taking you back to the beginning of the level)
</td>
</tr>
<tr>
<td>
You can have dozens of save states
</td>
<td>
Most games limit you to about 3 saves
</td>
</tr>
<tr>
<td>
Can only be loaded
by the same version of the same emulator
that created it
</td>
<td>
Works with any version of any emulator,
can sometimes even be copied to or from physical cartridges
</td>
</tr>
</tbody>
</table>
**Note:**
Loading a save state
will reset the entire emulated console
to the way it was when the save state was created,
*including in-game saves*.
If you create a save state,
then make an in-game save,
then load the save state,
**your in-game save will be lost**.
Don't do that.
Quick states
------------
higan has five Quick State slots,
which can be used from
[the Tools menu](../interface/higan.md#the-tools-menu),
or with the appropriate
[hotkeys](../interface/higan-settings.md#hotkeys).
Quick states are useful
as extra checkpoints
in games whose checkpoints are too far apart,
or in games that don't have checkpoints at all.
Map the "Save Quick State" and "Load Quick State" hotkeys
to your controller,
and you can cheese your way through just about anything.
Manager states
--------------
higan's
[State Manager](../interface/higan-tools.md#the-state-manager)
allows you to create over a hundred save states,
and add a helpful description to each one.
Manager States are more cumbersome to use than Quick States,
since they cannot be accessed with hotkeys,
but are useful when you want quick access
to many different parts of a game.

283
docs/faq.md Normal file
View File

@@ -0,0 +1,283 @@
I see "tearing" when a game scrolls. How can I enable vsync?
------------------------------------------------------------
higan supports synchronising video output
to the display's vertical-synchronisation (or "vsync") signal,
but the option is hidden
because it often causes more problems than it solves
(see the next question).
To enable video synchronisation:
- Open higan's configuration file, `settings.bml`
- On Windows, look in `%LOCALAPPDATA%\higan`
or beside `higan.exe`
- On Linux, look in `~/.local/share/higan`
- Open it in your favourite text editor
(Windows Notepad will corrupt the file,
use WordPad if you don't have anything better)
- Find the line that says "Video"
- Somewhere below that, find an indented line
that says "Synchronize:false".
- Change "false" to "true"
- Save your changes to `settings.bml`
and restart higan
Why is video synchronisation a problem for higan?
-------------------------------------------------
**The short version:**
Turning on video synchronisation
cleans up video tearing,
turning on audio synchronisation
cleans up audio glitches,
but turning on both
makes audio glitches worse.
**The long version:**
Enabling video synchronisation
locks the frame-rate of the emulated console
to the frame-rate of your computer's display.
If your display's refresh rate exactly matches
the emulated console's,
games play at the correct speed
and everything's fine.
However,
modern 60Hz displays do not always match
the emulated console's refresh rate:
- The Super Famicom usually runs a little faster than 60Hz
- the PAL variants of most consoles run at 50Hz
- the WonderSwan runs at 75Hz
- While the Game Boy does run its LCD at 60Hz,
games can turn the LCD off and on at any time,
requiring emulation to pause
until it can get back in sync
with the computer display.
Because of these frame-rate differences,
enabling video synchronisation
can force games to run
faster or slower than intended.
The consoles that higan emulates
produce video frames and audio samples at a particular rate.
If video synchronisation causes
the emulated console to run, say, 5% faster than intended,
that means audio samples are also being produced 5% faster.
You might not notice the changed game speed,
but you'll almost certainly notice
the game's audio glitching constantly
as your sound card tries to keep up.
Enabling
[audio synchronisation](interface/higan.md#the-settings-menu)
normally fixes this kind of audio glitching,
but with video synchronisation it makes things worse:
audio is likely to glitch
while higan waits for a video frame to be shown,
and video is likely to stutter
while higan waits for an audio buffer to complete.
Games run too fast
------------------
higan runs as fast as it can,
but it will pause and wait
for the audio and video drivers to catch up
if [audio synchronisation](interface/higan.md#the-settings-menu)
and [video synchronisation][vsync]
are enabled, respectively.
If games are running way too fast, here are some things to check:
- Make sure "Synchronize Audio" is ticked in
[the Settings menu](interface/higan.md#the-settings-menu)
- Make sure the Audio driver is not set to "None"
in [the Advanced settings](interface/higan-settings.md#advanced)
(remember to restart higan if you change driver settings)
- Make sure your computer has speakers or headphones connected
(some computers disable all audio if no outputs are available)
- If you want the game to be silent,
tick "Mute Audio" in
[the Settings menu](interface/higan.md#the-settings-menu)
- If you can't connect speakers or headphones to your computer,
or you did but it didn't help,
try enabling the secret [video synchronisation][vsync] option.
[vsync]: #i-see-tearing-when-a-game-scrolls-how-can-i-enable-vsync
Games run too slow
------------------
Of all the consoles higan can emulate,
higan's Super Famicom emulation
is the most resource intensive.
Full-speed emulation for the Super Famicom base unit
requires an Intel Core 2 Duo (or AMD equivalent),
full-speed for games with the SuperFX chip
requires an Intel Ivy Bridge (or equivalent),
full-speed for the wire-frame animations in *Mega Man X2*
requires an even faster computer.
Low-power CPUs like ARM chips,
or Intel Atom and Celeron CPUs
generally aren't fast enough to emulate the Super Famicom with higan,
although other emulated consoles may work.
If your computer meets the general speed requirements
but games run too slowly,
try choosing a different
[audio driver](interface/higan-settings.md#advanced),
since that's usually what drives higan's timing.
On some computers,
the operating system's power-management system
may be confused by higan's unusual pattern of CPU usage
(it runs furiously to calculate the next video frame
and the next few milliseconds of audio,
then stops dead as it waits for output to complete).
If holding down
the [fast forward hotkey](interface/higan-settings.md#hotkeys)
runs too fast but the game normally runs too slow,
try disabling "power saver" mode
or enabling "performance" mode.
On Linux specifically,
if your desktop environment doesn't provide a way
to change the CPU usage governor,
you can temporarily force your CPUs to run at full-speed
by running the following command in a terminal:
echo "performance" | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
To put things back to normal,
reboot the computer, or run this command instead:
echo "ondemand" | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
Why is higan so much slower than other emulators?
-------------------------------------------------
The consoles higan emulates
generally contain different devices
(say, a CPU, an audio chip and a video chip)
that do their own thing,
but can interrupt each other at any time.
Games can and do depend on timing details like
"when the audio chip is done playing this sound,
it will interrupt the CPU at exactly the right time
for the CPU to fiddle with the video chip".
higan is therefore very cautious about timing:
while it's emulating the CPU (for example),
at every point the emulated audio chip *might* interrupt
the emulated CPU,
higan switches to emulating the audio chip up to the same point
to find out whether it *will* interrupt the CPU.
In this way,
higan is a little bit like
an office-worker trying to do the jobs of three other people
by running from desk to desk,
sending the same emails
that those three people would send to each other,
leaving itself a note at each desk to remind it
where it was up to when it comes back.
Although this constant switching
is slow and inefficient,
higan does it
to ensure the emulated console
always matches the behaviour
of the original.
Other emulators,
especially ones that are popular on phones and tablets,
typically work differently.
Although emulated devices *can* interrupt each other at any time,
usually they don't,
and often the only exceptions are a handful of unpopular games.
So it's pretty safe
(and much, much more efficient)
to let certain devices run "ahead" of the others.
For example,
letting the video chip render an entire scanline at a time
instead of working pixel-by-pixel.
The downside is that some games don't work properly
in these emulators,
with game-breaking bugs,
minor visual glitches,
or anything in between.
Some emulators try to recognise
when the user loads a specific problematic game,
and will adjust the shortcuts the emulator takes
so that the game runs correctly.
This is a good way to increase game compatibility
without losing efficiency,
but it means the emulator does not accurately reflect
how the original console worked,
and it requires the emulator authors to diagnose and work around
each individual supported game,
instead of having one emulator that works for everything.
Why can't higan use multiple CPU cores?
---------------------------------------
These days,
most computers contain multiple CPU cores,
allowing them to run different programs
(or different parts of the same program)
at the same time.
Since higan requires high CPU performance,
sometimes people suggest that it should split its work
into multiple threads
so it can run across multiple cores
instead of stressing one core while the others lie idle.
After all,
the original consoles higan emulates
had separate devices running independently,
so why not run each emulated device in a separate thread?
The big problem with this approach is timing.
The devices in a physical Super Famicom (for example)
run at whatever speed they run at,
and talk to each other whenever they feel like.
Because every Super Famicom uses the exact same components,
every Super Famicom runs compatible games at the exact same speed,
and games can blindly assume that
when operation X is complete on device A,
device B will have finished operation Y
and be ready to do something new.
Meanwhile, higan's emulated components
take an unpredictable amount of time to do their work,
so without deliberate synchronisation
things would break almost immediately.
It's not practical to make higan's emulated devices
do their work in exactly the same amount of time
as their hardware counterparts.
The problem is forty years of technology
designed to make programs run as fast as possible:
optimising compilers
and super-scalar, out-of-order CPU architectures
change programs to make them faster,
speeding up some programs more than others
in ways that are very difficult to understand and predict.
Even if higan's emulated devices
ran at the exact, correct speed
on one particular computer,
they'd still run differently on
a computer with a smarter CPU,
or when compiled with a smarter compiler.
Since higan needs its emulated components
to run at particular speeds,
and it's not practical
to make them run at those speeds naturally,
it must force them manually.
An emulated device runs for a little while,
then all the others are run until they catch up.
It's this careful management,
regular stopping and starting,
that makes higan slow,
not the actual emulation of each device.
Having multiple CPU cores waiting on each other
would not help them wait any faster.

143
docs/guides/drivers.md Normal file
View File

@@ -0,0 +1,143 @@
Unfortunately,
there's no cross-platform standard for
displaying video,
playing audio,
and accepting input from game controllers.
Or rather,
there are many standards,
and different ones work best
on different computers.
Therefore,
higan comes with "drivers"
for video, audio and input,
so you can find the one that works best for you.
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.
Here are the most notable drivers
for each platform
in each category.
If your copy of higan
includes a driver not listed here,
it's probably a reasonable choice,
so try it out and see how you like it.
**Note:** After changing any driver,
you must restart higan for the change to take effect.
**Note:**
Video, Audio and Input
all have a special driver named "None".
This is a dummy driver that does nothing
(draws no video,
plays no audio,
accepts no input),
so don't choose it unless you're
trying to diagnose problems with other drivers.
Video
-----
On Windows:
- **OpenGL** is the best choice,
since it's fast
and it supports [custom shaders](shaders.md),
but requires support for OpenGL 3.2.
This excludes some older and weaker hardware.
- **Direct3D** is a good choice
if OpenGL is unavailable.
It also allows
[Exclusive fullscreen](../interface/higan-settings.md#video),
bypassing Windows' desktop compositor.
- **GDI** is the safest choice,
but performs very poorly at large sizes.
On Linux:
- **OpenGL** is the best choice,
since it's fast
and it supports [custom shaders](shaders.md),
but requires support for OpenGL 3.2.
You can check what version of OpenGL
your system supports by running
`glxinfo | grep 'core profile version'`
in a terminal.
- **XVideo** is also fast,
but may be low-quality,
and generally only supports the "Blur" shader,
not "None".
- **XShm** is the safest choice,
but performs very poorly at large sizes.
Audio
-----
On Windows:
- **ASIO** offers the lowest possible latency,
but is the least likely to work on any given computer.
- **WASAPI** offers low latency,
but is only slightly more likely to work than ASIO.
It also offers
[Exclusive Mode](../interface/higan-settings.md#audio),
which can improve audio quality and lower latency,
but may be better or worse than shared mode
in practice.
- **XAudio2** is a good choice,
but it requires the latest (June 2010) version
of the [DirectX 9 End-User Runtime][dx9]
to be installed.
- **DirectSound** is the safest choice,
and not a bad one.
[dx9]: https://www.microsoft.com/en-us/download/details.aspx?id=35
On Linux:
- **PulseAudio** or **PulseAudioSimple**
are almost certainly the drivers to use,
since almost every Linux distribution uses
[PulseAudio](https://en.wikipedia.org/wiki/PulseAudio)
to manage audio output.
The two drivers should behave identically,
but some users report one working better than the other.
- **ALSA** is a good choice
for Linux distributions that do not use PulseAudio.
If PulseAudio is running,
this will wind up using PulseAudio's ALSA emulation,
or failing due to "sound card already in use",
unless you use a tool like `pasuspender`.
- **OSS** may be useful
for non-Linux platforms,
or for Linux systems with the third-party OSSv4
kernel drivers installed.
For most Linux systems,
this will use ALSA's OSS emulation,
PulseAudio's OSS emulation,
or not work at all.
**Note:**
By default,
higan uses the audio driver to regulate its emulation speed.
If games run too fast on your computer,
see [Games run too fast](../faq.md#games-run-too-fast) in the FAQ.
Input
-----
On Windows,
"Windows" is the only driver available,
and uses RawInput for keyboard and mouse input,
XInput for Xbox controllers,
and DirectInput for other controllers.
On Linux:
- **udev** supports every input device,
but requires a modern Linux system.
- **Xlib** is the safest choice,
but only supports keyboard and mouse input.

723
docs/guides/import.md Normal file
View File

@@ -0,0 +1,723 @@
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).
For [regular games](#regular-games)
this is simple,
but some games require special treatment,
especially those that make use of
unusual hardware.
Regular 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
(as long as the `.zip` file contains only one game).
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),
choose "Load ROM File ..."
to open [a filesystem browser](../interface/common.md#the-filesystem-browser),
choose the ROM file of the game you want to play,
and it will be imported into the library and 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.
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.)
then choose the appropriate console menu item.
A filesystem browser will appear
listing all the games in the library
for that particular console.
Select the game you want to play
and click the Open button,
or just double-click the game,
and it will begin playing.
**Note:**
Sometimes
the NTSC and PAL variants of a particular console
behave differently,
or the Japanese and American variants.
When choosing a game from the Game Library,
a drop-down list in the bottom-right of the filesystem browser
allows you to choose which regional variant
of the console higan should emulate.
For most consoles,
higan can reliably guess which variant to use,
and the list defaults to "Auto".
Games with co-processor firmware
--------------------------------
Many games included extra chips inside the game cartridge,
to provide enhanced capabilities of one kind or another.
Sometimes,
those extra chips were separate CPUs
running their own separate firmware,
and for those cases
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.
To import a game that requires co-processor firmware,
you must first 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`
and requires firmware for the DSP1 co-processor
stored in `dsp1.program.rom` and `dsp1.data.rom`.
On Windows,
you can combine them from the command-line like this:
```dos
copy /b sbl2.sfc + dsp1.program.rom + dsp1.data.rom
```
On Linux,
the equivalent command-line syntax is:
```bash
cat dsp1.program.rom dsp1.data.rom >> sbl2.sfc
```
(note the use of `>>` to append rather than `>` to overwrite)
**Note:**
For co-processor chips with multiple firmware files,
you must 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.
Once you've figured out which co-processor
(if any)
is used by the game you want to import,
here's the firmware files you'll need:
[wpec]: https://en.wikipedia.org/wiki/List_of_Super_NES_enhancement_chips#List_of_Super_NES_games_that_use_enhancement_chips
<table>
<thead>
<tr>
<th>Co-processor</th>
<th>Filename</th>
<th>Size (bytes)</th>
<th>SHA256</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">CX4</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>
<td><code>dsp1.data.rom</code></td>
<td>2048</td>
<td><code>0b5da6533e55852ee8fc397977ec5576c5b9f1fb2e05656d8f87123a121b076e</code></td>
</tr>
<tr>
<td><code>dsp1.program.rom</code></td>
<td>6144</td>
<td><code>269584b347a22953a2989494c850a7c1c027f4ca5add517a60e0c7d8833d0fac</code></td>
</tr>
<tr>
<th scope="row" rowspan=2>DSP1B<br><sup>See Note 2</sup></th>
<td><code>dsp1b.data.rom</code></td>
<td>2048</td>
<td><code>8546cbac530830446bb8a277f6b139d4ad64d650bdbac7e4e150e2f095665049</code></td>
</tr>
<tr>
<td><code>dsp1b.program.rom</code></td>
<td>6144</td>
<td><code>2eccb54a8f89374911f7e2db48f1b4cde855655e28103f7bda2982a5b418a187</code></td>
</tr>
<tr>
<th scope="row" rowspan=2>DSP2</th>
<td><code>dsp2.data.rom</code></td>
<td>2048</td>
<td><code>3beef9bffdc1e84c9f99f3301d8bd3e520d2e62909a995320f9faeae8f46ec11</code></td>
</tr>
<tr>
<td><code>dsp2.program.rom</code></td>
<td>6144</td>
<td><code>62a2ef8d2d7db638f4ec0fbcebf0e5bf18a75ee95be06e885d9519a10487f0da</code></td>
</tr>
<tr>
<th scope="row" rowspan="2">DSP3</th>
<td><code>dsp3.data.rom</code></td>
<td>2048</td>
<td><code>7fe51796e9c97fee1fa2aab40592b7c78997f67dd00333e69d0f79a12f3cb69f</code></td>
</tr>
<tr>
<td><code>dsp3.program.rom</code></td>
<td>6144</td>
<td><code>aea7b622e7c1de346cb15d16afcbedf92b6798507e179f83ed2a4cff40d0e663</code></td>
</tr>
<tr>
<th scope="row" rowspan="2">DSP4</th>
<td><code>dsp4.data.rom</code></td>
<td>2048</td>
<td><code>ef3ffb4256dd896a60213269b4e2d3bdd120c97e2fd623bddabbf43c2be422af</code></td>
</tr>
<tr>
<td><code>dsp4.program.rom</code></td>
<td>6144</td>
<td><code>89b1826e6038be3a0ea0f923e85d589ff6f02dc1a1819fb2ec8c0cea5b333dcd</code></td>
</tr>
<tr>
<th scope="row" rowspan="2">ST010</th>
<td><code>st010.data.rom</code></td>
<td>4096</td>
<td><code>dc7056a51b53993d7a8ba5bacf9501f785d2fce5e5be748e9ff2737c5938d4a5</code></td>
</tr>
<tr>
<td><code>st010.program.rom</code></td>
<td>49152</td>
<td><code>2c1f74bb5f466d81c64c326e71ac054489efe1abc9a5d6f91aac7747f2ddab67</code></td>
</tr>
<tr>
<th scope="row" rowspan="2">ST011</th>
<td><code>st011.data.rom</code></td>
<td>4096</td>
<td><code>b5377d1bebe8adc507a024b6e2b9b8fdf4877e451da84fbad05dff4e4a70311e</code></td>
</tr>
<tr>
<td><code>st011.program.rom</code></td>
<td>49152</td>
<td><code>d90a5cda380e81cb9ba11a9da7539b173c49b31bedc7a3ac9c3c8b3f97e89e14</code></td>
</tr>
<tr>
<th scope="row" rowspan="2">ST018</th>
<td><code>st018.data.rom</code></td>
<td>32768</td>
<td><code>b5377d1bebe8adc507a024b6e2b9b8fdf4877e451da84fbad05dff4e4a70311e</code></td>
</tr>
<tr>
<td><code>st018.program.rom</code></td>
<td>131072</td>
<td><code>d90a5cda380e81cb9ba11a9da7539b173c49b31bedc7a3ac9c3c8b3f97e89e14</code></td>
</tr>
</tbody>
</table>
**Note 1:**
The DSP1 and DSP1A are physically different,
but the firmware inside is identical.
**Note 2:**
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,
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,
a window will appear saying
"Import completed, but with 1 errors. View log?"
(or however many games were lacking the correct 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
If you try to import a game
using the "Load ROM File ..." option
in [the Library menu](../interface/higan.md#the-library-menu)
but it does not include the correct firmware data,
nothing will happen,
and higan will just sit there
with "No cartridge loaded" in
[the status bar](../interface/higan.md#the-status-bar).
Once a game with co-processor firmware is imported,
you can play it just like any [regular game](#regular-games).
Satellaview games
-----------------
The [Satellaview][wpbsx]
was a satellite modem peripheral
released for the Super Famicom in Japan.
As well as the actual modem
(designed to sit underneath the Super Famicom),
it also included a cartridge
with software to control the modem,
browse online services,
and download games and data.
This control cartridge was called
*BS-X Sore wa Namae o Nusumareta Machi no Monogatari*,
which in English is
*BS-X The Story of The Town Whose Name Was Stolen*.
[wpbsx]: https://en.wikipedia.org/wiki/Satellaview
The control cartridge had a slot that accepted
re-writable "memory paks",
so that people could store the games and data they downloaded.
A small number of games that did not use the Satellaview modem
also had a memory pak slot,
so the game's publishers could
publish extra content for the game
via the Satellaview service
after the game's release.
For the benefit of people who didn't own a Satellaview
some read-only memory paks
were sold in retail stores
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`
(if it's in a `.zip` file,
that's OK,
but the name *inside* the `.zip` file
must end in `.bs`)
in order for it to be successfully imported.
Sometimes memory pak filenames end in `.sfc`,
which will make higan try to import them as
regular Super Famicom games
and fail.
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)
to let you pick which previously-imported memory pak
you want to insert into the game.
If you press "Cancel" at this point,
the game will load without any cartridge in its memory pak slot.
If you load the control cartridge into higan,
make sure the emulated Satellaview
is connected to the emulated Super Famicom's expansion port
by opening the "Super Famicom" menu,
selecting the "Expansion Port" sub-menu,
and choosing "Satellaview".
If the expansion port was previously
configured with a different option,
power-cycle the Super Famicom
(also in the "Super Famicom" menu)
to make sure the control cartridge will find the Satellaview
when it starts up.
Note that higan's Satellaview emulation is not very accurate,
so the control cartridge may not work as it should.
Playing a memory pak on its own doesn't make much sense,
it's not a standalone cartridge.
Play a game with a memory pak slot,
and choose which memory pak you want when higan asks for it.
For more information about the Satellaview service,
a translation patch for the control cartridge
and emulators that do a better job of Satellaview emulation,
see [the BS-X Project](https://bsxproj.superfamicom.org/).
Sufami Turbo games
------------------
The [Sufami Turbo][wpst]
was a special cartridge released
for the Super Famicom in Japan.
The Sufami Turbo on its own does nothing,
but it has two slots in the top
that accept Sufami Turbo mini-cartridges.
The game in slot A is the one that actually plays,
but some games can make use of additional data
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`
(if it's in a `.zip` file,
that's OK,
but the name *inside* the `.zip` file
must end in `.st`)
in order for it to be successfully imported.
Sometimes mini-cartridge filenames end in `.sfc`,
which will make higan try to import them as
regular Super Famicom games,
and fail miserably.
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)
to let you pick which previously-imported mini-cartridge
you want to insert into slot A.
If you press "Cancel" at this point,
the Sufami Turbo cartridge will boot without anything in slot A,
which just displays an image telling you
to turn off your Super Famicom,
insert a game into slot A,
and try again.
If you chose a cartridge for slot A,
higan will open yet another
filesystem browser
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
Super Game Boy games
--------------------
The Super Game Boy was a special cartridge
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 cartridge includes
the complete hardware of an original
(black-and-white)
Game Boy,
so it needs a boot ROM:
<table>
<thead>
<tr>
<th>Cartridge</th>
<th>Filename</th>
<th>Size (bytes)</th>
<th>SHA256</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">SGB</th>
<td><code>sgb1.boot.rom</code></td>
<td>256</td>
<td><code>0e4ddff32fc9d1eeaae812a157dd246459b00c9e14f2f61751f661f32361e360</code></td>
</tr>
<tr>
<th scope="row">SGB2</th>
<td><code>sgb2.boot.rom</code></td>
<td>256</td>
<td><code>fd243c4fb27008986316ce3df29e9cfbcdc0cd52704970555a8bb76edbec3988</code></td>
</tr>
</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)
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.
**Note:**
Only games for the original, black-and-white Game Boy
can be used with the Super Game Boy.
Some games designed for the Game Boy Color
were backward compatible with the original Game Boy
and hence the Super Game Boy;
see [Playing Game Boy Color games in Game Boy mode][blackcarts]
for details.
[blackcarts]: ../notes.md#playing-game-boy-color-games-in-game-boy-mode
MSU-1 games
-----------
The MSU-1 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
to any particular storage medium,
it gives the Super Famicom similar capabilities
to CD-based add-ons
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,
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.
There are a number of homebrew games
that make use of the MSU-1,
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
is that early firmware versions of the SD2SNES
had a bug that caused MSU-1 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
"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.
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,
it may be that the existing audio is only slightly boosted,
so try the "hardware" version first, for best quality.
- If the audio tracks are heavily boosted,
the "hardware" patch may sound terrible,
distorting and clipping,
in which case try the "emulator" patch.
To import an MSU-1 game:
1. If you have a single, large file
with the `.msu1` extension,
that is a pack for use with [Mercurial Magic][mermag],
which can automatically set up a game folder
in the correct format.
Go read Mercurial Magic's documentation
instead of these instructions.
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,
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,
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],
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)
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`
- If there's no file by that name,
look for 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.
- If you have to choose between two sets of audio files,
see "One thing to be aware of..." above.
- These should be named
`track-1.pcm`,
`track-2.pcm`,
... `track-9.pcm`,
`track-10.pcm`,
etc.
- If there's no files with those names,
there should be other numbered `.pcm` files
that you can rename to match what higan expects.
- If the `.pcm` files have no numbers in the filenames,
there maybe a `.bml` or `.xml` file that lists
which number goes with which file.
- If there's no `.pcm` files at all,
that's OK,
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
[a regular game](#regular-games).
[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/
Patched games
-------------
The console emulation community
has a long and vibrant history of game modding,
or [ROM hacking][rhdn],
including fan-translations,
new levels for existing games,
and more.
Since distributing the modified versions of existing games
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].
higan does not support soft-patching,
so if you want to play a patched game in higan,
you will need to use a patcher to apply it yourself,
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/
Game Boy Advance games
----------------------
Before you can play Game Boy Advance games,
you must provide a copy of the Game Boy Advance BIOS.
Unlike game-specific firmware,
the GBA BIOS was part of the console,
not the cartridge,
so it must be installed
[into higan](../install/general.md).
Once the GBA BIOS is installed,
GBA games can be imported and played just like
[any other games](#regular-games).
Note that some GBA games
have trouble with
[in-game saves](../notes#in-game-saves-and-the-game-boy-advance).
PowerFest '94
-------------
[PowerFest '94](https://en.wikipedia.org/wiki/Nintendo_PowerFest_%2794)
was a video game competition
organised by Nintendo,
in which contestants had six minutes
to complete a challenge based on three Super Famicom games.
The PowerFest '94 cartridge
was custom-built for the competition,
and included the three base games
as well as software to run each game,
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.
You will need the following files:
<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.

215
docs/guides/shaders.md Normal file
View File

@@ -0,0 +1,215 @@
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
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).
Which shaders are available depends on
the [video driver](drivers.md#video) higan is configured to use.
Most drivers only support these shaders:
- **None**
draws each computer pixel according to
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],
neighbouring console pixels may be drawn
with a different number of computer pixels due to rounding errors,
causing a distracting rippling effect.
- **Blur**
draws each computer pixel according to
the weighted average colour
of the four nearest console pixels,
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.
**Note:**
For technical reasons,
higan's emulation of certain consoles
can produce surprising behaviour
in certain shaders,
particularly shaders that compare each console pixel
with its neighbours.
See [Video Shaders and TV-based consoles][vstv] for details.
[vstv]: #video-shaders-and-tv-based-consoles
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 write your own.
How to install custom shaders
-----------------------------
Make sure the shader you want to install
is in the correct format:
it should be a folder whose name ends in `.shader`,
it should contain a file named `manifest.bml`,
and probably some `*.fs` or `*.vs` files.
Place the shader folder inside
the `Video 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`.
- On Windows,
this is probably the folder containing `higan.exe`
- On Linux,
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.
Under "Driver Selection",
make sure "Video" is set to "OpenGL".
If it wasn't already set that way,
you'll need to restart higan
for the change to take effect.
Open the Settings menu again,
choose the "Video Shader" sub-menu,
and now the shaders you installed
should be listed at the bottom of the menu.
Load a game
(so you can see the results),
switch between shaders
to see what they do,
and pick your favourite!
Notable examples
----------------
The quark-shaders repository mentioned above
contains lots of carefully-crafted shaders,
but some are particularly noteworthy:
- **AANN** implements "anti-aliased nearest neighbour" scaling.
This uses anti-aliasing to hide
the rounding errors often introduced by
aspect ratio correction
and non-integral scaling,
producing an image nearly as crisp as the "None" shader,
but without the distracting ripple effect.
- **Gameboy** emulates the squarish aspect-ratio,
greenish-colours,
and limited palette
of the original Game Boy.
At larger scales,
you can even see the fine gaps between each pixel,
and the shadow that dark colours would cast
on the LCD background.
- **NTSC** performs NTSC encoding,
bandwidth limiting,
and NTSC decoding of the video image to recreate
the colour fringing,
blurring
and shimmer
that most game players would have seen
on real televisions.
Some games depend on NTSC artefacts
to display colours outside the console's official palette
or to create effects like transparency.
Video Shaders and TV-based consoles
-----------------------------------
Simple shaders
(like "None"
and the third-party "AANN" shader)
just blindly scale up the images they're given,
but sophisticated shaders
(such as the third-party "xBR" shader)
try to produce higher-quality output
by recognising particular patterns,
like taking three diagonal pixels
and turning that into a smooth diagonal line.
These shaders assume that
each pixel drawn by the game's artists
becomes a single pixel in the video output they analyze.
The hand-held consoles that higan emulates
(and also the Famicom)
can only output video at one specific resolution,
so this "one pixel equals one pixel" rule holds true,
and pattern-based shaders like "xBR" work just fine.
Unfortunately,
this is not true for most of the TV-based consoles
that higan supports.
The Super Famicom's "normal" video mode
draws 256 pixels across the width of the screen,
but the "high resolution" mode draws 512.
Since Super Famicom games can enable hi-res mode at any time
(even halfway through a frame),
higan always renders Super Famicom video output 512 pixels wide,
just in case.
This means that in "normal" mode,
each pixel drawn by the game's artists
becomes two pixels in the video output,
breaking the assumption
that pattern-based shaders are based on.
The Super Famicom has a similar issue in the vertical direction:
normally,
an NTSC-based Super Famicom draws about 240 rows of output every frame,
sometimes referred to as "240p" video.
When a game turns on "interlaced" mode,
it draws the 240 odd-numbered lines of one frame,
then the 240 even-numbered lines of the next,
and so forth.
This is sometimes referred to as "480i" video.
Although interlaced mode cannot be enabled mid-frame
like high-resolution mode,
resolution switching is still complex,
so higan always draws all 480 lines of video output.
This means for a normal, non-interlaced game,
each pixel drawn by the game's artists
becomes four pixels in the video output
(two horizontally and two vertically)
making pattern-based shaders even less useful.
It also breaks most scanline-emulation shaders,
since they typically draw a scanline
for each row of pixels in the video output.
The Mega Drive has similar problems
to the Super Famicom.
It has the same behaviour with interlacing,
but its high-resolution mode switches
from 256 pixels across to 320 pixels across.
Therefore in normal mode,
each pixel drawn by the game's artists
becomes five pixels in the video output,
while in high-resolution mode,
each pixel drawn by the game's artists
becomes four pixels in the video output
(or 10 and 8 pixels in non-interlaced mode).
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.

122
docs/index.md Normal file
View File

@@ -0,0 +1,122 @@
higan, the multi-system emulator
================================
higan emulates a number of classic video-game consoles of the 1980s and 1990s,
allowing you to play classic games on a modern general-purpose computer.
To get started with higan right away,
see the [Quick Start](qs.md) section of the documentation.
About higan
-----------
As of v104,
higan has top-tier support for the following consoles:
- Nintendo Super Famicom/Super Nintendo Entertainment System,
including add-on hardware:
- Super Game Boy
- Sufami Turbo
- Nintendo Game Boy Advance
It also includes some level of support for these consoles:
- Satellaview add-on for the Super Famicom
- Nintendo Famicom/Nintendo Entertainment System
- Nintendo Game Boy
- Nintendo Game Boy Color
- Sega Master System
- Sega Game Gear
- Sega Mega Drive/Genesis
- NEC PC Engine/TurboGrafx 16 (but not the CD-ROM² System/TurboGrafx-CD)
- NEC SuperGrafx
- Bandai WonderSwan
- Bandai WonderSwan Color
**Note:** Some consoles were released under different names
in different geographic regions.
To avoid listing all possible names
every time such a console is mentioned,
higan uses the name from the console's region of origin.
In practice,
that means Japanese names:
"Famicom" and "Super Famicom" instead of "NES" and "SNES",
"Mega Drive" instead of "Genesis",
"PC Engine" instead of "TurboGrafx-16".
higan is actively supported on
FreeBSD 10 and above, and
Microsoft Windows 7 and above.
It also includes some level of support
for GNU/Linux and macOS.
higan is officially spelled with a lowercase "h", not a capital.
About this document
-------------------
This is the semi-official higan manual,
a community-maintained introduction and reference.
It may be out of date
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.
Official higan resources
------------------------
- [Official homepage](https://byuu.org/emulation/higan/)
- [Official forum](https://board.byuu.org/viewforum.php?f=4)
Unofficial higan resources
--------------------------
- [Source code repository](https://gitlab.com/higan/higan/)
archives official higan releases
and WIP snapshots
since approximately v067r21.
- [Quark shader repository](https://github.com/hizzlekizzle/quark-shaders)
collects shaders that higan can use
to add special effects like TV scanlines to its video output,
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
higan can use.
See [Importing MSU-1 games][msu1] for details.
[shaders]: guides/shaders.md
[msu1]: guides/import.md#msu-1-games
There are also other projects
based on current or older versions of higan,
in whole or in part,
that you might want to check out.
- [Mednafen](https://mednafen.github.io/)
is another multi-system emulator.
Its Super Famicom emulation is based on bsnes v059,
from the time before bsnes was renamed to higan.
- [BizHawk](http://tasvideos.org/BizHawk.html)
is another multi-system emulator,
specialising in the creation of
tool-assisted speed-runs.
Its Super Famicom emulation is based on bsnes v087.
- [bsnes-plus](https://github.com/devinacker/bsnes-plus)
is a fork of bsnes v073
that adds improved support for debugging Super Famicom software.
- [bsnes-mercury](https://github.com/libretro/bsnes-mercury/)
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)
is a fork of higan that greatly enhances
its NES emulation support,
and adds minor features to the other cores too.
It also restores the "balanced" Super Famicom emulation core
that was removed from higan in v099,
which is less CPU intensive
than the current accuracy-focused core.

53
docs/install/general.md Normal file
View File

@@ -0,0 +1,53 @@
Installing the GBA BIOS
-----------------------
For most of the systems higan emulates,
the console itself contains (almost) no actual software,
so emulating the system does not require
infringing the copyright of the hardware manufacturer.
However,
the Game Boy Advance is different:
every device contains a standard library of software routines
for [common functions games require][bios],
often called a "BIOS"
by analogy with the Basic Input/Output System
used in IBM PC compatibles.
Although the GBA BIOS is required
in order to emulate GBA games,
it cannot be distributed with higan
for the same legal reasons that commercial games
cannot be distributed with higan,
so you'll need to obtain a copy of the BIOS for yourself.
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,
and has the SHA256 hash
`fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570`.
Once you have the correct BIOS file:
1. rename it to `bios.rom`
- if you're using Windows,
turn off "hide extensions for known file types"
so you don't wind up with a file called
`bios.rom.dat`
or whatever the file's original extension was.
2. Copy the file into higan's `Game Boy Advance.sys` folder,
alongside the `manifest.bml` file that is already there.
- In Windows,
find `Game Boy Advance.sys` in the same folder
as `higan.exe`
- In Linux,
find `Game Boy Advance.sys` in
`~/.local/share/higan/`
**Note:**
If you upgrade this version of higan to a newer version,
make sure the `bios.rom` file
winds up in the `Game Boy Advance.sys` folder
of the new version.
[bios]: http://problemkaputt.de/gbatek.htm#biosfunctions

142
docs/install/linux.md Normal file
View File

@@ -0,0 +1,142 @@
Compiling from source on Linux
------------------------------
You will need a copy of the higan source-code.
If you download an official release from the higan homepage,
you will need [7-zip](http://www.7-zip.org)
or a compatible tool to extract it.
Alternatively,
you may obtain higan source code from
[the unofficial git repo](https://gitlab.com/higan/higan/)
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,
including the C and C++ compiler,
GNU Make,
and development files
(headers, etc.)
for the following libraries:
- GTK 2.x
- PulseAudio
- Mesa
- gtksourceview 2.x
- Cairo
- SDL 1.2
- libXv
- libAO
- OpenAL
- udev
On a Debian-derived Linux distribution
(including Ubuntu and Mint),
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 \
libxv-dev libao-dev libopenal-dev libudev-dev
Once you have all the dependencies installed,
you may build and install higan.
Note: Run these commands as yourself,
**do not run them as root**
(no `sudo`, no `su`, etc.),
because higan does not support
being installed system-wide.
1. Put the higan source code in some convenient location,
like `~/higan-src`
2. Open a terminal window
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
to build the main higan executable
5. Type `make -C icarus compiler=g++` and press Enter
to build the icarus import tool
Installing a compiled build on Linux
------------------------------------
Assuming you have successfully compiled higan
as described in the previous section:
1. Open a terminal window
2. Type `cd ~/higan-src`
(or wherever you put the higan source)
and press Enter
3. Type `make -C 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
5. If the higan source includes a `shaders` subdirectory,
type `make -C shaders install`
to install the example
[video shaders](../guides/shaders.md).
This installs higan and its associated data files
into the `~/.local` directory hierarchy.
To confirm higan is installed correctly,
type `higan` in a terminal and press Enter.
If the higan window appears,
everything is working.
On the other hand,
if you get an error message like "command not found",
you should double-check that the directory `~/.local/bin`
is included in your `$PATH` environment variable
by running the following command in a terminal:
echo "$PATH" | tr ':' '\n' | grep ~/.local/bin
If the above command prints the full path of `~/.local/bin`
(for example: `/home/yourname/.local/bin`)
then you should be good.
If it prints nothing,
you need to add the following line to `~/.profile`:
export PATH=~/.local/bin:$PATH
(this line must be in `~/.profile` because
most GUIs do not read any other files at login)
If you also have a `~/.bash_profile`,
make sure it reads the contents of `~/.profile`
with a line like this:
source ~/.profile
You will need to log out and log back in
for changes to `~/.profile` or `~/.bash_profile`
to take effect.
Uninstalling a compiled build on Linux
--------------------------------------
To uninstall higan,
as installed by the above instructions:
1. Open a terminal window
2. Type `cd ~/higan-src`
(or wherever you put the higan source)
and press Enter
3. Type `make -C higan uninstall` and press Enter
4. Type `make -C icarus uninstall` and press Enter
To remove higan's configuration,
you should also delete the following directories
if they exist:
- `~/.config/higan/`
- `~/.config/hiro/`
- `~/.local/share/higan/`
- `~/.local/share/hiro/`
To remove the games imported into higan's library
(including in-game saves and save-states),
delete the directory `~/Emulation`.
You may also wish to delete the higan source directory.

155
docs/install/windows.md Normal file
View File

@@ -0,0 +1,155 @@
Installing an official release on Windows
-----------------------------------------
Official higan releases are distributed in
[7-zip](http://www.7-zip.org/)
archives.
You will need to install 7-zip
(or another compatible archiving tool)
to install higan.
Once you have a suitable archiving tool,
extract the contents of the higan archive into a new folder.
When you're done,
the new folder should contain `higan.exe` and `icarus.exe`
along with other assorted files and folders
that describe the systems higan emulates.
You may put that new folder wherever you like.
To run higan, open the `higan.exe` file.
Uninstalling an official release on Windows
-------------------------------------------
Delete the folder containing `higan.exe`
and the other associated data from the original archive.
To remove higan's configuration:
1. Press Win+R to open the Run dialog
2. Type `%LOCALAPPDATA%` and press Enter
to open the folder where higan's configuration data lives
3. Delete the sub-folders named `icarus` and `higan`
if they exist.
You might also want to remove
[higan's game library](../concepts/game-library.md#where-is-the-game-library)
(including in-game saves and save-states).
Compiling from source on Windows
--------------------------------
You will need a copy of the higan source-code.
If you download an official release from the higan homepage,
you will need [7-zip](http://www.7-zip.org/)
or a compatible tool to extract it.
Alternatively,
you may obtain higan source code from
[the unofficial git repo](https://gitlab.com/higan/higan/)
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.
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).
Once you've installed the compiler,
open a command-prompt window,
type `g++ --version`
then press Enter
to check it's installed correctly.
You should see a message like
```text
g++ 1.2.3 20010101
Copyright (C) 2001 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.
```
...except it should mention the compiler version that you installed
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%`.
See the compiler's documentation for help with that.
Once mingw is installed and available from the command prompt:
1. Put the higan source code in some convenient location,
like `C:\higan-src`
2. Open 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
to build the icarus import tool
5. Type `mingw32-make -C higan compiler=g++` and press Enter
to build the main higan executable
[tdm]: http://tdm-gcc.tdragon.net/download
Installing a compiled build on Windows
--------------------------------------
1. In Windows Explorer,
create the folder where you want higan to live
2. Assuming you built higan in `C:\higan-src`,
copy `C:\higan-src\icarus\out\icarus.exe`
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`
into the new folder
5. Copy all the `*.sys` folders
in `C:\higan-src\higan\systems`
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.
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.
This is what you would get by downloading an official build,
as described under
[Installing an official release on Windows][instwin]
above.
[instwin]: #installing-an-official-release-on-windows
Uninstalling a compiled build on Windows
----------------------------------------
The process is the same as
[Uninstalling an official release on Windows][uninstwin]
above. You may also wish to delete the higan source folder.
[uninstwin]: #uninstalling-an-official-release-on-windows

61
docs/interface/common.md Normal file
View File

@@ -0,0 +1,61 @@
The Filesystem Browser
----------------------
Sometimes higan will need you
to choose a file or folder.
For this, it uses a special Filesystem Browser dialog.
Although many operating systems provide a native filesystem browser,
they do not all allow the same customisations.
Therefore,
higan provides its own filesystem browser
that works the same way on every platform.
The filesystem browser shows the contents of some particular folder,
and allows you to select one of those items.
Across the top of the window,
a text-box displays the path of the current folder.
If you want to browse a specific path,
you may edit the contents of this box
and press Enter to switch to the new location.
The ![two-blue-arrows](refresh.png) button is "Refresh".
Pressing this button will check
if anything has been added to or removed from the current folder
and update the list to match.
The ![house](home.png) button is "Home".
Pressing this button will switch to your home folder.
The ![up-arrow](up.png) button is "Parent".
Pressing this button will
switch to the parent of the current folder.
Most of the filesystem browser lists the contents
of the current folder.
Double-clicking a folder,
or selecting it and pressing Enter,
will switch to showing the contents of that folder.
If the list has keyboard focus,
typing any text will jump to the first item in the list
whose name begins with the text you typed.
If a drop-down list appears in the bottom-left,
it allows you to choose which files appear in the list,
based on file-extension.
If this filesystem browser is asking for a file,
you can choose one
by double-clicking it,
by selecting it and pressing Enter,
or by selecting it and clicking the "Select" button in the bottom-right.
If this filesystem browser is asking for a folder,
you can choose one
by selecting it and clicking the "Select" button in the bottom-right.
Double-clicking
or selecting and pressing Enter don't work,
they just switch to viewing the contents of that folder.
The "Cancel" button in the bottom-right
closes the filesystem browser without selecting anything.

View File

@@ -0,0 +1,95 @@
# Synopsis
> higan [*\-\-fullscreen*] [*GAME* [*SUBGAME1* [*SUBGAME2*]]]
# Description
When launched with `--fullscreen`,
higan will automatically enter fullscreen mode
when it starts,
otherwise it starts in windowed mode.
This is not much use unless you also specify `GAME`,
because you won't be able to load a game
until you exit fullscreen mode
by pressing the "Toggle Fullscreen"
[hotkey](higan-settings.md#hotkeys).
When `GAME` is not given,
higan starts with no game loaded.
When `GAME` is the path to an existing
[game folder](../concepts/game-folders.md)
for any supported console,
that game will automatically be loaded
after higan starts.
When `GAME` is the path to a ROM file
for any supported console,
it will be imported into a game folder in
[the Game Library](../concepts/game-library.md),
and loaded from there after higan starts.
When `GAME` is of the form `REGION|PATH`
(that is,
a region code,
a vertical bar (`|`),
and a path to a game folder or ROM file)
higan will load the game at `PATH`
into the `REGION` variant of the appropriate console,
rather than detecting which variant the game expects.
This is the command-line equivalent
of choosing a region from
the drop-down in the bottom-right of the filesystem browser window
when you [load a game from the game library](../guides/import.md#regular-games).
The list of region codes differs from console to console,
so load a game from the library
and check the list
before loading it from the command-line.
When `GAME` refers to a game with
a [Satellaview](../guides/import.md#satellaview-games) memory pak slot,
`SUBGAME1` should be
the path to a game folder or ROM file
representing the memory pak to insert into the slot.
If `SUBGAME1` is not supplied,
higan will prompt for one.
`SUBGAME2` is ignored.
When `GAME` refers to
the [Super Game Boy](../guides/import.md#super-game-boy-games),
`SUBGAME1` should be
the path to a game folder or ROM file
representing a Game Boy game to insert into the slot.
If `SUBGAME1` is not supplied,
higan will prompt for one.
`SUBGAME2` is ignored.
When `GAME` refers to
the [Sufami Turbo](../guides/import.md#sufami-turbo-games),
then `SUBGAME1` should be
the path to a game folder or ROM file
representing the mini-cartridge to be inserted into
slot A of the Sufami Turbo base cartridge,
and `SUBGAME2` is the same thing for slot B.
higan will prompt for
any mini-cartridges not supplied on the command-line.
When `GAME` refers to any other game,
both `SUBGAME1` and `SUBGAME2` are ignored.
# Examples
Play a previously-imported copy of Super Mario World
in fullscreen (assuming Linux defaults):
```sh
higan --fullscreen "$HOME/Emulation/Super Famicom/Super Mario World.sfc"
```
Play a previously-imported copy of Sonic the Hedgehog 3,
forcing higan to emulate a Japanese Mega Drive:
```sh
higan "NTSC-J|$HOME/Emulation/Mega Drive/Sonic the Hedgehog 3.md"
```

View File

@@ -0,0 +1,373 @@
The Settings window
appears when you choose
one of the items at the bottom of
[the Settings menu](higan.md#the-settings-menu),
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:
Video
=====
This tab contains options that affect
how higan displays
the emulated console's video output.
**Color Adjustment**
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,
100% is normal,
and 200% is garishly brightly coloured.
- **Gamma** adjusts how bright mid-range colours are
compared to the brightest colours,
where 100% is normal,
and 200% makes mid-range colours much darker.
This is in addition to
any adjustment applied by
the "Colors" option
in the "Video 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.
- **Horizontal**
removes pixels from the left and right of the video output.
- **Vertical**
removes pixels from the top and bottom of the video output.
The units are "pixels in the emulated console's standard video-mode".
For example, setting "Horizontal" to 8
will clip 8/256ths from the left and right sides
of the Super Famicom's video output,
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**
settings apply
when higan is running fullscreen,
because it was started with the `--fullscreen`
[command-line option](higan-cli.md)
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
when higan enters fullscreen mode.
This prevents other applications
or the operating system itself
from drawing anything,
and may also temporarily disable any kind of compositing,
reducing video latency.
As of v104,
only the Direct3D video driver is capable of exclusive mode;
with other drivers this option does nothing.
Audio
=====
This tab contains options that affect
how higan reproduces
the emulated console's audio output.
**Driver**
settings affect
the current [Audio driver](../guides/drivers.md).
- **Device** allows you to choose
which audio device higan sends
the emulated game's audio to,
if you have more than one.
- **Frequency** controls the sample-rate that higan will use
when generating audio.
If your PC's audio hardware has a "native" sample-rate
and you know what it is,
pick that.
Otherwise,
44.1kHz or 48kHz should be fine.
- **Latency** controls how much audio output higan calculates in advance.
Higher values reduce the chance of
"popping" or "glitching" noises,
but increase the delay between an action occurring on-screen
and the corresponding sound-effect being played.
- **Exclusive Mode** appears
if the current audio driver
allows higan to take exclusive control of your PC's audio output,
so no other applications can play sounds.
This can improve audio quality,
and lower the effective audio latency.
**Effects**
are applied to the emulated audio output
before it is sent to your computer's speakers.
- **Volume** controls the overall loudness of
the emulated console's audio,
where 100% is normal volume,
and 0% is complete silence.
- **Balance** controls the relative loudness of
the left and right 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.
The exact PC inputs that can be mapped
depend on [the input driver](../guides/drivers.md#input).
General input settings:
- **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!
Choosing which of the possible controllers to configure:
- 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:
- The mapping list includes
every button and axis on the selected controller,
and the PC inputs that will be mapped to it
when it is connected to the selected port of the selected console.
- **Erase** removes the mapping
for the selected button or axis.
- **Reset** removes all the mappings currently in the list.
To map
a keyboard or gamepad button on your PC to
a controller button,
double-click the controller button in the list,
or select it and press Enter.
The Settings window will grey out,
and a message will appear in the bottom left:
"Press a key or button to map [the button]".
Press the key or button you want to map,
and it should appear in the list
next to the controller button it is mapped to.
To map
a mouse button on your PC to
a controller button,
select the controller button in the list,
then click one of the "Mouse Left",
"Mouse Middle",
or "Mouse Right" buttons in the bottom-left of the Settings window.
To map
a joystick axis on your PC to
a controller axis,
double-click the axis in the list,
or select it and press Enter.
The Settings window will grey out,
and a message will appear in the bottom left:
"Press a key or button to map [the axis]".
Press the joystick in the direction you want to map,
and it should appear in the list
next to the controller axis it is mapped to.
To map
a mouse axis on your PC to
a controller axis,
select the axis in the list,
then click one of the
"Mouse X-axis",
or "Mouse Y-axis"
buttons in the bottom-left of the window.
If you start mapping a button or axis,
but decide you don't want to,
you can press Escape
to exit the "Press a key or button to map..." mode
without actually mapping anything.
**Note:**
Consoles in the Game Boy family include
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]
for details.
[gbcrumble]: ../notes.md#rumble-compatibility-for-game-boy-color
[gbarumble]: ../notes.md#rumble-compatibility-for-game-boy-advance
**Note:**
Once you've configured which computer inputs
higan should route to which controllers,
make sure the controller in question
is actually connected to the correct controller port
in [the console menu](higan.md#the-console-menu).
Hotkeys
=======
This tab is like "Inputs" above,
except it contains controls for higan itself
instead of 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.
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.
This is useful when the mouse is being used to emulate
a light-gun controller like the Super Scope.
- **Save Quick State** saves the current state of the emulated console
to the currently-selected [Quick State][qstates] slot.
- **Load Quick State** restores the emulated console
to the state saved in the currently-selected [Quick State][qstates] slot.
- **Decrement Quick State** selects the previous [Quick State][qstates] slot.
The status bar will briefly display the new current slot number.
- **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.
- **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.
- **Power Cycle** turns the emulated console off and back on
(a "hard reset"),
just like the "Power Cycle" menu item
in [the console menu](higan.md#the-console-menu).
- **Rotate Display** will rotate the display
of the Game Boy Advance
and WonderSwan (Color).
See [Game Boy Advance rotation](../notes.md#game-boy-advance-rotation)
and [WonderSwan rotation](../notes.md#wonderswan-rotation)
for details.
[qstates]: ../concepts/save-states.md#quick-states
Advanced
========
This tab contains all the settings
that didn't fit into one of the other categories.
**Driver Selection**
tells higan how to
accept input,
display video,
and play sound
on this computer.
- **Video** controls how higan will draw
the emulated console's video output
to the PC screen.
"None" means no video will be drawn.
- **Audio** controls how higan will present
the emulated console's audio output.
"None" means no audio will be played.
- **Input** controls how higan checks for input
from the PC's input devices.
"None" means the emulated console cannot be controlled.
See [Choosing drivers](../guides/drivers.md)
for help choosing which drivers you should use.
**Game Library**
configures how higan interacts
with the [Game Library](../concepts/game-library.md).
- **Location** selects where higan
looks 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
the [manifest](../concepts/manifests.md) file
in the loaded game's
[game folder](../concepts/game-folders.md)
in favour of asking icarus
to guess a manifest on the fly.
See [Ignoring manifests](../concepts/manifests.md#ignoring-manifests)
for details.

View File

@@ -0,0 +1,137 @@
The Tools window
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 Cheat Editor
----------------
For some consoles,
higan supports applying temporary changes to the code of a running game.
For example,
you could disable the code that detects when the player takes damage,
resulting in an "invulnerability" mode.
Currently,
higan supports cheats for the following consoles:
- Famicom
- Super Famicom
- Game Boy
- Master System
- PC Engine
- Wonder Swan
To add a new cheat,
select an unused row in the list,
then type the relevant codes in the "Code(s)" field at the bottom,
and a description in the "Description" field.
See [Cheat code formats](#cheat-code-formats)
for a description of the codes higan understands.
To enable or disable an existing cheat,
tick the checkbox in the first column of the list.
The change should take effect immediately.
To clear out an existing cheat,
select it from the list
and click the "Erase" button in the bottom right,
or just manually delete
the contents of the "Code(s)" and "Description" fields.
To clear out all existing cheats,
click the "Reset" button in the bottom right.
Changes made in the Cheat Editor are saved to disk
when the game is unloaded,
or when higan exits.
higan stores the known cheats for a particular game
in `higan/cheats.bml`
inside the game's [game folder](../concepts/game-folders.md).
If your copy of higan includes a cheat database
(a file named `cheats.bml`
in the same folder as `Super Famicom.sys`
and the other `*.sys` folders),
you can click the "Find Codes ..." button in the bottom left
to load all known cheats for the currently-running game.
Cheat code formats
------------------
A cheat code of the format `addr=data`
will cause the emulated console to obtain `data`
whenever it reads from memory address `addr`.
A cheat code of the format `addr=comp?data`
will cause reads from `addr` to obtain `data`,
but only if the true value at `addr` is `comp`.
In both formats,
`data` is a single byte expressed as two hexadecimal digits,
`comp` is also a single byte expressed as two hexadecimal digits,
and `addr` is a memory address in the emulated console,
expressed as however many hexadecimal digits are required
for the console in question
(typically 4 for 8-bit CPUs,
6 for 16-bit CPUs,
and 8 for 32-bit CPUs).
For compatibility with older versions of higan,
the older syntaxes of `addr/data` and `addr/comp/data`
are still supported.
For cheats that require more than a single-byte change,
higan allows multiple codes to be combined with `+`
so that all of them can have a single description
and be toggled with a single click.
For example,
in Super Mario World,
you can lock the time to 999 with these codes:
`7e0f31=09+7e0f32=09+7e0f33=09`.
The State Manager
-----------------
The State Manager allows you to create,
load,
and remove Manager states.
For more information on Manager states,
quick states,
saved games
and how they compare,
see [Save States](../concepts/save-states.md).
To save the current state of the loaded game
in a new slot,
or to replace the contents of an existing slot,
select the slot in the list
then click "Save" in the bottom-left corner.
You can then type a description in the "Description" field,
to help you find the state again later.
To rename a state,
select the slot in the list
and edit the "Description" field.
To load a state,
select the slot in the list
and click "Load" in the bottom-left corner,
or just double-click the slot.
To clear the state out of a slot,
select the slot in the list
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
-------------------
As described in
[Game Manifests](../concepts/manifests.md),
higan uses a "manifest" to
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.

288
docs/interface/higan.md Normal file
View File

@@ -0,0 +1,288 @@
When you launch higan
the main window appears,
with a menu-bar across the top,
a status-bar across the bottom,
and a large area in the middle that shows
the running game's video output.
The Library menu
----------------
Manufacturer sub-menus
allow you to play
games you've already imported
into higan's
[game library](../concepts/game-library.md).
See [Importing and playing games](../guides/import.md).
**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
---------------
**Note:**
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.
For example,
when playing a Game Boy game,
you will have a "Game Boy" menu.
The console menu contains commands relevant
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
to the first controller port,
if there is one.
See [higan's Input settings](higan-settings.md#input)
for information about configuring
which PC controller inputs are used
for the emulated controllers.
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 Port 2**
allows you
to connect different emulated controllers
to the second controller port,
if there is one.
See [higan's Input settings](higan-settings.md#input)
for information about configuring
which PC controller inputs are used
for the emulated controllers.
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.
**Expansion Port**
allows you
to connect different emulated devices
to the console's expansion port,
if there is one.
For the Super Famicom,
the [21fx][21fx] is a homebrew device
that allows a program running on a PC
to control a physical Super Famicom (or SNES).
This option allows the same program
to control the emulated SNES,
for development or testing.
**Power Cycle**
restarts the loaded game
as though the emulated console were switched off and on again.
**Unload**
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).
[21fx]: https://github.com/defparam/21FX
The Settings menu
-----------------
The Settings menu allows you to configure things
that aren't specific to any particular console.
**Video Scale** determines the size
of the emulated console's video output
when higan is running in windowed mode
(as opposed to fullscreen).
**Video Emulation** applies various effects
to the emulated console's video output
to reproduce some behaviours
that aren't technically part of the console itself:
- **Blurring**
simulates the limited horizontal resolution
of standard-definition TVs
by blurring together horizontally-adjacent pixels.
For hand-held consoles,
this simulates the slow response time
of the cheap LCD screens these consoles used
by blending each output frame with the previous one.
- Games like
*Jurassic Park* for the Super Famicom
or *Chikyuu Kaihou Gun ZAS* for the Game Boy
depend on this to emulate a transparency effect.
- **Colors**
simulates the way a console's display device
differs from modern computer monitor's colour reproduction.
In particular,
it simulates the slightly-different gamma correction
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
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)
describes all the options in this sub-menu.
**Synchronize Audio**
causes higan to wait for audio playback to complete
before resuming emulation.
This should reduce popping and glitching noises,
and slows the emulation down to approximately the correct speed.
If your PC cannot emulate at full-speed,
(60fps for most consoles, 75fps for WonderSwan)
this has no noticeable effect.
**Mute Audio**
causes higan to not output sound from the emulated console.
The sound hardware is still emulated.
**Show Status Bar**
causes higan to show or hide the status bar
at the bottom of the window.
This option has no effect in fullscreen mode.
See [The status bar](#the-status-bar) for more information.
**Video ...**
opens [higan's Video settings](higan-settings.md#video).
**Audio ...**
opens [higan's Audio settings](higan-settings.md#audio).
**Input ...**
opens [higan's Input settings](higan-settings.md#input).
**Hotkeys ...**
opens [higan's Hotkeys settings](higan-settings.md#hotkeys).
**Advanced ...**
opens [higan's Advanced settings](higan-settings.md#advanced).
[svsa]: #why-do-synchronize-video-and-synchronize-audio-conflict
The Tools menu
--------------
The Tools menu
contains features for manipulating the emulated console.
**Save Quick State**
stores the current state of the emulated console
into one of the quick state slots.
See [Save States](../concepts/save-states.md) for more information.
**Load Quick State**
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)
of the Tools window.
**State Manager**
opens [the State Manager tab](higan-tools.md#the-state-manager)
of the Tools window.
**Manifest Viewer**
opens [the Manifest Viewer tab](higan-tools.md#the-manifest-viewer)
of the Tools window.
The Help menu
-------------
The Help menu contains information about higan itself.
**Documentation**
loads the official higan documentation
in your web-browser.
**Credits**
loads a list of people who have contributed to higan
in your web-browser.
**About**
opens the About dialog,
which displays basic information about higan,
including the version number.
The status bar
--------------
The status bar appears
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".
When a game is loaded and running,
the status bar displays the current emulation speed
in frames-per-second.
For PAL-based consoles,
this should be around 50 FPS for "full speed" emulation,
for NTSC and most portable consoles the ideal speed is 60 FPS,
but the WonderSwan runs at 75 FPS.
If the number is too low,
you may need a faster computer,
or a faster [video driver](../guides/drivers.md#video).
If the number is too high,
you may need to [Synchronize Audio](#the-settings-menu),
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),
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,
and/or press the "pause" hotkey.
The status bar briefly displays "Selected quick state slot X"
(where X is one of the Quick State slot numbers)
when you press the "Increment Quick State"
or "Decrement Quick State"
hotkeys,
to show which Quick State slot will be used
the next time you press the "Save Quick State"
or "Load Quick State" hotkeys.
The status bar briefly displays "Slot X quick state does not exist"
(where X is one of the Quick State slot numbers)
when you choose a slot from the
[Tools](#the-tools-menu) → "Load Quick State"
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"
when you choose "Power Cycle" from [the console menu](#the-console-menu),
or press the "Power Cycle" hotkey.
The status bar briefly displays "Display rotation not supported"
when you press the "Rotate Display" hotkey
while the emulated console does not support display rotation.

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

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

69
docs/interface/icarus.md Normal file
View File

@@ -0,0 +1,69 @@
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.
Bulk importing ROM files
------------------------
icarus' main window
is [a filesystem browser](common.md#the-filesystem-browser),
with customisations:
- The filesystem browser only lists
files with extensions typically used for ROM dumps from
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
by pressing "Select All" in the bottom-left.
- You can un-tick all the check-boxes
by pressing "Unselect All" in the bottom-left.
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].
icarus displays a progress dialog during the import process,
and a result window if any errors occurred.
**Note:** Some games require extra steps to import correctly;
see [Importing and playing games](../guides/import.md) for details.
The icarus Settings dialog
--------------------------
Pressing "Settings ..."
in the bottom-right corner of the main icarus window
opens the settings dialog.
The icarus Settings dialog contains the following settings:
- **Library Location** determines
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.
See [Ignoring manifests](../concepts/manifests.md#ignoring-manifests)
for details.
- **Use Database** causes icarus to use manifest information
from its database of known-good manifests,
if it's importing a game it recognises.
For unrecognised games,
and for all games if this box is unticked,
icarus guesses the manifest data.
This option is still relevant when "Create Manifests" is unticked:
higan uses icarus to generate a manifest when a game is loaded,
not just at import-time.
[gamelib]: ../concepts/game-library.md
[movgamelib]: ../concepts/game-library.md#moving-the-game-library

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

@@ -0,0 +1 @@
../../hiro/resource/icon/action/refresh.png

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

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

219
docs/notes.md Normal file
View File

@@ -0,0 +1,219 @@
The consoles that higan emulates
are similar in many ways,
but some of them do have particular quirks of their own.
Music and Sound Effect Volume on the Mega Drive
-----------------------------------------------
The Mega Drive has two different audio-generating chips:
- the SN76489 or "PSG" chip
inherited from the Master System,
mostly used for sound-effects
- the YM2612 or "FM" chip,
mostly used for music
With two different sound sources,
it's important that they have similar volumes
or one kind of sound will drown out the other.
Sega did *not* do this,
and different hardware revisions
used different relative volumes.
higan currently
sets the PSG volume to [125% of the FM volume][vol],
based on [a Sega Genesis model 1 VA6][va6] that byuu owns.
If you feel sound-effects in higan's Mega Drive emulation
are too loud or too quiet compared to the music,
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
Playing Game Boy Color games in Game Boy mode
---------------------------------------------
Games for the original Game Boy
came in solid grey cartridges,
and only supported four-shade grey-scale graphics.
ROM files for these games
typically have filenames ending in `.gb`.
The Game Boy Color played all the original Game Boy games,
but extended the hardware to support colour graphics.
Games that required
the extra hardware in the Game Boy Color
came in transparent cartridges,
and had a slightly different shape
to prevent them from being used in original Game Boys.
ROM files for these games
typically have filenames ending in `.gbc`.
However,
there were also some games
that could use colour if it was available,
but would stick to grey-scale if it wasn't.
These games came in black cartridges.
ROM files for these games
typically have filenames ending in `.gbc`
(since they are genuinely designed for the Game Boy Color)
or `.gbb`.
Sometimes people ask
for higan to include these backwards-compatible Game Boy Color games
when asking for a Game Boy game to load.
However,
this would make higan much more complex
for not much benefit:
it's just the same game as in Color mode,
but with bits chopped off.
You might as well play backward-compatible games
in Game Boy Color mode
and get the full experience the developers intended.
If you really, really want to see
what a particular game's backward-compatible mode looked like,
change the filename to end with `.gb`
(instead of `.gbc` or `.gbb`)
before [importing it](guides/import.md).
If you want to experiment
with loading in-game saves from colour-mode in monochrome mode
or vice-versa,
you can import the game once with `.gb`
and once with `.gbc`,
then manually copy files between the
[game folders](concepts/game-folders.md)
in the "Game Boy" and "Game Boy Color" sub-folders
of the [Game Library](concepts/game-library.md) folder.
Do not expect save-states to be compatible between
Game Boy and Game Boy Color.
In-Game Saves and the Game Boy Advance
--------------------------------------
For most of the consoles that higan emulates,
in-game saves are simple:
the cartridge contains some battery-backed RAM
that the game accesses like any other memory,
and the game's internal header usually contains some hint
about where in memory the save data appears
and how large it is.
The Game Boy Advance is different.
By the time of the GBA,
many save-storage technologies were available,
most with a more complex interface than plain memory.
Frustratingly, the GBA's internal header
does *not* describe which storage variant the game expects.
Therefore,
when importing a GBA game,
higan must guess which storage type to use
and sometimes it guesses incorrectly.
If higan guesses incorrectly for a game you want to play,
you will need to override the automatically-generated manifest.
See [Ignoring manifests](concepts/manifests.md#ignoring-manifests)
for details.
For more discussion of the GBA save type mess,
see [What's the deal with... GBA save files?][gbasaves]
[gbasaves]: http://zork.net/~st/jottings/GBA_saves.html
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)
included a rumble motor within the cartridge itself.
Because higan does not currently support
game-specific controller features,
to experience the rumble effect in higan
you'll need to configure the console:
- 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 inputs,
double-click "Rumble"
or select it and press Enter
- Press any button on the gamepad that should shake
when the game turns on the rumble effect.
Rumble compatibility for Game Boy Advance
-----------------------------------------
The original Game Boy Advance
and the Game Boy Advance SP
did not support any kind of rumble or force-feedback system,
but the Game Boy Player add-on for the GameCube
allowed Game Boy Advance games
to use the rumble feature in GameCube controllers.
Because rumble is a feature of the Game Boy Player,
to experience the rumble effect in higan
you'll need to configure the console itself:
- Open
[higan's Input settings](interface/higan-settings.md#input)
- In the list of consoles,
select Game Boy Advance
- In the list of inputs,
double-click "Rumble"
or select it and press Enter
- Press any button on the gamepad that should shake
when the game turns on the rumble effect.
As well as the Game Boy Player rumble feature,
some Game Boy Advance cartridges
included a rumble motor within the cartridge itself.
higan does not support this rumble technology,
but that's not a big deal:
the only two such games are *Drill Dozer*,
which can use Game Boy Player rumble,
and *WarioWare: Twisted*,
which doesn't work anyway
because it requires gyroscope hardware
that higan does not yet emulate.
Game Boy Advance rotation
-------------------------
Some Game Boy Advance homebrew games,
as well as a bonus mode in *Dr Mario + Puzzle League*,
expect the player to physically rotate the device
so the screen is tall rather than wide.
higan supports this feature with
a Rotate [hotkey](interface/higan-settings.md#hotkeys).
When the user presses the Rotate hotkey,
the console's video output is rotated 90° anti-clockwise,
and the directional-pad controls are also rotated
so that (for example) pushing the button for "up"
sends the signal "right" to the emulated console,
so that the player character moves "up" on the rotated screen.
WonderSwan rotation
-------------------
The WonderSwan hardware
included multiple sets of buttons
so the player could hold the device
vertically or horizontally.
*Makaimura for WonderSwan* includes a level
that requires the player to repeatedly rotate
the device as they play.
higan supports this feature with
a Rotate [hotkey](interface/higan-settings.md#hotkeys).
When the user presses the Rotate hotkey,
the console's video output is rotated 90° anti-clockwise,
and the X and Y button cluster mappings
are adjusted to match.

102
docs/qs.md Normal file
View File

@@ -0,0 +1,102 @@
If this is your first time using higan,
welcome!
Here's a brief guide to getting started:
Install higan
-------------
On Windows,
this should be as easy as downloading
the latest release archive from
[the higan homepage](https://byuu.org/emulation/higan/)
and extracting it.
[More information...](install/windows.md)
On Linux,
if your distribution doesn't already include the latest version,
you'll need to compile it yourself — but don't worry,
it's not too complex.
[More information...](install/linux.md)
Configuring inputs
------------------
Once higan's installed,
start it up.
Open the "Settings" menu,
and choose "Input ..."
to open [the Input settings](interface/higan-settings.md#input).
higan supports a *lot* of different controllers
for a lot of different consoles,
so you don't need to configure everything immediately.
For most consoles,
it's enough to just configure
whatever shows up in the list
when you choose that console.
For example,
when you pick the Super Famicom,
you're shown the inputs
for a Gamepad
in Controller Port 1.
Once you've configured the controls
just the way you want them,
higan will remember them
— you don't need to re-configure them
every time you start higan,
or every time you load a game.
Load a game
-----------
From
[the Library menu](interface/higan.md#the-library-menu),
choose "Load ROM File ..."
to open [a filesystem browser](interface/common.md#the-filesystem-browser),
and choose the game you want to play.
It will be added to
[the Game Library](concepts/game-library.md),
and it will start running immediately.
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,
which will list all the games for that console
in the Game Library.
Connect a controller
--------------------
Once the game is running,
a new menu will appear
named after the appropriate console.
For example,
if you're playing Super Mario World,
that's a Super Famicom game
so there will be a Super Famicom menu.
Open [the console menu](interface/higan.md#the-console-menu),
and if there is a sub-menu for a controller port,
make sure the port is using
the controller you [set up previously](#configuring-inputs).
If you configured inputs for a Gamepad in Controller Port 1,
the Controller Port 1 sub-menu
should be set to Gamepad.
Like a real console,
higan's controller ports
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
like the Game Boy and WonderSwan,
since the "controller" is always connected.
This *does* apply to the Famicom,
even though the Famicom's controllers are hard-wired,
because higan uses the name "Famicom"
to refer to Nintendo's 8-bit home console
in all territories,
including the Nintendo Entertainment System.

10
docs/release.md Normal file
View File

@@ -0,0 +1,10 @@
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/

View File

@@ -1,59 +0,0 @@
#ifndef EMULATOR_HPP
#define EMULATOR_HPP
#include <nall/nall.hpp>
#include <nall/dsp.hpp>
using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "096";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";
#if defined(PROFILE_ACCURACY)
static const string Profile = "Accuracy";
#elif defined(PROFILE_BALANCED)
static const string Profile = "Balanced";
#elif defined(PROFILE_PERFORMANCE)
static const string Profile = "Performance";
#endif
}
#include "interface.hpp"
//debugging function hook:
//no overhead (and no debugger invocation) if not compiled with -DDEBUGGER
//wraps testing of function to allow invocation without a defined callback
template<typename T> struct hook;
template<typename R, typename... P> struct hook<auto (P...) -> R> {
function<auto (P...) -> R> callback;
auto operator()(P... p) const -> R {
#if defined(DEBUGGER)
if(callback) return callback(forward<P>(p)...);
#endif
return R();
}
hook() {}
hook(const hook& hook) { callback = hook.callback; }
hook(void* function) { callback = function; }
hook(auto (*function)(P...) -> R) { callback = function; }
template<typename C> hook(auto (C::*function)(P...) -> R, C* object) { callback = {function, object}; }
template<typename C> hook(auto (C::*function)(P...) const -> R, C* object) { callback = {function, object}; }
template<typename L> hook(const L& function) { callback = function; }
auto operator=(const hook& source) -> hook& { callback = source.callback; return *this; }
};
#if defined(DEBUGGER)
#define privileged public
#else
#define privileged private
#endif
using varuint = varuint_t<uint>;
#endif

View File

@@ -1,117 +0,0 @@
#ifndef EMULATOR_INTERFACE_HPP
#define EMULATOR_INTERFACE_HPP
namespace Emulator {
struct Interface {
struct Information {
string name;
uint width;
uint height;
bool overscan;
double aspectRatio;
bool resettable;
struct Capability {
bool states;
bool cheats;
} capability;
} information;
struct Media {
uint id;
string name;
string type;
bool bootable; //false for cartridge slots (eg Sufami Turbo cartridges)
};
vector<Media> media;
struct Device {
uint id;
uint portmask;
string name;
struct Input {
uint id;
uint type; //0 = digital, 1 = analog (relative), 2 = rumble
string name;
uintptr guid; //user data field
};
vector<Input> input;
vector<uint> order;
};
struct Port {
uint id;
string name;
vector<Device> device;
};
vector<Port> port;
struct Bind {
virtual auto loadRequest(uint, string, string, bool) -> void {}
virtual auto loadRequest(uint, string, bool) -> void {}
virtual auto saveRequest(uint, string) -> void {}
virtual auto videoColor(uint, uint16, uint16, uint16, uint16) -> uint32 { return 0u; }
virtual auto videoRefresh(const uint32*, const uint32*, uint, uint, uint) -> void {}
virtual auto audioSample(int16, int16) -> void {}
virtual auto inputPoll(uint, uint, uint) -> int16 { return 0; }
virtual auto inputRumble(uint, uint, uint, bool) -> void {}
virtual auto dipSettings(const Markup::Node&) -> uint { return 0; }
virtual auto path(uint) -> string { return ""; }
virtual auto notify(string text) -> void { print(text, "\n"); }
};
Bind* bind = nullptr;
//callback bindings (provided by user interface)
auto loadRequest(uint id, string name, string type, bool required) -> void { return bind->loadRequest(id, name, type, required); }
auto loadRequest(uint id, string path, bool required) -> void { return bind->loadRequest(id, path, required); }
auto saveRequest(uint id, string path) -> void { return bind->saveRequest(id, path); }
auto videoColor(uint source, uint16 alpha, uint16 red, uint16 green, uint16 blue) -> uint32 { return bind->videoColor(source, alpha, red, green, blue); }
auto videoRefresh(const uint32* palette, const uint32* data, uint pitch, uint width, uint height) -> void { return bind->videoRefresh(palette, data, pitch, width, height); }
auto audioSample(int16 lsample, int16 rsample) -> void { return bind->audioSample(lsample, rsample); }
auto inputPoll(uint port, uint device, uint input) -> int16 { return bind->inputPoll(port, device, input); }
auto inputRumble(uint port, uint device, uint input, bool enable) -> void { return bind->inputRumble(port, device, input, enable); }
auto dipSettings(const Markup::Node& node) -> uint { return bind->dipSettings(node); }
auto path(uint group) -> string { return bind->path(group); }
template<typename... P> auto notify(P&&... p) -> void { return bind->notify({forward<P>(p)...}); }
//information
virtual auto manifest() -> string = 0;
virtual auto title() -> string = 0;
virtual auto videoFrequency() -> double = 0;
virtual auto audioFrequency() -> double = 0;
//media interface
virtual auto loaded() -> bool { return false; }
virtual auto sha256() -> string { return ""; }
virtual auto group(uint id) -> uint = 0;
virtual auto load(uint id) -> void {}
virtual auto save() -> void {}
virtual auto load(uint id, const stream& memory) -> void {}
virtual auto save(uint id, const stream& memory) -> void {}
virtual auto unload() -> void {}
//system interface
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 rtcsync() -> void {}
//state functions
virtual auto serialize() -> serializer = 0;
virtual auto unserialize(serializer&) -> bool = 0;
//cheat functions
virtual auto cheatSet(const lstring& = lstring{}) -> void {}
//utility functions
enum class PaletteMode : uint { Literal, Channel, Standard, Emulation };
virtual auto paletteUpdate(PaletteMode mode) -> void {}
};
}
#endif

View File

@@ -1,16 +0,0 @@
fc_objects := fc-interface fc-system fc-scheduler fc-input
fc_objects += fc-memory fc-cartridge fc-cpu fc-apu fc-ppu
fc_objects += fc-cheat fc-video
objects += $(fc_objects)
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-scheduler.o: $(fc)/scheduler/scheduler.cpp $(call rwildcard,$(fc)/scheduler/)
obj/fc-input.o: $(fc)/input/input.cpp $(call rwildcard,$(fc)/input/)
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-cheat.o: $(fc)/cheat/cheat.cpp $(call rwildcard,$(fc)/cheat/)
obj/fc-video.o: $(fc)/video/video.cpp $(call rwildcard,$(fc)/video/)

View File

@@ -1,329 +0,0 @@
#include <fc/fc.hpp>
namespace Famicom {
#include "envelope.cpp"
#include "sweep.cpp"
#include "pulse.cpp"
#include "triangle.cpp"
#include "noise.cpp"
#include "dmc.cpp"
#include "serialization.cpp"
APU apu;
APU::APU() {
for(uint amp : range(32)) {
if(amp == 0) {
pulse_dac[amp] = 0;
} else {
pulse_dac[amp] = 16384.0 * 95.88 / (8128.0 / amp + 100.0);
}
}
for(uint dmc_amp : range(128)) {
for(uint triangle_amp : range(16)) {
for(uint noise_amp : range(16)) {
if(dmc_amp == 0 && triangle_amp == 0 && noise_amp == 0) {
dmc_triangle_noise_dac[dmc_amp][triangle_amp][noise_amp] = 0;
} else {
dmc_triangle_noise_dac[dmc_amp][triangle_amp][noise_amp]
= 16384.0 * 159.79 / (100.0 + 1.0 / (triangle_amp / 8227.0 + noise_amp / 12241.0 + dmc_amp / 22638.0));
}
}
}
}
}
auto APU::Main() -> void {
apu.main();
}
auto APU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
uint pulse_output, triangle_output, noise_output, dmc_output;
pulse_output = pulse[0].clock();
pulse_output += pulse[1].clock();
triangle_output = triangle.clock();
noise_output = noise.clock();
dmc_output = dmc.clock();
clock_frame_counter_divider();
int output = pulse_dac[pulse_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
output = filter.run_hipass_strong(output);
output += cartridge_sample;
output = filter.run_hipass_weak(output);
//output = filter.run_lopass(output);
output = sclamp<16>(output);
interface->audioSample(output, output);
tick();
}
}
auto APU::tick() -> void {
clock += 12;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
}
auto APU::set_irq_line() -> void {
cpu.set_irq_apu_line(frame.irq_pending || dmc.irq_pending);
}
auto APU::set_sample(int16 sample) -> void {
cartridge_sample = sample;
}
auto APU::power() -> void {
filter.hipass_strong = 0;
filter.hipass_weak = 0;
filter.lopass = 0;
pulse[0].power();
pulse[1].power();
triangle.power();
noise.power();
dmc.power();
}
auto APU::reset() -> void {
create(APU::Main, 21477272);
pulse[0].reset();
pulse[1].reset();
triangle.reset();
noise.reset();
dmc.reset();
frame.irq_pending = 0;
frame.mode = 0;
frame.counter = 0;
frame.divider = 1;
enabled_channels = 0;
cartridge_sample = 0;
set_irq_line();
}
auto APU::read(uint16 addr) -> uint8 {
if(addr == 0x4015) {
uint8 result = 0x00;
result |= pulse[0].length_counter ? 0x01 : 0;
result |= pulse[1].length_counter ? 0x02 : 0;
result |= triangle.length_counter ? 0x04 : 0;
result |= noise.length_counter ? 0x08 : 0;
result |= dmc.length_counter ? 0x10 : 0;
result |= frame.irq_pending ? 0x40 : 0;
result |= dmc.irq_pending ? 0x80 : 0;
frame.irq_pending = false;
set_irq_line();
return result;
}
return cpu.mdr();
}
auto APU::write(uint16 addr, uint8 data) -> void {
const uint n = (addr >> 2) & 1; //pulse#
switch(addr) {
case 0x4000: case 0x4004:
pulse[n].duty = data >> 6;
pulse[n].envelope.loop_mode = data & 0x20;
pulse[n].envelope.use_speed_as_volume = data & 0x10;
pulse[n].envelope.speed = data & 0x0f;
break;
case 0x4001: case 0x4005:
pulse[n].sweep.enable = data & 0x80;
pulse[n].sweep.period = (data & 0x70) >> 4;
pulse[n].sweep.decrement = data & 0x08;
pulse[n].sweep.shift = data & 0x07;
pulse[n].sweep.reload = true;
break;
case 0x4002: case 0x4006:
pulse[n].period = (pulse[n].period & 0x0700) | (data << 0);
pulse[n].sweep.pulse_period = (pulse[n].sweep.pulse_period & 0x0700) | (data << 0);
break;
case 0x4003: case 0x4007:
pulse[n].period = (pulse[n].period & 0x00ff) | (data << 8);
pulse[n].sweep.pulse_period = (pulse[n].sweep.pulse_period & 0x00ff) | (data << 8);
pulse[n].duty_counter = 7;
pulse[n].envelope.reload_decay = true;
if(enabled_channels & (1 << n)) {
pulse[n].length_counter = length_counter_table[(data >> 3) & 0x1f];
}
break;
case 0x4008:
triangle.halt_length_counter = data & 0x80;
triangle.linear_length = data & 0x7f;
break;
case 0x400a:
triangle.period = (triangle.period & 0x0700) | (data << 0);
break;
case 0x400b:
triangle.period = (triangle.period & 0x00ff) | (data << 8);
triangle.reload_linear = true;
if(enabled_channels & (1 << 2)) {
triangle.length_counter = length_counter_table[(data >> 3) & 0x1f];
}
break;
case 0x400c:
noise.envelope.loop_mode = data & 0x20;
noise.envelope.use_speed_as_volume = data & 0x10;
noise.envelope.speed = data & 0x0f;
break;
case 0x400e:
noise.short_mode = data & 0x80;
noise.period = data & 0x0f;
break;
case 0x400f:
noise.envelope.reload_decay = true;
if(enabled_channels & (1 << 3)) {
noise.length_counter = length_counter_table[(data >> 3) & 0x1f];
}
break;
case 0x4010:
dmc.irq_enable = data & 0x80;
dmc.loop_mode = data & 0x40;
dmc.period = data & 0x0f;
dmc.irq_pending = dmc.irq_pending && dmc.irq_enable && !dmc.loop_mode;
set_irq_line();
break;
case 0x4011:
dmc.dac_latch = data & 0x7f;
break;
case 0x4012:
dmc.addr_latch = data;
break;
case 0x4013:
dmc.length_latch = data;
break;
case 0x4015:
if((data & 0x01) == 0) pulse[0].length_counter = 0;
if((data & 0x02) == 0) pulse[1].length_counter = 0;
if((data & 0x04) == 0) triangle.length_counter = 0;
if((data & 0x08) == 0) noise.length_counter = 0;
(data & 0x10) ? dmc.start() : dmc.stop();
dmc.irq_pending = false;
set_irq_line();
enabled_channels = data & 0x1f;
break;
case 0x4017:
frame.mode = data >> 6;
frame.counter = 0;
if(frame.mode & 2) clock_frame_counter();
if(frame.mode & 1) {
frame.irq_pending = false;
set_irq_line();
}
frame.divider = FrameCounter::NtscPeriod;
break;
}
}
auto APU::Filter::run_hipass_strong(int sample) -> int {
hipass_strong += ((((int64)sample << 16) - (hipass_strong >> 16)) * HiPassStrong) >> 16;
return sample - (hipass_strong >> 32);
}
auto APU::Filter::run_hipass_weak(int sample) -> int {
hipass_weak += ((((int64)sample << 16) - (hipass_weak >> 16)) * HiPassWeak) >> 16;
return sample - (hipass_weak >> 32);
}
auto APU::Filter::run_lopass(int sample) -> int {
lopass += ((((int64)sample << 16) - (lopass >> 16)) * LoPass) >> 16;
return (lopass >> 32);
}
auto APU::clock_frame_counter() -> void {
frame.counter++;
if(frame.counter & 1) {
pulse[0].clock_length();
pulse[0].sweep.clock(0);
pulse[1].clock_length();
pulse[1].sweep.clock(1);
triangle.clock_length();
noise.clock_length();
}
pulse[0].envelope.clock();
pulse[1].envelope.clock();
triangle.clock_linear_length();
noise.envelope.clock();
if(frame.counter == 0) {
if(frame.mode & 2) frame.divider += FrameCounter::NtscPeriod;
if(frame.mode == 0) {
frame.irq_pending = true;
set_irq_line();
}
}
}
auto APU::clock_frame_counter_divider() -> void {
frame.divider -= 2;
if(frame.divider <= 0) {
clock_frame_counter();
frame.divider += FrameCounter::NtscPeriod;
}
}
const uint8 APU::length_counter_table[32] = {
0x0a, 0xfe, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0xa0, 0x08, 0x3c, 0x0a, 0x0e, 0x0c, 0x1a, 0x0e,
0x0c, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xc0, 0x18, 0x48, 0x1a, 0x10, 0x1c, 0x20, 0x1e,
};
const uint16 APU::ntsc_noise_period_table[16] = {
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068,
};
const uint16 APU::pal_noise_period_table[16] = {
4, 7, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778,
};
const uint16 APU::ntsc_dmc_period_table[16] = {
428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54,
};
const uint16 APU::pal_dmc_period_table[16] = {
398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50,
};
}

View File

@@ -1,70 +0,0 @@
struct APU : Thread {
APU();
static auto Main() -> void;
auto main() -> void;
auto tick() -> void;
auto set_irq_line() -> void;
auto set_sample(int16 sample) -> void;
auto power() -> void;
auto reset() -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto serialize(serializer&) -> void;
struct Filter {
auto run_hipass_strong(int sample) -> int;
auto run_hipass_weak(int sample) -> int;
auto run_lopass(int sample) -> int;
auto serialize(serializer&) -> void;
enum : int { HiPassStrong = 225574, HiPassWeak = 57593, LoPass = 86322413 };
int64 hipass_strong;
int64 hipass_weak;
int64 lopass;
};
#include "envelope.hpp"
#include "sweep.hpp"
#include "pulse.hpp"
#include "triangle.hpp"
#include "noise.hpp"
#include "dmc.hpp"
struct FrameCounter {
auto serialize(serializer&) -> void;
enum : uint { NtscPeriod = 14915 }; //~(21.477MHz / 6 / 240hz)
bool irq_pending;
uint2 mode;
uint2 counter;
int divider;
};
auto clock_frame_counter() -> void;
auto clock_frame_counter_divider() -> void;
Filter filter;
FrameCounter frame;
uint8 enabled_channels;
int16 cartridge_sample;
int16 pulse_dac[32];
int16 dmc_triangle_noise_dac[128][16][16];
static const uint8 length_counter_table[32];
static const uint16 ntsc_dmc_period_table[16];
static const uint16 pal_dmc_period_table[16];
static const uint16 ntsc_noise_period_table[16];
static const uint16 pal_noise_period_table[16];
};
extern APU apu;

View File

@@ -1,117 +0,0 @@
auto APU::DMC::start() -> void {
if(length_counter == 0) {
read_addr = 0x4000 + (addr_latch << 6);
length_counter = (length_latch << 4) + 1;
}
}
auto APU::DMC::stop() -> void {
length_counter = 0;
dma_delay_counter = 0;
cpu.set_rdy_line(1);
cpu.set_rdy_addr(false);
}
auto APU::DMC::clock() -> uint8 {
uint8 result = dac_latch;
if(dma_delay_counter > 0) {
dma_delay_counter--;
if(dma_delay_counter == 1) {
cpu.set_rdy_addr(true, 0x8000 | read_addr);
} else if(dma_delay_counter == 0) {
cpu.set_rdy_line(1);
cpu.set_rdy_addr(false);
dma_buffer = cpu.mdr();
have_dma_buffer = true;
length_counter--;
read_addr++;
if(length_counter == 0) {
if(loop_mode) {
start();
} else if(irq_enable) {
irq_pending = true;
apu.set_irq_line();
}
}
}
}
if(--period_counter == 0) {
if(have_sample) {
int delta = (((sample >> bit_counter) & 1) << 2) - 2;
uint data = dac_latch + delta;
if((data & 0x80) == 0) dac_latch = data;
}
if(++bit_counter == 0) {
if(have_dma_buffer) {
have_sample = true;
sample = dma_buffer;
have_dma_buffer = false;
} else {
have_sample = false;
}
}
period_counter = ntsc_dmc_period_table[period];
}
if(length_counter > 0 && have_dma_buffer == false && dma_delay_counter == 0) {
cpu.set_rdy_line(0);
dma_delay_counter = 4;
}
return result;
}
auto APU::DMC::power() -> void {
}
auto APU::DMC::reset() -> void {
length_counter = 0;
irq_pending = 0;
period = 0;
period_counter = ntsc_dmc_period_table[0];
irq_enable = 0;
loop_mode = 0;
dac_latch = 0;
addr_latch = 0;
length_latch = 0;
read_addr = 0;
dma_delay_counter = 0;
bit_counter = 0;
have_dma_buffer = 0;
dma_buffer = 0;
have_sample = 0;
sample = 0;
}
auto APU::DMC::serialize(serializer& s) -> void {
s.integer(length_counter);
s.integer(irq_pending);
s.integer(period);
s.integer(period_counter);
s.integer(irq_enable);
s.integer(loop_mode);
s.integer(dac_latch);
s.integer(addr_latch);
s.integer(length_latch);
s.integer(read_addr);
s.integer(dma_delay_counter);
s.integer(bit_counter);
s.integer(have_dma_buffer);
s.integer(dma_buffer);
s.integer(have_sample);
s.integer(sample);
}

View File

@@ -1,33 +0,0 @@
struct DMC {
auto start() -> void;
auto stop() -> void;
auto clock() -> uint8;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
uint length_counter;
bool irq_pending;
uint4 period;
uint period_counter;
bool irq_enable;
bool loop_mode;
uint8 dac_latch;
uint8 addr_latch;
uint8 length_latch;
uint15 read_addr;
uint dma_delay_counter;
uint3 bit_counter;
bool have_dma_buffer;
uint8 dma_buffer;
bool have_sample;
uint8 sample;
} dmc;

View File

@@ -1,39 +0,0 @@
auto APU::Envelope::volume() const -> uint {
return use_speed_as_volume ? speed : decay_volume;
}
auto APU::Envelope::clock() -> void {
if(reload_decay) {
reload_decay = false;
decay_volume = 0x0f;
decay_counter = speed + 1;
return;
}
if(--decay_counter == 0) {
decay_counter = speed + 1;
if(decay_volume || loop_mode) decay_volume--;
}
}
auto APU::Envelope::power() -> void {
}
auto APU::Envelope::reset() -> void {
speed = 0;
use_speed_as_volume = 0;
loop_mode = 0;
reload_decay = 0;
decay_counter = 0;
decay_volume = 0;
}
auto APU::Envelope::serialize(serializer& s) -> void {
s.integer(speed);
s.integer(use_speed_as_volume);
s.integer(loop_mode);
s.integer(reload_decay);
s.integer(decay_counter);
s.integer(decay_volume);
}

View File

@@ -1,17 +0,0 @@
struct Envelope {
auto volume() const -> uint;
auto clock() -> void;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
uint4 speed;
bool use_speed_as_volume;
bool loop_mode;
bool reload_decay;
uint8 decay_counter;
uint4 decay_volume;
};

View File

@@ -1,57 +0,0 @@
auto APU::Noise::clock_length() -> void {
if(envelope.loop_mode == 0) {
if(length_counter > 0) length_counter--;
}
}
auto APU::Noise::clock() -> uint8 {
if(length_counter == 0) return 0;
uint8 result = (lfsr & 1) ? envelope.volume() : 0;
if(--period_counter == 0) {
uint feedback;
if(short_mode) {
feedback = ((lfsr >> 0) & 1) ^ ((lfsr >> 6) & 1);
} else {
feedback = ((lfsr >> 0) & 1) ^ ((lfsr >> 1) & 1);
}
lfsr = (lfsr >> 1) | (feedback << 14);
period_counter = apu.ntsc_noise_period_table[period];
}
return result;
}
auto APU::Noise::power() -> void {
}
auto APU::Noise::reset() -> void {
length_counter = 0;
envelope.speed = 0;
envelope.use_speed_as_volume = 0;
envelope.loop_mode = 0;
envelope.reload_decay = 0;
envelope.decay_counter = 0;
envelope.decay_volume = 0;
period = 0;
period_counter = 1;
short_mode = 0;
lfsr = 1;
}
auto APU::Noise::serialize(serializer& s) -> void {
s.integer(length_counter);
envelope.serialize(s);
s.integer(period);
s.integer(period_counter);
s.integer(short_mode);
s.integer(lfsr);
}

View File

@@ -1,19 +0,0 @@
struct Noise {
auto clock_length() -> void;
auto clock() -> uint8;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
uint length_counter;
Envelope envelope;
uint4 period;
uint period_counter;
bool short_mode;
uint15 lfsr;
} noise;

View File

@@ -1,51 +0,0 @@
auto APU::Pulse::clock_length() -> void {
if(envelope.loop_mode == 0) {
if(length_counter) length_counter--;
}
}
auto APU::Pulse::clock() -> uint8 {
if(sweep.check_period() == false) return 0;
if(length_counter == 0) return 0;
static const uint duty_table[] = {1, 2, 4, 6};
uint8 result = (duty_counter < duty_table[duty]) ? envelope.volume() : 0;
if(sweep.pulse_period < 0x008) result = 0;
if(--period_counter == 0) {
period_counter = (sweep.pulse_period + 1) * 2;
duty_counter++;
}
return result;
}
auto APU::Pulse::power() -> void {
envelope.power();
sweep.power();
}
auto APU::Pulse::reset() -> void {
envelope.reset();
sweep.reset();
length_counter = 0;
duty = 0;
duty_counter = 0;
period = 0;
period_counter = 1;
}
auto APU::Pulse::serialize(serializer& s) -> void {
s.integer(length_counter);
envelope.serialize(s);
sweep.serialize(s);
s.integer(duty);
s.integer(duty_counter);
s.integer(period);
s.integer(period_counter);
}

View File

@@ -1,21 +0,0 @@
struct Pulse {
auto clock_length() -> void;
auto check_period() -> bool;
auto clock() -> uint8;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
uint length_counter;
Envelope envelope;
Sweep sweep;
uint2 duty;
uint3 duty_counter;
uint11 period;
uint period_counter;
} pulse[2];

View File

@@ -1,28 +0,0 @@
auto APU::serialize(serializer& s) -> void {
Thread::serialize(s);
filter.serialize(s);
pulse[0].serialize(s);
pulse[1].serialize(s);
triangle.serialize(s);
dmc.serialize(s);
frame.serialize(s);
s.integer(enabled_channels);
s.integer(cartridge_sample);
}
auto APU::Filter::serialize(serializer& s) -> void {
s.integer(hipass_strong);
s.integer(hipass_weak);
s.integer(lopass);
}
auto APU::FrameCounter::serialize(serializer& s) -> void {
s.integer(irq_pending);
s.integer(mode);
s.integer(counter);
s.integer(divider);
}

View File

@@ -1,53 +0,0 @@
auto APU::Sweep::check_period() -> bool {
if(pulse_period > 0x7ff) return false;
if(decrement == 0) {
if((pulse_period + (pulse_period >> shift)) & 0x800) return false;
}
return true;
}
auto APU::Sweep::clock(uint channel) -> void {
if(--counter == 0) {
counter = period + 1;
if(enable && shift && pulse_period > 8) {
int delta = pulse_period >> shift;
if(decrement) {
pulse_period -= delta;
if(channel == 0) pulse_period--;
} else if((pulse_period + delta) < 0x800) {
pulse_period += delta;
}
}
}
if(reload) {
reload = false;
counter = period + 1;
}
}
auto APU::Sweep::power() -> void {
shift = 0;
decrement = 0;
period = 0;
counter = 1;
enable = 0;
reload = 0;
pulse_period = 0;
}
auto APU::Sweep::reset() -> void {
}
auto APU::Sweep::serialize(serializer& s) -> void {
s.integer(shift);
s.integer(decrement);
s.integer(period);
s.integer(counter);
s.integer(enable);
s.integer(reload);
s.integer(pulse_period);
}

View File

@@ -1,17 +0,0 @@
struct Sweep {
auto check_period() -> bool;
auto clock(uint channel) -> void;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
uint8 shift;
bool decrement;
uint3 period;
uint8 counter;
bool enable;
bool reload;
uint11 pulse_period;
};

View File

@@ -1,58 +0,0 @@
auto APU::Triangle::clock_length() -> void {
if(halt_length_counter == 0) {
if(length_counter > 0) length_counter--;
}
}
auto APU::Triangle::clock_linear_length() -> void {
if(reload_linear) {
linear_length_counter = linear_length;
} else if(linear_length_counter) {
linear_length_counter--;
}
if(halt_length_counter == 0) reload_linear = false;
}
auto APU::Triangle::clock() -> uint8 {
uint8 result = step_counter & 0x0f;
if((step_counter & 0x10) == 0) result ^= 0x0f;
if(length_counter == 0 || linear_length_counter == 0) return result;
if(--period_counter == 0) {
step_counter++;
period_counter = period + 1;
}
return result;
}
auto APU::Triangle::power() -> void {
reset();
}
auto APU::Triangle::reset() -> void {
length_counter = 0;
linear_length = 0;
halt_length_counter = 0;
period = 0;
period_counter = 1;
step_counter = 0;
linear_length_counter = 0;
reload_linear = 0;
}
auto APU::Triangle::serialize(serializer& s) -> void {
s.integer(length_counter);
s.integer(linear_length);
s.integer(halt_length_counter);
s.integer(period);
s.integer(period_counter);
s.integer(step_counter);
s.integer(linear_length_counter);
s.integer(reload_linear);
}

View File

@@ -1,22 +0,0 @@
struct Triangle {
auto clock_length() -> void;
auto clock_linear_length() -> void;
auto clock() -> uint8;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
uint length_counter;
uint8 linear_length;
bool halt_length_counter;
uint11 period;
uint period_counter;
uint5 step_counter;
uint8 linear_length_counter;
bool reload_linear;
} triangle;

View File

@@ -1,115 +0,0 @@
//BANDAI-FCG
struct BandaiFCG : Board {
BandaiFCG(Markup::Node& document) : Board(document) {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_counter_enable) {
if(--irq_counter == 0xffff) {
cpu.set_irq_line(1);
irq_counter_enable = false;
}
}
tick();
}
}
auto ciram_addr(uint addr) const -> uint {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
case 2: return 0x0000 | (addr & 0x03ff);
case 3: return 0x0400 | (addr & 0x03ff);
}
}
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) {
bool region = addr & 0x4000;
uint bank = (region == 0 ? prg_bank : 0x0f);
return prgrom.read((bank << 14) | (addr & 0x3fff));
}
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr >= 0x6000) {
switch(addr & 15) {
case 0x00: case 0x01: case 0x02: case 0x03:
case 0x04: case 0x05: case 0x06: case 0x07:
chr_bank[addr & 7] = data;
break;
case 0x08:
prg_bank = data & 0x0f;
break;
case 0x09:
mirror = data & 0x03;
break;
case 0x0a:
cpu.set_irq_line(0);
irq_counter_enable = data & 0x01;
irq_counter = irq_latch;
break;
case 0x0b:
irq_latch = (irq_latch & 0xff00) | (data << 0);
break;
case 0x0c:
irq_latch = (irq_latch & 0x00ff) | (data << 8);
break;
case 0x0d:
//TODO: serial EEPROM support
break;
}
}
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
return Board::chr_read(addr);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
return Board::chr_write(addr, data);
}
auto power() -> void {
reset();
}
auto reset() -> void {
for(auto &n : chr_bank) n = 0;
prg_bank = 0;
mirror = 0;
irq_counter_enable = 0;
irq_counter = 0;
irq_latch = 0;
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.array(chr_bank);
s.integer(prg_bank);
s.integer(mirror);
s.integer(irq_counter_enable);
s.integer(irq_counter);
s.integer(irq_latch);
}
uint8 chr_bank[8];
uint8 prg_bank;
uint2 mirror;
bool irq_counter_enable;
uint16 irq_counter;
uint16 irq_latch;
};

View File

@@ -1,38 +0,0 @@
struct KonamiVRC1 : Board {
KonamiVRC1(Markup::Node& document) : Board(document), vrc1(*this) {
}
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) return prgrom.read(vrc1.prg_addr(addr));
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) return vrc1.reg_write(addr, data);
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc1.ciram_addr(addr));
return Board::chr_read(vrc1.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc1.ciram_addr(addr), data);
return Board::chr_write(vrc1.chr_addr(addr), data);
}
auto power() -> void {
vrc1.power();
}
auto reset() -> void {
vrc1.reset();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
vrc1.serialize(s);
}
VRC1 vrc1;
};

View File

@@ -1,55 +0,0 @@
struct KonamiVRC2 : Board {
KonamiVRC2(Markup::Node& document) : Board(document), vrc2(*this) {
settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].natural();
settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].natural();
}
auto prg_read(uint addr) -> uint8 {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return vrc2.ram_read(addr);
return prgrom.read(vrc2.prg_addr(addr));
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr < 0x6000) return;
if(addr < 0x8000) return vrc2.ram_write(addr, data);
bool a0 = (addr & settings.pinout.a0);
bool a1 = (addr & settings.pinout.a1);
addr &= 0xfff0;
addr |= (a0 << 0) | (a1 << 1);
return vrc2.reg_write(addr, data);
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc2.ciram_addr(addr));
return Board::chr_read(vrc2.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc2.ciram_addr(addr), data);
return Board::chr_write(vrc2.chr_addr(addr), data);
}
auto power() -> void {
vrc2.power();
}
auto reset() -> void {
vrc2.reset();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
vrc2.serialize(s);
}
struct Settings {
struct Pinout {
uint a0;
uint a1;
} pinout;
} settings;
VRC2 vrc2;
};

View File

@@ -1,40 +0,0 @@
struct KonamiVRC6 : Board {
KonamiVRC6(Markup::Node& document) : Board(document), vrc6(*this) {
}
auto prg_read(uint addr) -> uint8{
if((addr & 0xe000) == 0x6000) return vrc6.ram_read(addr);
if(addr & 0x8000) return prgrom.read(vrc6.prg_addr(addr));
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xe000) == 0x6000) return vrc6.ram_write(addr, data);
if(addr & 0x8000) {
addr = (addr & 0xf003);
if(prgram.size) addr = (addr & ~3) | ((addr & 2) >> 1) | ((addr & 1) << 1);
return vrc6.reg_write(addr, data);
}
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc6.ciram_addr(addr));
return Board::chr_read(vrc6.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc6.ciram_addr(addr), data);
return Board::chr_write(vrc6.chr_addr(addr), data);
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
vrc6.serialize(s);
}
auto main() -> void { vrc6.main(); }
auto power() -> void { vrc6.power(); }
auto reset() -> void { vrc6.reset(); }
VRC6 vrc6;
};

View File

@@ -1,45 +0,0 @@
struct KonamiVRC7 : Board {
KonamiVRC7(Markup::Node& document) : Board(document), vrc7(*this) {
}
auto main() -> void {
return vrc7.main();
}
auto prg_read(uint addr) -> uint8 {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return prgram.read(addr);
return prgrom.read(vrc7.prg_addr(addr));
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr < 0x6000) return;
if(addr < 0x8000) return prgram.write(addr, data);
return vrc7.reg_write(addr, data);
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc7.ciram_addr(addr));
return chrram.read(vrc7.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc7.ciram_addr(addr), data);
return chrram.write(vrc7.chr_addr(addr), data);
}
auto power() -> void {
vrc7.power();
}
auto reset() -> void {
vrc7.reset();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
vrc7.serialize(s);
}
VRC7 vrc7;
};

View File

@@ -1,49 +0,0 @@
//NES-AMROM
//NES-ANROM
//NES-AN1ROM
//NES-AOROM
struct NES_AxROM : Board {
NES_AxROM(Markup::Node& document) : Board(document) {
}
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) {
prg_bank = data & 0x0f;
mirror_select = data & 0x10;
}
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read((mirror_select << 10) | (addr & 0x03ff));
return Board::chr_read(addr);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write((mirror_select << 10) | (addr & 0x03ff), data);
return Board::chr_write(addr, data);
}
auto power() -> void {
}
auto reset() -> void {
prg_bank = 0x0f;
mirror_select = 0;
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.integer(prg_bank);
s.integer(mirror_select);
}
uint4 prg_bank;
bool mirror_select;
};

View File

@@ -1,50 +0,0 @@
//NES-BN-ROM-01
struct NES_BNROM : Board {
NES_BNROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) prg_bank = data & 0x03;
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr);
}
return Board::chr_read(addr);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr, data);
}
return Board::chr_write(addr, data);
}
auto power() -> void {
}
auto reset() -> void {
prg_bank = 0;
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.integer(prg_bank);
}
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint2 prg_bank;
};

View File

@@ -1,52 +0,0 @@
//NES-CNROM
struct NES_CNROM : Board {
NES_CNROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) return prgrom.read(addr & 0x7fff);
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) chr_bank = data & 0x03;
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
return Board::chr_read(addr);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
Board::chr_write(addr, data);
}
auto power() -> void {
}
auto reset() -> void {
chr_bank = 0;
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.integer(chr_bank);
}
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint2 chr_bank;
};

View File

@@ -1,89 +0,0 @@
//MMC4
struct NES_FxROM : Board {
NES_FxROM(Markup::Node& document) : Board(document) {
revision = Revision::FKROM;
}
auto prg_read(uint addr) -> uint8 {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return prgram.read(addr);
uint bank = addr < 0xc000 ? prg_bank : (uint4)0x0f;
return prgrom.read((bank * 0x4000) | (addr & 0x3fff));
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr < 0x6000) return;
if(addr < 0x8000) return prgram.write(addr, data);
switch(addr & 0xf000) {
case 0xa000: prg_bank = data & 0x0f; break;
case 0xb000: chr_bank[0][0] = data & 0x1f; break;
case 0xc000: chr_bank[0][1] = data & 0x1f; break;
case 0xd000: chr_bank[1][0] = data & 0x1f; break;
case 0xe000: chr_bank[1][1] = data & 0x1f; break;
case 0xf000: mirror = data & 0x01; break;
}
}
auto ciram_addr(uint addr) const -> uint {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
}
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
bool region = addr & 0x1000;
uint bank = chr_bank[region][latch[region]];
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
return Board::chr_read((bank * 0x1000) | (addr & 0x0fff));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
bool region = addr & 0x1000;
uint bank = chr_bank[region][latch[region]];
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
return Board::chr_write((bank * 0x1000) | (addr & 0x0fff), data);
}
auto power() -> void {
}
auto reset() -> void {
prg_bank = 0;
chr_bank[0][0] = 0;
chr_bank[0][1] = 0;
chr_bank[1][0] = 0;
chr_bank[1][1] = 0;
mirror = 0;
latch[0] = 0;
latch[1] = 0;
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.integer(prg_bank);
s.integer(chr_bank[0][0]);
s.integer(chr_bank[0][1]);
s.integer(chr_bank[1][0]);
s.integer(chr_bank[1][1]);
s.integer(mirror);
s.array(latch);
}
enum Revision : uint {
FJROM,
FKROM,
} revision;
uint4 prg_bank;
uint5 chr_bank[2][2];
bool mirror;
bool latch[2];
};

View File

@@ -1,59 +0,0 @@
//NES-GNROM
//NES-MHROM
struct NES_GxROM : Board {
NES_GxROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) {
prg_bank = (data & 0x30) >> 4;
chr_bank = (data & 0x03) >> 0;
}
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
return Board::chr_read(addr);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
Board::chr_write(addr, data);
}
auto power() -> void {
}
auto reset() -> void {
prg_bank = 0;
chr_bank = 0;
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.integer(prg_bank);
s.integer(chr_bank);
}
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint2 prg_bank;
uint2 chr_bank;
};

View File

@@ -1,46 +0,0 @@
struct NES_HKROM : Board {
NES_HKROM(Markup::Node& document) : Board(document), mmc6(*this) {
}
auto main() -> void {
mmc6.main();
}
auto prg_read(uint addr) -> uint8 {
if((addr & 0xf000) == 0x7000) return mmc6.ram_read(addr);
if(addr & 0x8000) return prgrom.read(mmc6.prg_addr(addr));
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xf000) == 0x7000) return mmc6.ram_write(addr, data);
if(addr & 0x8000) return mmc6.reg_write(addr, data);
}
auto chr_read(uint addr) -> uint8 {
mmc6.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_read(mmc6.ciram_addr(addr));
return Board::chr_read(mmc6.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
mmc6.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_write(mmc6.ciram_addr(addr), data);
return Board::chr_write(mmc6.chr_addr(addr), data);
}
auto power() -> void {
mmc6.power();
}
auto reset() -> void {
mmc6.reset();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
mmc6.serialize(s);
}
MMC6 mmc6;
};

View File

@@ -1,99 +0,0 @@
struct NES_SxROM : Board {
NES_SxROM(Markup::Node& document) : Board(document), mmc1(*this) {
revision = Revision::SXROM;
}
auto main() -> void {
return mmc1.main();
}
auto ram_addr(uint addr) -> uint {
uint bank = 0;
if(revision == Revision::SOROM) bank = (mmc1.chr_bank[0] & 0x08) >> 3;
if(revision == Revision::SUROM) bank = (mmc1.chr_bank[0] & 0x0c) >> 2;
if(revision == Revision::SXROM) bank = (mmc1.chr_bank[0] & 0x0c) >> 2;
return (bank << 13) | (addr & 0x1fff);
}
auto prg_read(uint addr) -> uint8 {
if((addr & 0xe000) == 0x6000) {
if(revision == Revision::SNROM) {
if(mmc1.chr_bank[0] & 0x10) return cpu.mdr();
}
if(mmc1.ram_disable) return 0x00;
return prgram.read(ram_addr(addr));
}
if(addr & 0x8000) {
addr = mmc1.prg_addr(addr);
if(revision == Revision::SXROM) {
addr |= ((mmc1.chr_bank[0] & 0x10) >> 4) << 18;
}
return prgrom.read(addr);
}
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xe000) == 0x6000) {
if(revision == Revision::SNROM) {
if(mmc1.chr_bank[0] & 0x10) return;
}
if(mmc1.ram_disable) return;
return prgram.write(ram_addr(addr), data);
}
if(addr & 0x8000) return mmc1.mmio_write(addr, data);
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(mmc1.ciram_addr(addr));
return Board::chr_read(mmc1.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(mmc1.ciram_addr(addr), data);
return Board::chr_write(mmc1.chr_addr(addr), data);
}
auto power() -> void {
mmc1.power();
}
auto reset() -> void {
mmc1.reset();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
mmc1.serialize(s);
}
enum class Revision : uint {
SAROM,
SBROM,
SCROM,
SC1ROM,
SEROM,
SFROM,
SGROM,
SHROM,
SH1ROM,
SIROM,
SJROM,
SKROM,
SLROM,
SL1ROM,
SL2ROM,
SL3ROM,
SLRROM,
SMROM,
SNROM,
SOROM,
SUROM,
SXROM,
} revision;
MMC1 mmc1;
};

View File

@@ -1,65 +0,0 @@
struct NES_TxROM : Board {
NES_TxROM(Markup::Node& document) : Board(document), mmc3(*this) {
revision = Revision::TLROM;
}
auto main() -> void {
mmc3.main();
}
auto prg_read(uint addr) -> uint8 {
if((addr & 0xe000) == 0x6000) return mmc3.ram_read(addr);
if(addr & 0x8000) return prgrom.read(mmc3.prg_addr(addr));
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xe000) == 0x6000) return mmc3.ram_write(addr, data);
if(addr & 0x8000) return mmc3.reg_write(addr, data);
}
auto chr_read(uint addr) -> uint8 {
mmc3.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_read(mmc3.ciram_addr(addr));
return Board::chr_read(mmc3.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
mmc3.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_write(mmc3.ciram_addr(addr), data);
return Board::chr_write(mmc3.chr_addr(addr), data);
}
auto power() -> void {
mmc3.power();
}
auto reset() -> void {
mmc3.reset();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
mmc3.serialize(s);
}
enum class Revision : uint {
TBROM,
TEROM,
TFROM,
TGROM,
TKROM,
TKSROM,
TLROM,
TL1ROM,
TL2ROM,
TLSROM,
TNROM,
TQROM,
TR1ROM,
TSROM,
TVROM,
} revision;
MMC3 mmc3;
};

View File

@@ -1,53 +0,0 @@
//NES-UNROM
//NES-UOROM
struct NES_UxROM : Board {
NES_UxROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
auto prg_read(uint addr) -> uint8 {
if((addr & 0xc000) == 0x8000) return prgrom.read((prg_bank << 14) | (addr & 0x3fff));
if((addr & 0xc000) == 0xc000) return prgrom.read(( 0x0f << 14) | (addr & 0x3fff));
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) prg_bank = data & 0x0f;
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr);
}
return Board::chr_read(addr);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr, data);
}
return Board::chr_write(addr, data);
}
auto power() -> void {
}
auto reset() -> void {
prg_bank = 0;
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.integer(prg_bank);
}
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint4 prg_bank;
};

View File

@@ -1,88 +0,0 @@
#include <fc/fc.hpp>
namespace Famicom {
#include "chip/chip.cpp"
#include "board/board.cpp"
Cartridge cartridge;
auto Cartridge::loaded() const -> bool {
return _loaded;
}
auto Cartridge::sha256() const -> string {
return _sha256;
}
auto Cartridge::manifest() const -> string {
return information.markup;
}
auto Cartridge::title() const -> string {
return information.title;
}
auto Cartridge::Main() -> void {
cartridge.main();
}
auto Cartridge::main() -> void {
board->main();
}
auto Cartridge::load() -> void {
interface->loadRequest(ID::Manifest, "manifest.bml", true);
Board::load(information.markup); //this call will set Cartridge::board if successful
if(!board) return;
Hash::SHA256 sha;
sha.data(board->prgrom.data, board->prgrom.size);
sha.data(board->chrrom.data, board->chrrom.size);
_sha256 = sha.digest();
system.load();
_loaded = true;
}
auto Cartridge::unload() -> void {
if(!loaded()) return;
_loaded = false;
memory.reset();
}
auto Cartridge::power() -> void {
board->power();
}
auto Cartridge::reset() -> void {
create(Cartridge::Main, 21477272);
board->reset();
}
auto Cartridge::prg_read(uint addr) -> uint8 {
return board->prg_read(addr);
}
auto Cartridge::prg_write(uint addr, uint8 data) -> void {
return board->prg_write(addr, data);
}
auto Cartridge::chr_read(uint addr) -> uint8 {
return board->chr_read(addr);
}
auto Cartridge::chr_write(uint addr, uint8 data) -> void {
return board->chr_write(addr, data);
}
auto Cartridge::scanline(uint y) -> void {
return board->scanline(y);
}
auto Cartridge::serialize(serializer& s) -> void {
Thread::serialize(s);
return board->serialize(s);
}
}

View File

@@ -1,48 +0,0 @@
#include "chip/chip.hpp"
#include "board/board.hpp"
struct Cartridge : Thread {
static auto Main() -> void;
auto main() -> void;
auto loaded() const -> bool;
auto sha256() const -> string;
auto manifest() const -> string;
auto title() const -> string;
auto load() -> void;
auto unload() -> void;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
struct Information {
string markup;
string title;
} information;
struct Memory {
unsigned id;
string name;
};
vector<Memory> memory;
//privileged:
Board* board = nullptr;
bool _loaded = false;
string _sha256;
auto prg_read(uint addr) -> uint8;
auto prg_write(uint addr, uint8 data) -> void;
auto chr_read(uint addr) -> uint8;
auto chr_write(uint addr, uint8 data) -> void;
//scanline() is for debugging purposes only:
//boards must detect scanline edges on their own
auto scanline(uint y) -> void;
};
extern Cartridge cartridge;

View File

@@ -1,187 +0,0 @@
struct MMC3 : Chip {
MMC3(Board& board) : Chip(board) {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
tick();
}
}
auto irq_test(uint addr) -> void {
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
if(irq_delay == 0) {
if(irq_counter == 0) {
irq_counter = irq_latch;
} else if(--irq_counter == 0) {
if(irq_enable) irq_line = 1;
}
}
irq_delay = 6;
}
chr_abus = addr;
}
auto prg_addr(uint addr) const -> uint {
switch((addr >> 13) & 3) {
case 0:
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 1:
return (prg_bank[1] << 13) | (addr & 0x1fff);
case 2:
if(prg_mode == 0) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 3:
return (0x3f << 13) | (addr & 0x1fff);
}
}
auto chr_addr(uint addr) const -> uint {
if(chr_mode == 0) {
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
} else {
if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
}
}
auto ciram_addr(uint addr) const -> uint {
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
}
auto ram_read(uint addr) -> uint8 {
if(ram_enable) return board.prgram.data[addr & 0x1fff];
return 0x00;
}
auto ram_write(uint addr, uint8 data) -> void {
if(ram_enable && !ram_write_protect) board.prgram.data[addr & 0x1fff] = data;
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr & 0xe001) {
case 0x8000:
chr_mode = data & 0x80;
prg_mode = data & 0x40;
bank_select = data & 0x07;
break;
case 0x8001:
switch(bank_select) {
case 0: chr_bank[0] = data & ~1; break;
case 1: chr_bank[1] = data & ~1; break;
case 2: chr_bank[2] = data; break;
case 3: chr_bank[3] = data; break;
case 4: chr_bank[4] = data; break;
case 5: chr_bank[5] = data; break;
case 6: prg_bank[0] = data & 0x3f; break;
case 7: prg_bank[1] = data & 0x3f; break;
}
break;
case 0xa000:
mirror = data & 0x01;
break;
case 0xa001:
ram_enable = data & 0x80;
ram_write_protect = data & 0x40;
break;
case 0xc000:
irq_latch = data;
break;
case 0xc001:
irq_counter = 0;
break;
case 0xe000:
irq_enable = false;
irq_line = 0;
break;
case 0xe001:
irq_enable = true;
break;
}
}
auto power() -> void {
}
auto reset() -> void {
chr_mode = 0;
prg_mode = 0;
bank_select = 0;
prg_bank[0] = 0;
prg_bank[1] = 0;
chr_bank[0] = 0;
chr_bank[1] = 0;
chr_bank[2] = 0;
chr_bank[3] = 0;
chr_bank[4] = 0;
chr_bank[5] = 0;
mirror = 0;
ram_enable = 1;
ram_write_protect = 0;
irq_latch = 0;
irq_counter = 0;
irq_enable = false;
irq_delay = 0;
irq_line = 0;
chr_abus = 0;
}
auto serialize(serializer& s) -> void {
s.integer(chr_mode);
s.integer(prg_mode);
s.integer(bank_select);
s.array(prg_bank);
s.array(chr_bank);
s.integer(mirror);
s.integer(ram_enable);
s.integer(ram_write_protect);
s.integer(irq_latch);
s.integer(irq_counter);
s.integer(irq_enable);
s.integer(irq_delay);
s.integer(irq_line);
s.integer(chr_abus);
}
bool chr_mode;
bool prg_mode;
uint3 bank_select;
uint8 prg_bank[2];
uint8 chr_bank[6];
bool mirror;
bool ram_enable;
bool ram_write_protect;
uint8 irq_latch;
uint8 irq_counter;
bool irq_enable;
uint irq_delay;
bool irq_line;
uint16 chr_abus;
};

View File

@@ -1,495 +0,0 @@
struct MMC5 : Chip {
MMC5(Board& board) : Chip(board) {
revision = Revision::MMC5;
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
//scanline() resets this; if no scanlines detected, enter video blanking period
if(++cpu_cycle_counter >= 200) blank(); //113-114 normal; ~2500 across Vblank period
cpu.set_irq_line(irq_enable && irq_pending);
tick();
}
}
auto scanline(uint y) -> void {
//used for testing only, to verify MMC5 scanline detection is accurate:
//if(y != vcounter && y <= 240) print(y, " vs ", vcounter, "\n");
}
auto prg_access(bool write, uint addr, uint8 data = 0x00) -> uint8 {
uint bank;
if((addr & 0xe000) == 0x6000) {
bank = (ram_select << 2) | ram_bank;
addr &= 0x1fff;
} else if(prg_mode == 0) {
bank = prg_bank[3] & ~3;
addr &= 0x7fff;
} else if(prg_mode == 1) {
if((addr & 0xc000) == 0x8000) bank = (prg_bank[1] & ~1);
if((addr & 0xe000) == 0xc000) bank = (prg_bank[3] & ~1);
addr &= 0x3fff;
} else if(prg_mode == 2) {
if((addr & 0xe000) == 0x8000) bank = (prg_bank[1] & ~1) | 0;
if((addr & 0xe000) == 0xa000) bank = (prg_bank[1] & ~1) | 1;
if((addr & 0xe000) == 0xc000) bank = (prg_bank[2]);
if((addr & 0xe000) == 0xe000) bank = (prg_bank[3]);
addr &= 0x1fff;
} else if(prg_mode == 3) {
if((addr & 0xe000) == 0x8000) bank = prg_bank[0];
if((addr & 0xe000) == 0xa000) bank = prg_bank[1];
if((addr & 0xe000) == 0xc000) bank = prg_bank[2];
if((addr & 0xe000) == 0xe000) bank = prg_bank[3];
addr &= 0x1fff;
}
bool rom = bank & 0x80;
bank &= 0x7f;
if(write == false) {
if(rom) {
return board.prgrom.read((bank << 13) | addr);
} else {
return board.prgram.read((bank << 13) | addr);
}
} else {
if(rom) {
board.prgrom.write((bank << 13) | addr, data);
} else {
if(prgram_write_protect[0] == 2 && prgram_write_protect[1] == 1) {
board.prgram.write((bank << 13) | addr, data);
}
}
return 0x00;
}
}
auto prg_read(uint addr) -> uint8 {
if((addr & 0xfc00) == 0x5c00) {
if(exram_mode >= 2) return exram[addr & 0x03ff];
return cpu.mdr();
}
if(addr >= 0x6000) {
return prg_access(0, addr);
}
switch(addr) {
case 0x5204: {
uint8 result = (irq_pending << 7) | (in_frame << 6);
irq_pending = false;
return result;
}
case 0x5205: return (multiplier * multiplicand) >> 0;
case 0x5206: return (multiplier * multiplicand) >> 8;
}
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xfc00) == 0x5c00) {
//writes 0x00 *during* Vblank (not during screen rendering ...)
if(exram_mode == 0 || exram_mode == 1) exram[addr & 0x03ff] = in_frame ? data : 0x00;
if(exram_mode == 2) exram[addr & 0x03ff] = data;
return;
}
if(addr >= 0x6000) {
prg_access(1, addr, data);
return;
}
switch(addr) {
case 0x2000:
sprite_8x16 = data & 0x20;
break;
case 0x2001:
//if BG+sprites are disabled; enter video blanking period
if((data & 0x18) == 0) blank();
break;
case 0x5100: prg_mode = data & 3; break;
case 0x5101: chr_mode = data & 3; break;
case 0x5102: prgram_write_protect[0] = data & 3; break;
case 0x5103: prgram_write_protect[1] = data & 3; break;
case 0x5104:
exram_mode = data & 3;
break;
case 0x5105:
nametable_mode[0] = (data & 0x03) >> 0;
nametable_mode[1] = (data & 0x0c) >> 2;
nametable_mode[2] = (data & 0x30) >> 4;
nametable_mode[3] = (data & 0xc0) >> 6;
break;
case 0x5106:
fillmode_tile = data;
break;
case 0x5107:
fillmode_color = data & 3;
fillmode_color |= fillmode_color << 2;
fillmode_color |= fillmode_color << 4;
break;
case 0x5113:
ram_select = data & 0x04;
ram_bank = data & 0x03;
break;
case 0x5114: prg_bank[0] = data; break;
case 0x5115: prg_bank[1] = data; break;
case 0x5116: prg_bank[2] = data; break;
case 0x5117: prg_bank[3] = data | 0x80; break;
case 0x5120: chr_sprite_bank[0] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5121: chr_sprite_bank[1] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5122: chr_sprite_bank[2] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5123: chr_sprite_bank[3] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5124: chr_sprite_bank[4] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5125: chr_sprite_bank[5] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5126: chr_sprite_bank[6] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5127: chr_sprite_bank[7] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5128: chr_bg_bank[0] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x5129: chr_bg_bank[1] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x512a: chr_bg_bank[2] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x512b: chr_bg_bank[3] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x5130:
chr_bank_hi = data & 3;
break;
case 0x5200:
vs_enable = data & 0x80;
vs_side = data & 0x40;
vs_tile = data & 0x1f;
break;
case 0x5201:
vs_scroll = data;
break;
case 0x5202:
vs_bank = data;
break;
case 0x5203:
irq_line = data;
break;
case 0x5204:
irq_enable = data & 0x80;
break;
case 0x5205:
multiplicand = data;
break;
case 0x5206:
multiplier = data;
break;
}
}
auto chr_sprite_addr(uint addr) -> uint {
if(chr_mode == 0) {
auto bank = chr_sprite_bank[7];
return (bank * 0x2000) + (addr & 0x1fff);
}
if(chr_mode == 1) {
auto bank = chr_sprite_bank[(addr / 0x1000) * 4 + 3];
return (bank * 0x1000) + (addr & 0x0fff);
}
if(chr_mode == 2) {
auto bank = chr_sprite_bank[(addr / 0x0800) * 2 + 1];
return (bank * 0x0800) + (addr & 0x07ff);
}
if(chr_mode == 3) {
auto bank = chr_sprite_bank[(addr / 0x0400)];
return (bank * 0x0400) + (addr & 0x03ff);
}
}
auto chr_bg_addr(uint addr) -> uint {
addr &= 0x0fff;
if(chr_mode == 0) {
auto bank = chr_bg_bank[3];
return (bank * 0x2000) + (addr & 0x0fff);
}
if(chr_mode == 1) {
auto bank = chr_bg_bank[3];
return (bank * 0x1000) + (addr & 0x0fff);
}
if(chr_mode == 2) {
auto bank = chr_bg_bank[(addr / 0x0800) * 2 + 1];
return (bank * 0x0800) + (addr & 0x07ff);
}
if(chr_mode == 3) {
auto bank = chr_bg_bank[(addr / 0x0400)];
return (bank * 0x0400) + (addr & 0x03ff);
}
}
auto chr_vs_addr(uint addr) -> uint {
return (vs_bank * 0x1000) + (addr & 0x0ff8) + (vs_vpos & 7);
}
auto blank() -> void {
in_frame = false;
}
auto scanline() -> void {
hcounter = 0;
if(in_frame == false) {
in_frame = true;
irq_pending = false;
vcounter = 0;
} else {
if(vcounter == irq_line) irq_pending = true;
vcounter++;
}
cpu_cycle_counter = 0;
}
auto ciram_read(uint addr) -> uint8 {
if(vs_fetch && (hcounter & 2) == 0) return exram[vs_vpos / 8 * 32 + vs_hpos / 8];
if(vs_fetch && (hcounter & 2) != 0) return exram[vs_vpos / 32 * 8 + vs_hpos / 32 + 0x03c0];
switch(nametable_mode[(addr >> 10) & 3]) {
case 0: return ppu.ciram_read(0x0000 | (addr & 0x03ff));
case 1: return ppu.ciram_read(0x0400 | (addr & 0x03ff));
case 2: return exram_mode < 2 ? exram[addr & 0x03ff] : 0x00;
case 3: return (hcounter & 2) == 0 ? fillmode_tile : fillmode_color;
}
}
auto chr_read(uint addr) -> uint8 {
chr_access[0] = chr_access[1];
chr_access[1] = chr_access[2];
chr_access[2] = chr_access[3];
chr_access[3] = addr;
//detect two unused nametable fetches at end of each scanline
if((chr_access[0] & 0x2000) == 0
&& (chr_access[1] & 0x2000)
&& (chr_access[2] & 0x2000)
&& (chr_access[3] & 0x2000)) scanline();
if(in_frame == false) {
vs_fetch = false;
if(addr & 0x2000) return ciram_read(addr);
return board.chrrom.read(chr_active ? chr_bg_addr(addr) : chr_sprite_addr(addr));
}
bool bg_fetch = (hcounter < 256 || hcounter >= 320);
uint8 result = 0x00;
if((hcounter & 7) == 0) {
vs_hpos = hcounter >= 320 ? hcounter - 320 : hcounter + 16;
vs_vpos = vcounter + vs_scroll;
vs_fetch = vs_enable && bg_fetch && exram_mode < 2
&& (vs_side ? vs_hpos / 8 >= vs_tile : vs_hpos / 8 < vs_tile);
if(vs_vpos >= 240) vs_vpos -= 240;
result = ciram_read(addr);
exbank = (chr_bank_hi << 6) | (exram[addr & 0x03ff] & 0x3f);
exattr = exram[addr & 0x03ff] >> 6;
exattr |= exattr << 2;
exattr |= exattr << 4;
} else if((hcounter & 7) == 2) {
result = ciram_read(addr);
if(bg_fetch && exram_mode == 1) result = exattr;
} else {
if(vs_fetch) result = board.chrrom.read(chr_vs_addr(addr));
else if(sprite_8x16 ? bg_fetch : chr_active) result = board.chrrom.read(chr_bg_addr(addr));
else result = board.chrrom.read(chr_sprite_addr(addr));
if(bg_fetch && exram_mode == 1) result = board.chrrom.read(exbank * 0x1000 + (addr & 0x0fff));
}
hcounter += 2;
return result;
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
switch(nametable_mode[(addr >> 10) & 3]) {
case 0: return ppu.ciram_write(0x0000 | (addr & 0x03ff), data);
case 1: return ppu.ciram_write(0x0400 | (addr & 0x03ff), data);
case 2: exram[addr & 0x03ff] = data; break;
}
}
}
auto power() -> void {
}
auto reset() -> void {
for(auto& n : exram) n = 0xff;
prg_mode = 3;
chr_mode = 0;
for(auto& n : prgram_write_protect) n = 0;
exram_mode = 0;
for(auto& n : nametable_mode) n = 0;
fillmode_tile = 0;
fillmode_color = 0;
ram_select = 0;
ram_bank = 0;
prg_bank[0] = 0x00;
prg_bank[1] = 0x00;
prg_bank[2] = 0x00;
prg_bank[3] = 0xff;
for(auto& n : chr_sprite_bank) n = 0;
for(auto& n : chr_bg_bank) n = 0;
chr_bank_hi = 0;
vs_enable = 0;
vs_side = 0;
vs_tile = 0;
vs_scroll = 0;
vs_bank = 0;
irq_line = 0;
irq_enable = 0;
multiplicand = 0;
multiplier = 0;
cpu_cycle_counter = 0;
irq_counter = 0;
irq_pending = 0;
in_frame = 0;
vcounter = 0;
hcounter = 0;
for(auto& n : chr_access) n = 0;
chr_active = 0;
sprite_8x16 = 0;
exbank = 0;
exattr = 0;
vs_fetch = 0;
vs_vpos = 0;
vs_hpos = 0;
}
auto serialize(serializer& s) -> void {
s.array(exram);
s.integer(prg_mode);
s.integer(chr_mode);
for(auto& n : prgram_write_protect) s.integer(n);
s.integer(exram_mode);
for(auto& n : nametable_mode) s.integer(n);
s.integer(fillmode_tile);
s.integer(fillmode_color);
s.integer(ram_select);
s.integer(ram_bank);
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_sprite_bank) s.integer(n);
for(auto& n : chr_bg_bank) s.integer(n);
s.integer(chr_bank_hi);
s.integer(vs_enable);
s.integer(vs_side);
s.integer(vs_tile);
s.integer(vs_scroll);
s.integer(vs_bank);
s.integer(irq_line);
s.integer(irq_enable);
s.integer(multiplicand);
s.integer(multiplier);
s.integer(cpu_cycle_counter);
s.integer(irq_counter);
s.integer(irq_pending);
s.integer(in_frame);
s.integer(vcounter);
s.integer(hcounter);
for(auto& n : chr_access) s.integer(n);
s.integer(chr_active);
s.integer(sprite_8x16);
s.integer(exbank);
s.integer(exattr);
s.integer(vs_fetch);
s.integer(vs_vpos);
s.integer(vs_hpos);
}
enum class Revision : uint {
MMC5,
MMC5B,
} revision;
uint8 exram[1024];
//programmable registers
uint2 prg_mode; //$5100
uint2 chr_mode; //$5101
uint2 prgram_write_protect[2]; //$5102,$5103
uint2 exram_mode; //$5104
uint2 nametable_mode[4]; //$5105
uint8 fillmode_tile; //$5106
uint8 fillmode_color; //$5107
bool ram_select; //$5113
uint2 ram_bank; //$5113
uint8 prg_bank[4]; //$5114-5117
uint10 chr_sprite_bank[8]; //$5120-5127
uint10 chr_bg_bank[4]; //$5128-512b
uint2 chr_bank_hi; //$5130
bool vs_enable; //$5200
bool vs_side; //$5200
uint5 vs_tile; //$5200
uint8 vs_scroll; //$5201
uint8 vs_bank; //$5202
uint8 irq_line; //$5203
bool irq_enable; //$5204
uint8 multiplicand; //$5205
uint8 multiplier; //$5206
//status registers
uint cpu_cycle_counter;
uint irq_counter;
bool irq_pending;
bool in_frame;
uint vcounter;
uint hcounter;
uint16 chr_access[4];
bool chr_active;
bool sprite_8x16;
uint8 exbank;
uint8 exattr;
bool vs_fetch;
uint8 vs_vpos;
uint8 vs_hpos;
};

View File

@@ -1,198 +0,0 @@
struct MMC6 : Chip {
MMC6(Board& board) : Chip(board) {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
tick();
}
}
auto irq_test(uint addr) -> void {
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
if(irq_delay == 0) {
if(irq_counter == 0) {
irq_counter = irq_latch;
} else if(--irq_counter == 0) {
if(irq_enable) irq_line = 1;
}
}
irq_delay = 6;
}
chr_abus = addr;
}
auto prg_addr(uint addr) const -> uint {
switch((addr >> 13) & 3) {
case 0:
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 1:
return (prg_bank[1] << 13) | (addr & 0x1fff);
case 2:
if(prg_mode == 0) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 3:
return (0x3f << 13) | (addr & 0x1fff);
}
}
auto chr_addr(uint addr) const -> uint {
if(chr_mode == 0) {
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
} else {
if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
}
}
auto ciram_addr(uint addr) const -> uint {
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
}
auto ram_read(uint addr) -> uint8 {
if(ram_enable == false) return cpu.mdr();
if(ram_readable[0] == false && ram_readable[1] == false) return cpu.mdr();
bool region = addr & 0x0200;
if(ram_readable[region] == false) return 0x00;
return board.prgram.read((region * 0x0200) + (addr & 0x01ff));
}
auto ram_write(uint addr, uint8 data) -> void {
if(ram_enable == false) return;
bool region = addr & 0x0200;
if(ram_writable[region] == false) return;
return board.prgram.write((region * 0x0200) + (addr & 0x01ff), data);
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr & 0xe001) {
case 0x8000:
chr_mode = data & 0x80;
prg_mode = data & 0x40;
ram_enable = data & 0x20;
bank_select = data & 0x07;
if(ram_enable == false) {
for(auto &n : ram_readable) n = false;
for(auto &n : ram_writable) n = false;
}
break;
case 0x8001:
switch(bank_select) {
case 0: chr_bank[0] = data & ~1; break;
case 1: chr_bank[1] = data & ~1; break;
case 2: chr_bank[2] = data; break;
case 3: chr_bank[3] = data; break;
case 4: chr_bank[4] = data; break;
case 5: chr_bank[5] = data; break;
case 6: prg_bank[0] = data & 0x3f; break;
case 7: prg_bank[1] = data & 0x3f; break;
}
break;
case 0xa000:
mirror = data & 0x01;
break;
case 0xa001:
if(ram_enable == false) break;
ram_readable[1] = data & 0x80;
ram_writable[1] = data & 0x40;
ram_readable[0] = data & 0x20;
ram_writable[0] = data & 0x10;
break;
case 0xc000:
irq_latch = data;
break;
case 0xc001:
irq_counter = 0;
break;
case 0xe000:
irq_enable = false;
irq_line = 0;
break;
case 0xe001:
irq_enable = true;
break;
}
}
auto power() -> void {
}
auto reset() -> void {
chr_mode = 0;
prg_mode = 0;
ram_enable = 0;
bank_select = 0;
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_bank) n = 0;
mirror = 0;
for(auto& n : ram_readable) n = 0;
for(auto& n : ram_writable) n = 0;
irq_latch = 0;
irq_counter = 0;
irq_enable = 0;
irq_delay = 0;
irq_line = 0;
chr_abus = 0;
}
auto serialize(serializer& s) -> void {
s.integer(chr_mode);
s.integer(prg_mode);
s.integer(ram_enable);
s.integer(bank_select);
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_bank) s.integer(n);
s.integer(mirror);
for(auto& n : ram_readable) s.integer(n);
for(auto& n : ram_writable) s.integer(n);
s.integer(irq_latch);
s.integer(irq_counter);
s.integer(irq_enable);
s.integer(irq_delay);
s.integer(irq_line);
s.integer(chr_abus);
}
bool chr_mode;
bool prg_mode;
bool ram_enable;
uint3 bank_select;
uint8 prg_bank[2];
uint8 chr_bank[6];
bool mirror;
bool ram_readable[2];
bool ram_writable[2];
uint8 irq_latch;
uint8 irq_counter;
bool irq_enable;
uint irq_delay;
bool irq_line;
uint16 chr_abus;
};

View File

@@ -1,78 +0,0 @@
struct VRC1 : Chip {
VRC1(Board& board) : Chip(board) {
}
auto prg_addr(uint addr) const -> uint {
uint bank = 0x0f;
if((addr & 0xe000) == 0x8000) bank = prg_bank[0];
if((addr & 0xe000) == 0xa000) bank = prg_bank[1];
if((addr & 0xe000) == 0xc000) bank = prg_bank[2];
return (bank * 0x2000) + (addr & 0x1fff);
}
auto chr_addr(uint addr) const -> uint {
uint bank = chr_banklo[(bool)(addr & 0x1000)];
bank |= chr_bankhi[(bool)(addr & 0x1000)] << 4;
return (bank * 0x1000) + (addr & 0x0fff);
}
auto ciram_addr(uint addr) const -> uint {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
}
throw;
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr & 0xf000) {
case 0x8000:
prg_bank[0] = data & 0x0f;
break;
case 0x9000:
chr_bankhi[1] = data & 0x04;
chr_bankhi[0] = data & 0x02;
mirror = data & 0x01;
break;
case 0xa000:
prg_bank[1] = data & 0x0f;
break;
case 0xc000:
prg_bank[2] = data & 0x0f;
break;
case 0xe000:
chr_banklo[0] = data & 0x0f;
break;
case 0xf000:
chr_banklo[1] = data & 0x0f;
break;
}
}
auto power() -> void {
}
auto reset() -> void {
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_banklo) n = 0;
for(auto& n : chr_bankhi) n = 0;
mirror = 0;
}
auto serialize(serializer& s) -> void {
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_banklo) s.integer(n);
for(auto& n : chr_bankhi) s.integer(n);
s.integer(mirror);
}
uint4 prg_bank[3];
uint4 chr_banklo[2];
bool chr_bankhi[2];
bool mirror;
};

View File

@@ -1,108 +0,0 @@
struct VRC2 : Chip {
VRC2(Board& board) : Chip(board) {
}
auto prg_addr(uint addr) const -> uint {
uint bank;
switch(addr & 0xe000) {
case 0x8000: bank = prg_bank[0]; break;
case 0xa000: bank = prg_bank[1]; break;
case 0xc000: bank = 0x1e; break;
case 0xe000: bank = 0x1f; break;
}
return (bank * 0x2000) + (addr & 0x1fff);
}
auto chr_addr(uint addr) const -> uint {
uint bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
auto ciram_addr(uint addr) const -> uint {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
}
throw;
}
auto ram_read(uint addr) -> uint8 {
if(board.prgram.size == 0) {
if((addr & 0xf000) == 0x6000) return cpu.mdr() | latch;
return cpu.mdr();
}
return board.prgram.read(addr & 0x1fff);
}
auto ram_write(uint addr, uint8 data) -> void {
if(board.prgram.size == 0) {
if((addr & 0xf000) == 0x6000) latch = data & 0x01;
return;
}
return board.prgram.write(addr & 0x1fff, data);
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr) {
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
prg_bank[0] = data & 0x1f;
break;
case 0x9000: case 0x9001: case 0x9002: case 0x9003:
mirror = data & 0x03;
break;
case 0xa000: case 0xa001: case 0xa002: case 0xa003:
prg_bank[1] = data & 0x1f;
break;
case 0xb000: chr_bank[0] = (chr_bank[0] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xb001: chr_bank[0] = (chr_bank[0] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xb002: chr_bank[1] = (chr_bank[1] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xb003: chr_bank[1] = (chr_bank[1] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xc000: chr_bank[2] = (chr_bank[2] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xc001: chr_bank[2] = (chr_bank[2] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xc002: chr_bank[3] = (chr_bank[3] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xc003: chr_bank[3] = (chr_bank[3] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xd000: chr_bank[4] = (chr_bank[4] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xd001: chr_bank[4] = (chr_bank[4] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xd002: chr_bank[5] = (chr_bank[5] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xd003: chr_bank[5] = (chr_bank[5] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xe000: chr_bank[6] = (chr_bank[6] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xe001: chr_bank[6] = (chr_bank[6] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xe002: chr_bank[7] = (chr_bank[7] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xe003: chr_bank[7] = (chr_bank[7] & 0x0f) | ((data & 0x0f) << 4); break;
}
}
auto power() -> void {
}
auto reset() -> void {
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_bank) n = 0;
mirror = 0;
latch = 0;
}
auto serialize(serializer& s) -> void {
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_bank) s.integer(n);
s.integer(mirror);
s.integer(latch);
}
uint5 prg_bank[2];
uint8 chr_bank[8];
uint2 mirror;
bool latch;
};

View File

@@ -1,98 +0,0 @@
struct VRC3 : Chip {
VRC3(Board& board) : Chip(board) {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_enable) {
if(irq_mode == 0) { //16-bit
if(++irq_counter.w == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.w = irq_latch;
}
}
if(irq_mode == 1) { //8-bit
if(++irq_counter.l == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.l = irq_latch;
}
}
}
cpu.set_irq_line(irq_line);
tick();
}
}
auto prg_addr(uint addr) const -> uint {
uint bank = (addr < 0xc000 ? (uint)prg_bank : 0x0f);
return (bank * 0x4000) + (addr & 0x3fff);
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr & 0xf000) {
case 0x8000: irq_latch = (irq_latch & 0xfff0) | ((data & 0x0f) << 0); break;
case 0x9000: irq_latch = (irq_latch & 0xff0f) | ((data & 0x0f) << 4); break;
case 0xa000: irq_latch = (irq_latch & 0xf0ff) | ((data & 0x0f) << 8); break;
case 0xb000: irq_latch = (irq_latch & 0x0fff) | ((data & 0x0f) << 12); break;
case 0xc000:
irq_mode = data & 0x04;
irq_enable = data & 0x02;
irq_acknowledge = data & 0x01;
if(irq_enable) irq_counter.w = irq_latch;
break;
case 0xd000:
irq_line = 0;
irq_enable = irq_acknowledge;
break;
case 0xf000:
prg_bank = data & 0x0f;
break;
}
}
auto power() -> void {
}
auto reset() -> void {
prg_bank = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
irq_latch = 0;
irq_counter.w = 0;
irq_line = 0;
}
auto serialize(serializer& s) -> void {
s.integer(prg_bank);
s.integer(irq_mode);
s.integer(irq_enable);
s.integer(irq_acknowledge);
s.integer(irq_latch);
s.integer(irq_counter.w);
s.integer(irq_line);
}
uint4 prg_bank;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
uint16 irq_latch;
struct {
union {
uint16 w;
struct { uint8 order_lsb2(l, h); };
};
} irq_counter;
bool irq_line;
};

View File

@@ -1,182 +0,0 @@
struct VRC4 : Chip {
VRC4(Board& board) : Chip(board) {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_enable) {
if(irq_mode == 0) {
irq_scalar -= 3;
if(irq_scalar <= 0) {
irq_scalar += 341;
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
if(irq_mode == 1) {
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
cpu.set_irq_line(irq_line);
tick();
}
}
auto prg_addr(uint addr) const -> uint {
uint bank = 0, banks = board.prgrom.size / 0x2000;
switch(addr & 0xe000) {
case 0x8000: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break;
case 0xa000: bank = prg_bank[1]; break;
case 0xc000: bank = prg_mode == 0 ? banks - 2 : (unsigned)prg_bank[0]; break;
case 0xe000: bank = banks - 1; break;
}
return (bank * 0x2000) + (addr & 0x1fff);
}
auto chr_addr(uint addr) const -> uint {
uint bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
auto ciram_addr(uint addr) const -> uint {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
}
throw;
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr) {
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
prg_bank[0] = data & 0x1f;
break;
case 0x9000: case 0x9001:
mirror = data & 0x03;
break;
case 0x9002: case 0x9003:
prg_mode = data & 0x02;
break;
case 0xa000: case 0xa001: case 0xa002: case 0xa003:
prg_bank[1] = data & 0x1f;
break;
case 0xb000: chr_bank[0] = (chr_bank[0] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xb001: chr_bank[0] = (chr_bank[0] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xb002: chr_bank[1] = (chr_bank[1] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xb003: chr_bank[1] = (chr_bank[1] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xc000: chr_bank[2] = (chr_bank[2] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xc001: chr_bank[2] = (chr_bank[2] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xc002: chr_bank[3] = (chr_bank[3] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xc003: chr_bank[3] = (chr_bank[3] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xd000: chr_bank[4] = (chr_bank[4] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xd001: chr_bank[4] = (chr_bank[4] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xd002: chr_bank[5] = (chr_bank[5] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xd003: chr_bank[5] = (chr_bank[5] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xe000: chr_bank[6] = (chr_bank[6] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xe001: chr_bank[6] = (chr_bank[6] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xe002: chr_bank[7] = (chr_bank[7] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xe003: chr_bank[7] = (chr_bank[7] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xf000:
irq_latch = (irq_latch & 0xf0) | ((data & 0x0f) << 0);
break;
case 0xf001:
irq_latch = (irq_latch & 0x0f) | ((data & 0x0f) << 4);
break;
case 0xf002:
irq_mode = data & 0x04;
irq_enable = data & 0x02;
irq_acknowledge = data & 0x01;
if(irq_enable) {
irq_counter = irq_latch;
irq_scalar = 341;
}
irq_line = 0;
break;
case 0xf003:
irq_enable = irq_acknowledge;
irq_line = 0;
break;
}
}
auto power() -> void {
}
auto reset() -> void {
prg_mode = 0;
for(auto& n : prg_bank) n = 0;
mirror = 0;
for(auto& n : chr_bank) n = 0;
irq_latch = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
irq_counter = 0;
irq_scalar = 0;
irq_line = 0;
}
auto serialize(serializer& s) -> void {
s.integer(prg_mode);
for(auto& n : prg_bank) s.integer(n);
s.integer(mirror);
for(auto& n : chr_bank) s.integer(n);
s.integer(irq_latch);
s.integer(irq_mode);
s.integer(irq_enable);
s.integer(irq_acknowledge);
s.integer(irq_counter);
s.integer(irq_scalar);
s.integer(irq_line);
}
bool prg_mode;
uint5 prg_bank[2];
uint2 mirror;
uint8 chr_bank[8];
uint8 irq_latch;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
uint8 irq_counter;
int irq_scalar;
bool irq_line;
};

View File

@@ -1,152 +0,0 @@
//Konami VRC7
//Yamaha YM2413 OPLL audio - not emulated
struct VRC7 : Chip {
VRC7(Board& board) : Chip(board) {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_enable) {
if(irq_mode == 0) {
irq_scalar -= 3;
if(irq_scalar <= 0) {
irq_scalar += 341;
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
if(irq_mode == 1) {
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
cpu.set_irq_line(irq_line);
tick();
}
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr) {
case 0x8000: prg_bank[0] = data; break;
case 0x8010: prg_bank[1] = data; break;
case 0x9000: prg_bank[2] = data; break;
case 0x9010: break; //APU addr port
case 0x9030: break; //APU data port
case 0xa000: chr_bank[0] = data; break;
case 0xa010: chr_bank[1] = data; break;
case 0xb000: chr_bank[2] = data; break;
case 0xb010: chr_bank[3] = data; break;
case 0xc000: chr_bank[4] = data; break;
case 0xc010: chr_bank[5] = data; break;
case 0xd000: chr_bank[6] = data; break;
case 0xd010: chr_bank[7] = data; break;
case 0xe000: mirror = data & 0x03; break;
case 0xe010:
irq_latch = data;
break;
case 0xf000:
irq_mode = data & 0x04;
irq_enable = data & 0x02;
irq_acknowledge = data & 0x01;
if(irq_enable) {
irq_counter = irq_latch;
irq_scalar = 341;
}
irq_line = 0;
break;
case 0xf010:
irq_enable = irq_acknowledge;
irq_line = 0;
break;
}
}
auto prg_addr(uint addr) const -> uint {
uint bank = 0;
switch(addr & 0xe000) {
case 0x8000: bank = prg_bank[0]; break;
case 0xa000: bank = prg_bank[1]; break;
case 0xc000: bank = prg_bank[2]; break;
case 0xe000: bank = 0xff; break;
}
return (bank * 0x2000) + (addr & 0x1fff);
}
auto chr_addr(uint addr) const -> uint {
uint bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
auto ciram_addr(uint addr) const -> uint {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
}
}
auto power() -> void {
}
auto reset() -> void {
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_bank) n = 0;
mirror = 0;
irq_latch = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
irq_counter = 0;
irq_scalar = 0;
irq_line = 0;
}
auto serialize(serializer& s) -> void {
s.array(prg_bank);
s.array(chr_bank);
s.integer(mirror);
s.integer(irq_latch);
s.integer(irq_mode);
s.integer(irq_enable);
s.integer(irq_acknowledge);
s.integer(irq_counter);
s.integer(irq_scalar);
s.integer(irq_line);
}
uint8 prg_bank[3];
uint8 chr_bank[8];
uint2 mirror;
uint8 irq_latch;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
uint8 irq_counter;
int irq_scalar;
bool irq_line;
};

View File

@@ -1,28 +0,0 @@
#include <fc/fc.hpp>
namespace Famicom {
Cheat cheat;
auto Cheat::reset() -> void {
codes.reset();
}
auto Cheat::append(uint addr, uint data) -> void {
codes.append({addr, Unused, data});
}
auto Cheat::append(uint addr, uint comp, uint data) -> void {
codes.append({addr, comp, data});
}
auto Cheat::find(uint addr, uint comp) -> maybe<uint> {
for(auto& code : codes) {
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
return code.data;
}
}
return nothing;
}
}

View File

@@ -1,17 +0,0 @@
struct Cheat {
struct Code {
uint addr;
uint comp;
uint data;
};
vector<Code> codes;
enum : uint { Unused = ~0u };
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
auto reset() -> void;
auto append(uint addr, uint data) -> void;
auto append(uint addr, uint comp, uint data) -> void;
auto find(uint addr, uint comp) -> maybe<uint>;
};
extern Cheat cheat;

View File

@@ -1,111 +0,0 @@
#include <fc/fc.hpp>
namespace Famicom {
#include "timing.cpp"
#include "serialization.cpp"
CPU cpu;
auto CPU::Enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
cpu.main();
}
}
auto CPU::main() -> void {
if(status.interrupt_pending) {
interrupt();
return;
}
exec();
}
auto CPU::add_clocks(uint clocks) -> void {
apu.clock -= clocks;
if(apu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(apu.thread);
ppu.clock -= clocks;
if(ppu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(ppu.thread);
cartridge.clock -= clocks;
if(cartridge.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cartridge.thread);
}
auto CPU::power() -> void {
R6502::power();
for(auto addr : range(0x0800)) ram[addr] = 0xff;
ram[0x0008] = 0xf7;
ram[0x0009] = 0xef;
ram[0x000a] = 0xdf;
ram[0x000f] = 0xbf;
}
auto CPU::reset() -> void {
R6502::reset();
create(CPU::Enter, 21477272);
regs.pc = bus.read(0xfffc) << 0;
regs.pc |= bus.read(0xfffd) << 8;
status.interrupt_pending = false;
status.nmi_pending = false;
status.nmi_line = 0;
status.irq_line = 0;
status.irq_apu_line = 0;
status.rdy_line = 1;
status.rdy_addr_valid = false;
status.rdy_addr_value = 0x0000;
status.oam_dma_pending = false;
status.oam_dma_page = 0x00;
status.controller_latch = false;
status.controller_port0 = 0;
status.controller_port1 = 0;
}
auto CPU::debugger_read(uint16 addr) -> uint8 {
return bus.read(addr);
}
auto CPU::ram_read(uint16 addr) -> uint8 {
return ram[addr & 0x07ff];
}
auto CPU::ram_write(uint16 addr, uint8 data) -> void {
ram[addr & 0x07ff] = data;
}
auto CPU::read(uint16 addr) -> uint8 {
if(addr == 0x4016) {
return (mdr() & 0xc0) | input.data(0);
}
if(addr == 0x4017) {
return (mdr() & 0xc0) | input.data(1);
}
return apu.read(addr);
}
auto CPU::write(uint16 addr, uint8 data) -> void {
if(addr == 0x4014) {
status.oam_dma_page = data;
status.oam_dma_pending = true;
}
if(addr == 0x4016) {
input.latch(data & 0x01);
}
return apu.write(addr, data);
}
}

View File

@@ -1,57 +0,0 @@
struct CPU : Processor::R6502, Thread {
static auto Enter() -> void;
auto main() -> void;
auto add_clocks(uint clocks) -> void;
auto power() -> void;
auto reset() -> void;
auto debugger_read(uint16 addr) -> uint8;
auto ram_read(uint16 addr) -> uint8;
auto ram_write(uint16 addr, uint8 data) -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto serialize(serializer&) -> void;
//timing.cpp
auto op_read(uint16 addr) -> uint8;
auto op_write(uint16 addr, uint8 data) -> void;
auto last_cycle() -> void;
auto nmi(uint16& vector) -> void;
auto oam_dma() -> void;
auto set_nmi_line(bool) -> void;
auto set_irq_line(bool) -> void;
auto set_irq_apu_line(bool) -> void;
auto set_rdy_line(bool) -> void;
auto set_rdy_addr(bool valid, uint16 value = 0) -> void;
//protected:
uint8 ram[0x0800];
struct Status {
bool interrupt_pending;
bool nmi_pending;
bool nmi_line;
bool irq_line;
bool irq_apu_line;
bool rdy_line;
bool rdy_addr_valid;
uint16 rdy_addr_value;
bool oam_dma_pending;
uint8 oam_dma_page;
bool controller_latch;
uint controller_port0;
uint controller_port1;
} status;
};
extern CPU cpu;

View File

@@ -1,23 +0,0 @@
auto CPU::serialize(serializer& s) -> void {
R6502::serialize(s);
Thread::serialize(s);
s.array(ram);
s.integer(status.interrupt_pending);
s.integer(status.nmi_pending);
s.integer(status.nmi_line);
s.integer(status.irq_line);
s.integer(status.irq_apu_line);
s.integer(status.rdy_line);
s.integer(status.rdy_addr_valid);
s.integer(status.rdy_addr_value);
s.integer(status.oam_dma_pending);
s.integer(status.oam_dma_page);
s.integer(status.controller_latch);
s.integer(status.controller_port0);
s.integer(status.controller_port1);
}

View File

@@ -1,64 +0,0 @@
auto CPU::op_read(uint16 addr) -> uint8 {
if(status.oam_dma_pending) {
status.oam_dma_pending = false;
op_read(addr);
oam_dma();
}
while(status.rdy_line == 0) {
regs.mdr = bus.read(status.rdy_addr_valid ? status.rdy_addr_value : addr);
add_clocks(12);
}
regs.mdr = bus.read(addr);
add_clocks(12);
return regs.mdr;
}
auto CPU::op_write(uint16 addr, uint8 data) -> void {
bus.write(addr, regs.mdr = data);
add_clocks(12);
}
auto CPU::last_cycle() -> void {
status.interrupt_pending = ((status.irq_line | status.irq_apu_line) & ~regs.p.i) | status.nmi_pending;
}
auto CPU::nmi(uint16& vector) -> void {
if(status.nmi_pending) {
status.nmi_pending = false;
vector = 0xfffa;
}
}
auto CPU::oam_dma() -> void {
for(uint n : range(256)) {
uint8 data = op_read((status.oam_dma_page << 8) + n);
op_write(0x2004, data);
}
}
auto CPU::set_nmi_line(bool line) -> void {
//edge-sensitive (0->1)
if(!status.nmi_line && line) status.nmi_pending = true;
status.nmi_line = line;
}
auto CPU::set_irq_line(bool line) -> void {
//level-sensitive
status.irq_line = line;
}
auto CPU::set_irq_apu_line(bool line) -> void {
//level-sensitive
status.irq_apu_line = line;
}
auto CPU::set_rdy_line(bool line) -> void {
status.rdy_line = line;
}
auto CPU::set_rdy_addr(bool valid, uint16 value) -> void {
status.rdy_addr_valid = valid;
status.rdy_addr_value = value;
}

View File

@@ -1,59 +0,0 @@
#ifndef FC_HPP
#define FC_HPP
#include <emulator/emulator.hpp>
#include <processor/r6502/r6502.hpp>
namespace Famicom {
namespace Info {
static const string Name = "bnes";
static const uint SerializerVersion = 2;
}
}
/*
bnes - Famicom emulator
authors: byuu, Ryphecha
license: GPLv3
project started: 2011-09-05
*/
#include <libco/libco.h>
namespace Famicom {
struct Thread {
~Thread() {
if(thread) co_delete(thread);
}
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
if(thread) co_delete(thread);
thread = co_create(65536 * sizeof(void*), entrypoint);
this->frequency = frequency;
clock = 0;
}
auto serialize(serializer& s) -> void {
s.integer(frequency);
s.integer(clock);
}
cothread_t thread = nullptr;
uint frequency = 0;
int64 clock = 0;
};
#include <fc/system/system.hpp>
#include <fc/scheduler/scheduler.hpp>
#include <fc/input/input.hpp>
#include <fc/memory/memory.hpp>
#include <fc/cartridge/cartridge.hpp>
#include <fc/cpu/cpu.hpp>
#include <fc/apu/apu.hpp>
#include <fc/ppu/ppu.hpp>
#include <fc/cheat/cheat.hpp>
#include <fc/video/video.hpp>
#include <fc/interface/interface.hpp>
}
#endif

View File

@@ -1,53 +0,0 @@
#include <fc/fc.hpp>
namespace Famicom {
#include "serialization.cpp"
Input input;
auto Input::latch(bool data) -> void {
latchdata = data;
if(latchdata == 1) {
counter1 = 0;
counter2 = 0;
}
}
auto Input::data(bool port) -> bool {
bool result = 0;
if(port == 0) {
if(port1 == Device::Joypad) {
if(counter1 >= 8) return 1;
result = interface->inputPoll(0, 0u, counter1);
if(latchdata == 0) counter1++;
}
}
if(port == 1) {
if(port2 == Device::Joypad) {
if(counter2 >= 8) return 1;
result = interface->inputPoll(1, 0u, counter2);
if(latchdata == 0) counter2++;
}
}
return result;
}
auto Input::connect(bool port, Device device) -> void {
if(port == 0) port1 = device, counter1 = 0;
if(port == 1) port2 = device, counter2 = 0;
}
auto Input::power() -> void {
}
auto Input::reset() -> void {
latchdata = 0;
counter1 = 0;
counter2 = 0;
}
}

View File

@@ -1,25 +0,0 @@
struct Input {
enum class Device : uint {
Joypad,
None,
};
auto latch(bool data) -> void;
auto data(bool port) -> bool;
auto connect(bool port, Device device) -> void;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
private:
Device port1;
Device port2;
bool latchdata;
uint counter1;
uint counter2;
};
extern Input input;

View File

@@ -1,8 +0,0 @@
auto Input::serialize(serializer& s) -> void {
s.integer((uint&)port1);
s.integer((uint&)port2);
s.integer(latchdata);
s.integer(counter1);
s.integer(counter2);
}

View File

@@ -1,173 +0,0 @@
#include <fc/fc.hpp>
namespace Famicom {
Interface* interface = nullptr;
Interface::Interface() {
interface = this;
information.name = "Famicom";
information.width = 256;
information.height = 240;
information.overscan = true;
information.aspectRatio = 8.0 / 7.0;
information.resettable = true;
information.capability.states = true;
information.capability.cheats = true;
media.append({ID::Famicom, "Famicom", "fc", true});
{ Device device{0, ID::Port1 | ID::Port2, "Controller"};
device.input.append({0, 0, "A" });
device.input.append({1, 0, "B" });
device.input.append({2, 0, "Select"});
device.input.append({3, 0, "Start" });
device.input.append({4, 0, "Up" });
device.input.append({5, 0, "Down" });
device.input.append({6, 0, "Left" });
device.input.append({7, 0, "Right" });
device.order = {4, 5, 6, 7, 1, 0, 2, 3};
this->device.append(device);
}
port.append({0, "Port 1"});
port.append({1, "Port 2"});
for(auto& device : this->device) {
for(auto& port : this->port) {
if(device.portmask & (1 << port.id)) {
port.device.append(device);
}
}
}
}
auto Interface::manifest() -> string {
return cartridge.manifest();
}
auto Interface::title() -> string {
return cartridge.title();
}
auto Interface::videoFrequency() -> double {
return 21477272.0 / (262.0 * 1364.0 - 4.0);
}
auto Interface::audioFrequency() -> double {
return 21477272.0 / 12.0;
}
auto Interface::loaded() -> bool {
return cartridge.loaded();
}
auto Interface::sha256() -> string {
return cartridge.sha256();
}
auto Interface::group(uint id) -> uint {
switch(id) {
case ID::SystemManifest:
return 0;
case ID::Manifest:
case ID::ProgramROM:
case ID::ProgramRAM:
case ID::CharacterROM:
case ID::CharacterRAM:
return 1;
}
throw;
}
auto Interface::load(uint id) -> void {
cartridge.load();
}
auto Interface::save() -> void {
for(auto& memory : cartridge.memory) {
saveRequest(memory.id, memory.name);
}
}
auto Interface::load(uint id, const stream& stream) -> void {
if(id == ID::SystemManifest) {
system.information.manifest = stream.text();
}
if(id == ID::Manifest) {
cartridge.information.markup = stream.text();
}
if(id == ID::ProgramROM) {
stream.read(cartridge.board->prgrom.data, min(cartridge.board->prgrom.size, stream.size()));
}
if(id == ID::ProgramRAM) {
stream.read(cartridge.board->prgram.data, min(cartridge.board->prgram.size, stream.size()));
}
if(id == ID::CharacterROM) {
stream.read(cartridge.board->chrrom.data, min(cartridge.board->chrrom.size, stream.size()));
}
if(id == ID::CharacterRAM) {
stream.read(cartridge.board->chrram.data, min(cartridge.board->chrram.size, stream.size()));
}
}
auto Interface::save(uint id, const stream& stream) -> void {
if(id == ID::ProgramRAM) {
stream.write(cartridge.board->prgram.data, cartridge.board->prgram.size);
}
if(id == ID::CharacterRAM) {
stream.write(cartridge.board->chrram.data, cartridge.board->chrram.size);
}
}
auto Interface::unload() -> void {
save();
cartridge.unload();
}
auto Interface::power() -> void {
system.power();
}
auto Interface::reset() -> void {
system.reset();
}
auto Interface::run() -> void {
system.run();
}
auto Interface::serialize() -> serializer {
system.runtosave();
return system.serialize();
}
auto Interface::unserialize(serializer& s) -> bool {
return system.unserialize(s);
}
auto Interface::cheatSet(const lstring& list) -> void {
cheat.reset();
for(auto& codeset : list) {
lstring codes = codeset.split("+");
for(auto& code : codes) {
lstring part = code.split("/");
if(part.size() == 2) cheat.append(hex(part[0]), hex(part[1]));
if(part.size() == 3) cheat.append(hex(part[0]), hex(part[1]), hex(part[2]));
}
}
}
auto Interface::paletteUpdate(PaletteMode mode) -> void {
video.generate_palette(mode);
}
}

View File

@@ -1,63 +0,0 @@
#ifndef FC_HPP
namespace Famicom {
#endif
struct ID {
enum : uint {
System,
Famicom,
};
enum : uint {
SystemManifest,
Manifest,
ProgramROM,
ProgramRAM,
CharacterROM,
CharacterRAM,
};
enum : uint {
Port1 = 1,
Port2 = 2,
};
};
struct Interface : Emulator::Interface {
Interface();
auto manifest() -> string;
auto title() -> string;
auto videoFrequency() -> double;
auto audioFrequency() -> double;
auto loaded() -> bool;
auto sha256() -> string;
auto group(uint id) -> uint;
auto load(uint id) -> void;
auto save() -> void;
auto load(uint id, const stream& stream) -> void;
auto save(uint id, const stream& stream) -> void;
auto unload() -> void;
auto power() -> void;
auto reset() -> void;
auto run() -> void;
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;
auto cheatSet(const lstring&) -> void;
auto paletteUpdate(PaletteMode mode) -> void;
private:
vector<Device> device;
};
extern Interface* interface;
#ifndef FC_HPP
}
#endif

View File

@@ -1,34 +0,0 @@
#include <fc/fc.hpp>
namespace Famicom {
Bus bus;
//$0000-07ff = RAM (2KB)
//$0800-1fff = RAM (mirror)
//$2000-2007 = PPU
//$2008-3fff = PPU (mirror)
//$4000-4017 = APU + I/O
//$4018-ffff = Cartridge
auto Bus::read(uint16 addr) -> uint8 {
uint8 data = cartridge.prg_read(addr);
if(addr <= 0x1fff) data = cpu.ram_read(addr);
else if(addr <= 0x3fff) data = ppu.read(addr);
else if(addr <= 0x4017) data = cpu.read(addr);
if(cheat.enable()) {
if(auto result = cheat.find(addr, data)) return result();
}
return data;
}
auto Bus::write(uint16 addr, uint8 data) -> void {
cartridge.prg_write(addr, data);
if(addr <= 0x1fff) return cpu.ram_write(addr, data);
if(addr <= 0x3fff) return ppu.write(addr, data);
if(addr <= 0x4017) return cpu.write(addr, data);
}
}

View File

@@ -1,487 +0,0 @@
#include <fc/fc.hpp>
namespace Famicom {
#include "serialization.cpp"
PPU ppu;
auto PPU::Main() -> void {
ppu.main();
}
auto PPU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::PPU) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
raster_scanline();
}
}
auto PPU::tick() -> void {
if(status.ly == 240 && status.lx == 340) status.nmi_hold = 1;
if(status.ly == 241 && status.lx == 0) status.nmi_flag = status.nmi_hold;
if(status.ly == 241 && status.lx == 2) cpu.set_nmi_line(status.nmi_enable && status.nmi_flag);
if(status.ly == 260 && status.lx == 340) status.sprite_zero_hit = 0, status.sprite_overflow = 0;
if(status.ly == 260 && status.lx == 340) status.nmi_hold = 0;
if(status.ly == 261 && status.lx == 0) status.nmi_flag = status.nmi_hold;
if(status.ly == 261 && status.lx == 2) cpu.set_nmi_line(status.nmi_enable && status.nmi_flag);
clock += 4;
if(clock >= 0) co_switch(cpu.thread);
status.lx++;
}
auto PPU::scanline() -> void {
status.lx = 0;
if(++status.ly == 262) {
status.ly = 0;
frame();
}
cartridge.scanline(status.ly);
}
auto PPU::frame() -> void {
status.field ^= 1;
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
auto PPU::power() -> void {
}
auto PPU::reset() -> void {
create(PPU::Main, 21477272);
status.mdr = 0x00;
status.field = 0;
status.ly = 0;
status.bus_data = 0x00;
status.address_latch = 0;
status.vaddr = 0x0000;
status.taddr = 0x0000;
status.xaddr = 0x00;
status.nmi_hold = 0;
status.nmi_flag = 0;
//$2000
status.nmi_enable = false;
status.master_select = 0;
status.sprite_size = 0;
status.bg_addr = 0x0000;
status.sprite_addr = 0x0000;
status.vram_increment = 1;
//$2001
status.emphasis = 0;
status.sprite_enable = false;
status.bg_enable = false;
status.sprite_edge_enable = false;
status.bg_edge_enable = false;
status.grayscale = false;
//$2002
status.sprite_zero_hit = false;
status.sprite_overflow = false;
//$2003
status.oam_addr = 0x00;
for(auto& n : buffer) n = 0;
for(auto& n : ciram ) n = 0;
for(auto& n : cgram ) n = 0;
for(auto& n : oam ) n = 0;
}
auto PPU::read(uint16 addr) -> uint8 {
uint8 result = 0x00;
switch(addr & 7) {
case 2: //PPUSTATUS
result |= status.nmi_flag << 7;
result |= status.sprite_zero_hit << 6;
result |= status.sprite_overflow << 5;
result |= status.mdr & 0x1f;
status.address_latch = 0;
status.nmi_hold = 0;
cpu.set_nmi_line(status.nmi_flag = 0);
break;
case 4: //OAMDATA
result = oam[status.oam_addr];
if((status.oam_addr & 3) == 3) result &= 0xe3;
break;
case 7: //PPUDATA
if(raster_enable() && (status.ly <= 240 || status.ly == 261)) return 0x00;
addr = status.vaddr & 0x3fff;
if(addr <= 0x1fff) {
result = status.bus_data;
status.bus_data = cartridge.chr_read(addr);
} else if(addr <= 0x3eff) {
result = status.bus_data;
status.bus_data = cartridge.chr_read(addr);
} else if(addr <= 0x3fff) {
result = cgram_read(addr);
status.bus_data = cartridge.chr_read(addr);
}
status.vaddr += status.vram_increment;
break;
}
return result;
}
auto PPU::write(uint16 addr, uint8 data) -> void {
status.mdr = data;
switch(addr & 7) {
case 0: //PPUCTRL
status.nmi_enable = data & 0x80;
status.master_select = data & 0x40;
status.sprite_size = data & 0x20;
status.bg_addr = (data & 0x10) ? 0x1000 : 0x0000;
status.sprite_addr = (data & 0x08) ? 0x1000 : 0x0000;
status.vram_increment = (data & 0x04) ? 32 : 1;
status.taddr = (status.taddr & 0x73ff) | ((data & 0x03) << 10);
cpu.set_nmi_line(status.nmi_enable && status.nmi_hold && status.nmi_flag);
return;
case 1: //PPUMASK
status.emphasis = data >> 5;
status.sprite_enable = data & 0x10;
status.bg_enable = data & 0x08;
status.sprite_edge_enable = data & 0x04;
status.bg_edge_enable = data & 0x02;
status.grayscale = data & 0x01;
return;
case 2: //PPUSTATUS
return;
case 3: //OAMADDR
status.oam_addr = data;
return;
case 4: //OAMDATA
oam[status.oam_addr++] = data;
return;
case 5: //PPUSCROLL
if(status.address_latch == 0) {
status.xaddr = data & 0x07;
status.taddr = (status.taddr & 0x7fe0) | (data >> 3);
} else {
status.taddr = (status.taddr & 0x0c1f) | ((data & 0x07) << 12) | ((data >> 3) << 5);
}
status.address_latch ^= 1;
return;
case 6: //PPUADDR
if(status.address_latch == 0) {
status.taddr = (status.taddr & 0x00ff) | ((data & 0x3f) << 8);
} else {
status.taddr = (status.taddr & 0x7f00) | data;
status.vaddr = status.taddr;
}
status.address_latch ^= 1;
return;
case 7: //PPUDATA
if(raster_enable() && (status.ly <= 240 || status.ly == 261)) return;
addr = status.vaddr & 0x3fff;
if(addr <= 0x1fff) {
cartridge.chr_write(addr, data);
} else if(addr <= 0x3eff) {
cartridge.chr_write(addr, data);
} else if(addr <= 0x3fff) {
cgram_write(addr, data);
}
status.vaddr += status.vram_increment;
return;
}
}
auto PPU::ciram_read(uint16 addr) -> uint8 {
return ciram[addr & 0x07ff];
}
auto PPU::ciram_write(uint16 addr, uint8 data) -> void {
ciram[addr & 0x07ff] = data;
}
auto PPU::cgram_read(uint16 addr) -> uint8 {
if((addr & 0x13) == 0x10) addr &= ~0x10;
uint8 data = cgram[addr & 0x1f];
if(status.grayscale) data &= 0x30;
return data;
}
auto PPU::cgram_write(uint16 addr, uint8 data) -> void {
if((addr & 0x13) == 0x10) addr &= ~0x10;
cgram[addr & 0x1f] = data;
}
//
//vaddr = 0yyy VHYY YYYX XXXX
//yyy = fine Yscroll (y:d0-d2)
//V = V nametable (y:d8)
//H = H nametable (x:d8)
//YYYYY = Y nametable (y:d3-d7)
//XXXXX = X nametable (x:d3-d7)
auto PPU::raster_enable() const -> bool {
return (status.bg_enable || status.sprite_enable);
}
auto PPU::nametable_addr() const -> uint {
return 0x2000 + (status.vaddr & 0x0c00);
}
auto PPU::scrollx() const -> uint {
return ((status.vaddr & 0x1f) << 3) | status.xaddr;
}
auto PPU::scrolly() const -> uint {
return (((status.vaddr >> 5) & 0x1f) << 3) | ((status.vaddr >> 12) & 7);
}
auto PPU::sprite_height() const -> uint {
return status.sprite_size == 0 ? 8 : 16;
}
//
auto PPU::chr_load(uint16 addr) -> uint8 {
if(raster_enable() == false) return 0x00;
return cartridge.chr_read(addr);
}
//
auto PPU::scrollx_increment() -> void {
if(raster_enable() == false) return;
status.vaddr = (status.vaddr & 0x7fe0) | ((status.vaddr + 0x0001) & 0x001f);
if((status.vaddr & 0x001f) == 0x0000) {
status.vaddr ^= 0x0400;
}
}
auto PPU::scrolly_increment() -> void {
if(raster_enable() == false) return;
status.vaddr = (status.vaddr & 0x0fff) | ((status.vaddr + 0x1000) & 0x7000);
if((status.vaddr & 0x7000) == 0x0000) {
status.vaddr = (status.vaddr & 0x7c1f) | ((status.vaddr + 0x0020) & 0x03e0);
if((status.vaddr & 0x03e0) == 0x03c0) { //0x03c0 == 30 << 5; 30 * 8 = 240
status.vaddr &= 0x7c1f;
status.vaddr ^= 0x0800;
}
}
}
//
auto PPU::raster_pixel() -> void {
uint32* output = buffer + status.ly * 256;
uint mask = 0x8000 >> (status.xaddr + (status.lx & 7));
uint palette = 0, object_palette = 0;
bool object_priority = 0;
palette |= (raster.tiledatalo & mask) ? 1 : 0;
palette |= (raster.tiledatahi & mask) ? 2 : 0;
if(palette) {
uint attr = raster.attribute;
if(mask >= 256) attr >>= 2;
palette |= (attr & 3) << 2;
}
if(status.bg_enable == false) palette = 0;
if(status.bg_edge_enable == false && status.lx < 8) palette = 0;
if(status.sprite_enable == true)
for(int sprite = 7; sprite >= 0; sprite--) {
if(status.sprite_edge_enable == false && status.lx < 8) continue;
if(raster.oam[sprite].id == 64) continue;
uint spritex = status.lx - raster.oam[sprite].x;
if(spritex >= 8) continue;
if(raster.oam[sprite].attr & 0x40) spritex ^= 7;
uint mask = 0x80 >> spritex;
uint sprite_palette = 0;
sprite_palette |= (raster.oam[sprite].tiledatalo & mask) ? 1 : 0;
sprite_palette |= (raster.oam[sprite].tiledatahi & mask) ? 2 : 0;
if(sprite_palette == 0) continue;
if(raster.oam[sprite].id == 0 && palette && status.lx != 255) status.sprite_zero_hit = 1;
sprite_palette |= (raster.oam[sprite].attr & 3) << 2;
object_priority = raster.oam[sprite].attr & 0x20;
object_palette = 16 + sprite_palette;
}
if(object_palette) {
if(palette == 0 || object_priority == 0) palette = object_palette;
}
if(raster_enable() == false) palette = 0;
output[status.lx] = (status.emphasis << 6) | cgram_read(palette);
}
auto PPU::raster_sprite() -> void {
if(raster_enable() == false) return;
uint n = raster.oam_iterator++;
int ly = (status.ly == 261 ? -1 : status.ly);
uint y = ly - oam[(n * 4) + 0];
if(y >= sprite_height()) return;
if(raster.oam_counter == 8) {
status.sprite_overflow = 1;
return;
}
raster.soam[raster.oam_counter].id = n;
raster.soam[raster.oam_counter].y = oam[(n * 4) + 0];
raster.soam[raster.oam_counter].tile = oam[(n * 4) + 1];
raster.soam[raster.oam_counter].attr = oam[(n * 4) + 2];
raster.soam[raster.oam_counter].x = oam[(n * 4) + 3];
raster.oam_counter++;
}
auto PPU::raster_scanline() -> void {
if((status.ly >= 240 && status.ly <= 260)) {
for(auto x : range(341)) tick();
return scanline();
}
raster.oam_iterator = 0;
raster.oam_counter = 0;
for(auto n : range(8)) {
raster.soam[n].id = 64;
raster.soam[n].y = 0xff;
raster.soam[n].tile = 0xff;
raster.soam[n].attr = 0xff;
raster.soam[n].x = 0xff;
raster.soam[n].tiledatalo = 0;
raster.soam[n].tiledatahi = 0;
}
for(uint tile : range(32)) { // 0-255
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
uint tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
raster_pixel();
tick();
raster_pixel();
tick();
uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
if(scrolly() & 16) attribute >>= 4;
if(scrollx() & 16) attribute >>= 2;
raster_pixel();
tick();
scrollx_increment();
if(tile == 31) scrolly_increment();
raster_pixel();
raster_sprite();
tick();
uint tiledatalo = chr_load(tileaddr + 0);
raster_pixel();
tick();
raster_pixel();
tick();
uint tiledatahi = chr_load(tileaddr + 8);
raster_pixel();
tick();
raster_pixel();
raster_sprite();
tick();
raster.nametable = (raster.nametable << 8) | nametable;
raster.attribute = (raster.attribute << 2) | (attribute & 3);
raster.tiledatalo = (raster.tiledatalo << 8) | tiledatalo;
raster.tiledatahi = (raster.tiledatahi << 8) | tiledatahi;
}
for(auto n : range(8)) raster.oam[n] = raster.soam[n];
for(uint sprite : range(8)) { //256-319
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
tick();
if(raster_enable() && sprite == 0) status.vaddr = (status.vaddr & 0x7be0) | (status.taddr & 0x041f); //257
tick();
uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
uint tileaddr = (sprite_height() == 8)
? status.sprite_addr + raster.oam[sprite].tile * 16
: ((raster.oam[sprite].tile & ~1) * 16) + ((raster.oam[sprite].tile & 1) * 0x1000);
tick();
tick();
uint spritey = (status.ly - raster.oam[sprite].y) & (sprite_height() - 1);
if(raster.oam[sprite].attr & 0x80) spritey ^= (sprite_height() - 1);
tileaddr += spritey + (spritey & 8);
raster.oam[sprite].tiledatalo = chr_load(tileaddr + 0);
tick();
tick();
raster.oam[sprite].tiledatahi = chr_load(tileaddr + 8);
tick();
tick();
if(raster_enable() && sprite == 6 && status.ly == 261) status.vaddr = status.taddr; //304
}
for(uint tile : range(2)) { //320-335
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
uint tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
tick();
tick();
uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
if(scrolly() & 16) attribute >>= 4;
if(scrollx() & 16) attribute >>= 2;
tick();
scrollx_increment();
tick();
uint tiledatalo = chr_load(tileaddr + 0);
tick();
tick();
uint tiledatahi = chr_load(tileaddr + 8);
tick();
tick();
raster.nametable = (raster.nametable << 8) | nametable;
raster.attribute = (raster.attribute << 2) | (attribute & 3);
raster.tiledatalo = (raster.tiledatalo << 8) | tiledatalo;
raster.tiledatahi = (raster.tiledatahi << 8) | tiledatahi;
}
//336-339
chr_load(0x2000 | (status.vaddr & 0x0fff));
tick();
bool skip = (raster_enable() && status.field == 1 && status.ly == 261);
tick();
chr_load(0x2000 | (status.vaddr & 0x0fff));
tick();
tick();
//340
if(skip == false) tick();
return scanline();
}
}

View File

@@ -1,107 +0,0 @@
struct PPU : Thread {
static auto Main() -> void;
auto main() -> void;
auto tick() -> void;
auto scanline() -> void;
auto frame() -> void;
auto power() -> void;
auto reset() -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto ciram_read(uint16 addr) -> uint8;
auto ciram_write(uint16 addr, uint8 data) -> void;
auto cgram_read(uint16 addr) -> uint8;
auto cgram_write(uint16 addr, uint8 data) -> void;
auto raster_enable() const -> bool;
auto nametable_addr() const -> uint;
auto scrollx() const -> uint;
auto scrolly() const -> uint;
auto sprite_height() const -> uint;
auto chr_load(uint16 addr) -> uint8;
auto scrollx_increment() -> void;
auto scrolly_increment() -> void;
auto raster_pixel() -> void;
auto raster_sprite() -> void;
auto raster_scanline() -> void;
auto serialize(serializer&) -> void;
struct Status {
uint8 mdr;
bool field;
uint lx;
uint ly;
uint8 bus_data;
bool address_latch;
uint15 vaddr;
uint15 taddr;
uint8 xaddr;
bool nmi_hold;
bool nmi_flag;
//$2000
bool nmi_enable;
bool master_select;
bool sprite_size;
uint bg_addr;
uint sprite_addr;
uint vram_increment;
//$2001
uint3 emphasis;
bool sprite_enable;
bool bg_enable;
bool sprite_edge_enable;
bool bg_edge_enable;
bool grayscale;
//$2002
bool sprite_zero_hit;
bool sprite_overflow;
//$2003
uint8 oam_addr;
} status;
struct Raster {
uint16 nametable;
uint16 attribute;
uint16 tiledatalo;
uint16 tiledatahi;
uint oam_iterator;
uint oam_counter;
struct OAM {
uint8 id;
uint8 y;
uint8 tile;
uint8 attr;
uint8 x;
uint8 tiledatalo;
uint8 tiledatahi;
} oam[8], soam[8];
} raster;
uint32 buffer[256 * 262];
uint8 ciram[2048];
uint8 cgram[32];
uint8 oam[256];
};
extern PPU ppu;

View File

@@ -1,74 +0,0 @@
auto PPU::serialize(serializer& s) -> void {
Thread::serialize(s);
s.integer(status.mdr);
s.integer(status.field);
s.integer(status.lx);
s.integer(status.ly);
s.integer(status.bus_data);
s.integer(status.address_latch);
s.integer(status.vaddr);
s.integer(status.taddr);
s.integer(status.xaddr);
s.integer(status.nmi_hold);
s.integer(status.nmi_flag);
s.integer(status.nmi_enable);
s.integer(status.master_select);
s.integer(status.sprite_size);
s.integer(status.bg_addr);
s.integer(status.sprite_addr);
s.integer(status.vram_increment);
s.integer(status.emphasis);
s.integer(status.sprite_enable);
s.integer(status.bg_enable);
s.integer(status.sprite_edge_enable);
s.integer(status.bg_edge_enable);
s.integer(status.grayscale);
s.integer(status.sprite_zero_hit);
s.integer(status.sprite_overflow);
s.integer(status.oam_addr);
s.integer(raster.nametable);
s.integer(raster.attribute);
s.integer(raster.tiledatalo);
s.integer(raster.tiledatahi);
s.integer(raster.oam_iterator);
s.integer(raster.oam_counter);
for(auto n : range(8)) {
s.integer(raster.oam[n].id);
s.integer(raster.oam[n].y);
s.integer(raster.oam[n].tile);
s.integer(raster.oam[n].attr);
s.integer(raster.oam[n].x);
s.integer(raster.oam[n].tiledatalo);
s.integer(raster.oam[n].tiledatahi);
}
for(auto n : range(8)) {
s.integer(raster.soam[n].id);
s.integer(raster.soam[n].y);
s.integer(raster.soam[n].tile);
s.integer(raster.soam[n].attr);
s.integer(raster.soam[n].x);
s.integer(raster.soam[n].tiledatalo);
s.integer(raster.soam[n].tiledatahi);
}
s.array(buffer);
s.array(ciram);
s.array(cgram);
s.array(oam);
}

View File

@@ -1,28 +0,0 @@
#include <fc/fc.hpp>
namespace Famicom {
Scheduler scheduler;
auto Scheduler::enter() -> void {
host_thread = co_active();
co_switch(thread);
}
auto Scheduler::exit(ExitReason reason) -> void {
exit_reason = reason;
thread = co_active();
co_switch(host_thread);
}
auto Scheduler::power() -> void {
}
auto Scheduler::reset() -> void {
host_thread = co_active();
thread = cpu.thread;
sync = SynchronizeMode::None;
exit_reason = ExitReason::UnknownEvent;
}
}

View File

@@ -1,16 +0,0 @@
struct Scheduler : property<Scheduler> {
enum class SynchronizeMode : uint { None, PPU, All } sync;
enum class ExitReason : uint { UnknownEvent, FrameEvent, SynchronizeEvent };
auto enter() -> void;
auto exit(ExitReason) -> void;
auto power() -> void;
auto reset() -> void;
cothread_t host_thread; //program thread (used to exit emulation)
cothread_t thread; //active emulation thread (used to enter emulation)
readonly<ExitReason> exit_reason;
};
extern Scheduler scheduler;

View File

@@ -1,60 +0,0 @@
auto System::serialize() -> serializer {
serializer s(serialize_size);
uint signature = 0x31545342, version = Info::SerializerVersion;
char hash[64], description[512];
memcpy(&hash, (const char*)cartridge.sha256(), 64);
memset(&description, 0, sizeof description);
s.integer(signature);
s.integer(version);
s.array(hash);
s.array(description);
serialize_all(s);
return s;
}
auto System::unserialize(serializer& s) -> bool {
uint signature, version;
char hash[64], description[512];
s.integer(signature);
s.integer(version);
s.array(hash);
s.array(description);
if(signature != 0x31545342) return false;
if(version != Info::SerializerVersion) return false;
power();
serialize_all(s);
return true;
}
auto System::serialize(serializer& s) -> void {
}
auto System::serialize_all(serializer& s) -> void {
system.serialize(s);
input.serialize(s);
cartridge.serialize(s);
cpu.serialize(s);
apu.serialize(s);
ppu.serialize(s);
}
auto System::serialize_init() -> void {
serializer s;
uint signature = 0, version = 0;
char hash[64], description[512];
s.integer(signature);
s.integer(version);
s.array(hash);
s.array(description);
serialize_all(s);
serialize_size = s.size();
}

View File

@@ -1,79 +0,0 @@
#include <fc/fc.hpp>
namespace Famicom {
#include "serialization.cpp"
System system;
auto System::run() -> void {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(video.palette, ppu.buffer, 4 * 256, 256, 240);
}
}
auto System::runtosave() -> void {
scheduler.sync = Scheduler::SynchronizeMode::PPU;
runthreadtosave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.thread = cpu.thread;
runthreadtosave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.thread = apu.thread;
runthreadtosave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.thread = cartridge.thread;
runthreadtosave();
scheduler.sync = Scheduler::SynchronizeMode::None;
}
auto System::runthreadtosave() -> void {
while(true) {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(video.palette, ppu.buffer, 4 * 256, 256, 240);
}
}
}
auto System::load() -> void {
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
auto document = BML::unserialize(information.manifest);
serialize_init();
}
auto System::power() -> void {
cartridge.power();
cpu.power();
apu.power();
ppu.power();
input.reset();
scheduler.power();
reset();
}
auto System::reset() -> void {
cartridge.reset();
cpu.reset();
apu.reset();
ppu.reset();
input.reset();
scheduler.reset();
}
auto System::init() -> void {
assert(interface != 0);
input.connect(0, Input::Device::Joypad);
input.connect(1, Input::Device::None);
}
auto System::term() -> void {
}
}

View File

@@ -1,27 +0,0 @@
struct System {
auto run() -> void;
auto runtosave() -> void;
auto runthreadtosave() -> void;
auto load() -> void;
auto power() -> void;
auto reset() -> void;
auto init() -> void;
auto term() -> void;
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;
auto serialize(serializer&) -> void;
auto serialize_all(serializer&) -> void;
auto serialize_init() -> void;
struct Information {
string manifest;
} information;
uint serialize_size;
};
extern System system;

View File

@@ -1,85 +0,0 @@
#include <fc/fc.hpp>
#include <cmath>
#define VIDEO_CPP
namespace Famicom {
Video video;
Video::Video() {
palette = new uint32_t[1 << 9]();
}
Video::~Video() {
delete[] palette;
}
auto Video::generate_palette(Emulator::Interface::PaletteMode mode) -> void {
for(auto color : range(1 << 9)) {
if(mode == Emulator::Interface::PaletteMode::Literal) {
palette[color] = color;
} else if(mode == Emulator::Interface::PaletteMode::Channel) {
uint emphasis = (color >> 6) & 7;
uint luma = (color >> 4) & 3;
uint chroma = (color >> 0) & 15;
emphasis = image::normalize(emphasis, 3, 16);
luma = image::normalize(luma, 2, 16);
chroma = image::normalize(chroma, 4, 16);
palette[color] = interface->videoColor(color, 0, emphasis, luma, chroma);
} else if(mode == Emulator::Interface::PaletteMode::Standard) {
palette[color] = generate_color(color, 2.0, 0.0, 1.0, 1.0, 2.2);
} else if(mode == Emulator::Interface::PaletteMode::Emulation) {
palette[color] = generate_color(color, 2.0, 0.0, 1.0, 1.0, 1.8);
}
}
}
auto Video::generate_color(
uint n, double saturation, double hue,
double contrast, double brightness, double gamma
) -> uint32 {
int color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
static const double black = 0.518, white = 1.962, attenuation = 0.746;
static const double levels[8] = {
0.350, 0.518, 0.962, 1.550,
1.094, 1.506, 1.962, 1.962,
};
double lo_and_hi[2] = {
levels[level + 4 * (color == 0x0)],
levels[level + 4 * (color < 0xd)],
};
double y = 0.0, i = 0.0, q = 0.0;
auto wave = [](int p, int color) { return (color + p + 8) % 12 < 6; };
for(int p : range(12)) {
double spot = lo_and_hi[wave(p, color)];
if(((n & 0x040) && wave(p, 12))
|| ((n & 0x080) && wave(p, 4))
|| ((n & 0x100) && wave(p, 8))
) spot *= attenuation;
double v = (spot - black) / (white - black);
v = (v - 0.5) * contrast + 0.5;
v *= brightness / 12.0;
y += v;
i += v * std::cos((3.141592653 / 6.0) * (p + hue));
q += v * std::sin((3.141592653 / 6.0) * (p + hue));
}
i *= saturation;
q *= saturation;
auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); };
uint r = 65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q);
uint g = 65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
uint b = 65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q);
return interface->videoColor(n, 0, uclamp<16>(r), uclamp<16>(g), uclamp<16>(b));
}
}

View File

@@ -1,13 +0,0 @@
struct Video {
Video();
~Video();
auto generate_palette(Emulator::Interface::PaletteMode mode) -> void;
uint32* palette = nullptr;
private:
auto generate_color(uint, double, double, double, double, double) -> uint32;
};
extern Video video;

View File

@@ -1,16 +0,0 @@
gb_objects := gb-interface gb-system gb-scheduler
gb_objects += gb-memory gb-cartridge
gb_objects += gb-cpu gb-ppu gb-apu
gb_objects += gb-cheat gb-video
objects += $(gb_objects)
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-scheduler.o: $(gb)/scheduler/scheduler.cpp $(call rwildcard,$(gb)/scheduler/)
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-cheat.o: $(gb)/cheat/cheat.cpp $(call rwildcard,$(gb)/cheat/)
obj/gb-video.o: $(gb)/video/video.cpp $(call rwildcard,$(gb)/video/)

View File

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

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