2016-01-11 10:31:30 +00:00
|
|
|
#pragma once
|
2010-12-28 01:53:15 +00:00
|
|
|
|
2016-04-09 05:20:41 +00:00
|
|
|
//license: GPLv3
|
|
|
|
//started: 2010-12-27
|
|
|
|
|
Update to v088r08 release.
byuu says:
From this WIP, I'm starting on the impossible task of
a declarative-based GUI, which I'm calling Ethos.
base/ becomes emulator/, and we add emulator/interface.hpp, which is
a base API that all emulation cores must implement in full.
(Right now, it's kind of a hybrid to work with the old GUI and the new
GUI at the same time, of course.)
Unlike the old interfaces, the new base class also provides all general
usability hooks: loading and saving files and states, cheat codes, etc.
The new interface also contains information and vector structs to
describe all possible loading methods, controller bindings, etc; and
gives names for them all.
The actual GUI in fact should not include eg <gba/gba.hpp> anymore.
Should speed up GUI compilation.
So the idea going forward is that ethos will build a list of emulators
right when the application starts up.
Once you've appended an emulator to that list, you're done. No more GUI
changes are needed to support that system.
The GUI will have code to parse the emulator interfaces list, and build
all the requisite GUI options dynamically, declarative style.
Ultimately, once the project is finished, the new GUI should look ~99%
identical to the current GUI. But it'll probably be a whole lot smaller.
2012-04-29 06:29:54 +00:00
|
|
|
#include <emulator/emulator.hpp>
|
2016-07-10 05:28:26 +00:00
|
|
|
#include <emulator/thread.hpp>
|
|
|
|
#include <emulator/scheduler.hpp>
|
|
|
|
#include <emulator/cheat.hpp>
|
|
|
|
|
2012-04-26 10:51:13 +00:00
|
|
|
#include <processor/lr35902/lr35902.hpp>
|
2012-02-06 12:03:45 +00:00
|
|
|
|
2012-04-26 10:51:13 +00:00
|
|
|
namespace GameBoy {
|
2017-01-13 01:15:45 +00:00
|
|
|
#define platform Emulator::platform
|
2017-02-10 23:56:42 +00:00
|
|
|
namespace File = Emulator::File;
|
2016-07-10 05:28:26 +00:00
|
|
|
using Scheduler = Emulator::Scheduler;
|
|
|
|
using Cheat = Emulator::Cheat;
|
|
|
|
extern Scheduler scheduler;
|
|
|
|
extern Cheat cheat;
|
2010-12-28 06:03:02 +00:00
|
|
|
|
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 03:56:12 +00:00
|
|
|
struct Thread : Emulator::Thread {
|
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 02:11:20 +00:00
|
|
|
auto create(auto (*entrypoint)() -> void, double frequency) -> void {
|
|
|
|
Emulator::Thread::create(entrypoint, frequency);
|
|
|
|
scheduler.append(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline auto synchronize(Thread& thread) -> void {
|
|
|
|
if(clock() >= thread.clock()) scheduler.resume(thread);
|
|
|
|
}
|
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 03:56:12 +00:00
|
|
|
};
|
|
|
|
|
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 01:06:06 +00:00
|
|
|
struct Model {
|
|
|
|
inline static auto GameBoy() -> bool;
|
|
|
|
inline static auto GameBoyColor() -> bool;
|
|
|
|
inline static auto SuperGameBoy() -> bool;
|
|
|
|
};
|
|
|
|
|
Update to v087r04 release.
byuu says:
Changelog:
- gameboy/ -> gb/
- GameBoy -> GB
- basic memory map for GBA
- enough code to execute the first BIOS instruction (b 0x68)
I have the code resetting r(15) to 0 on an exception just as a test.
Since that flushes the pipeline, that means we're basically executing "b
0x68" at 8MHz, and nothing else.
... and I am getting __6 motherfucking FPS__ at 4.4GHz on an i7.
Something is seriously, horribly, unfuckingbelievably wrong here, and
I can't figure out what it is.
My *fully complete* ARM core on the ST018 is even less efficient and
runs at 21.47MHz, and yet I get 60fps even after emulating the SNES
CPU+PPU @ 10+MHz each as well.
... I'm stuck. I can't proceed until we figure out what in the holy fuck
is going on here. So ... if anyone can help, please do. If we can't fix
this, the GBA emulation is dead.
I was able to profile on Windows, and I've included that in this WIP
under out/log.txt.
But it looks normal to me. But yeah, there's NO. FUCKING. WAY. This code
should be running this slowly.
2012-03-18 12:35:53 +00:00
|
|
|
#include <gb/memory/memory.hpp>
|
|
|
|
#include <gb/system/system.hpp>
|
|
|
|
#include <gb/cartridge/cartridge.hpp>
|
|
|
|
#include <gb/cpu/cpu.hpp>
|
2012-04-26 10:51:13 +00:00
|
|
|
#include <gb/ppu/ppu.hpp>
|
Update to v087r04 release.
byuu says:
Changelog:
- gameboy/ -> gb/
- GameBoy -> GB
- basic memory map for GBA
- enough code to execute the first BIOS instruction (b 0x68)
I have the code resetting r(15) to 0 on an exception just as a test.
Since that flushes the pipeline, that means we're basically executing "b
0x68" at 8MHz, and nothing else.
... and I am getting __6 motherfucking FPS__ at 4.4GHz on an i7.
Something is seriously, horribly, unfuckingbelievably wrong here, and
I can't figure out what it is.
My *fully complete* ARM core on the ST018 is even less efficient and
runs at 21.47MHz, and yet I get 60fps even after emulating the SNES
CPU+PPU @ 10+MHz each as well.
... I'm stuck. I can't proceed until we figure out what in the holy fuck
is going on here. So ... if anyone can help, please do. If we can't fix
this, the GBA emulation is dead.
I was able to profile on Windows, and I've included that in this WIP
under out/log.txt.
But it looks normal to me. But yeah, there's NO. FUCKING. WAY. This code
should be running this slowly.
2012-03-18 12:35:53 +00:00
|
|
|
#include <gb/apu/apu.hpp>
|
2016-01-11 10:31:30 +00:00
|
|
|
}
|
Update to v082r04 release.
byuu says:
So, here's the deal. I now have three emulators. I don't think the
NES/GB ones are at all useful, but I do want them to be eventually. And
having them have those pathetic little GUIs like ui-gameboy, and keeping
everything in separate project folders, just doesn't work well for me.
I kind of "got around" the issue with the Game Boy, by only allowing SGB
mode emulation. But there is no "Super Nintendo" ... er ... wait ...
uhmm ... well, you know what I mean anyway.
So, my idea is to write a multi-emulator GUI, and keep the projects
together. The GUI is not going to change much. The way I envision this
working:
At startup, you have a menubar with: "Cartridge, Settings, Tools, Help".
Cartridge has "Load NES Cartridge", "Load SNES Cartridge", etc.
When you load something, Cartridge is replaced with the appropriate
system menu, eg "SNES". Here you have all your regular items: "power,
reset, controller port selection, etc." There is also a new "Unload
Cartridge" option, which is how you restore the "Cartridge" menu again.
I have no plans to emulate any other systems, but if I ever do emulate
something that doesn't take cartridges, I'll change the name to just
"Load" or something.
The cheat editor / state manager will look and act exactly the same. The
settings panel will look exactly the same. I'll simply show/hide
system-specific options as needed, like NES/SNES aspect ratio
correction, etc. The input mapping window will just have settings for
the currently loaded system. Video and audio tweaking will apply
cross-system, as will hotkey mapping.
The GUI stuff is mostly copy-paste, so it should only take me a week to
get it 95% back to where it was, so don't worry, this isn't total GUI
rewrite #80.
I am, however, making all the objects pointers, so that I can destruct
them all prior to main() returning, which is certainly one way of fixing
that annoying Windows/Qt crash.
Please only test on Linux. The Windows port is broken to hell, and will
give you a bad impression of the idea:
- menu groups are not hiding for some reason (all groups are showing, it
looks hideous)
- Timer interval(0) is taking 16ms per call, capping the FPS to ~64 tops
[FWIW, bsnes/accuracy gets 130fps, bgameboy gets 450fps, bnes gets
800fps; all run at lowest possible granularity]
- the OS keeps beeping when you press keys (AGAIN)
Of course, Qt and GTK+ don't let you shrink a window from the requested
geometry size, because they suck. So the video scaling stuff doesn't
work all that great yet.
Man, a metric fuckton of things need to be fixed in phoenix, and
I really don't know how to fix any of them :/
2011-09-09 04:08:38 +00:00
|
|
|
|
2016-01-11 10:31:30 +00:00
|
|
|
#include <gb/interface/interface.hpp>
|