Compare commits

...

37 Commits
v088 ... v091

Author SHA1 Message Date
Tim Allen
7f404e6edb Update to v091 release.
byuu says:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This implements the spec from the XML part 3 thread:

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

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

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

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

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

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

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

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

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

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

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

byuu says:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

byuu says:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

byuu says:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[...]

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

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

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

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

- GB::CPU::Core -> Processor::LR35902
- Processor::LR35902 -> jump table to switch table
- GB::LCD -> GB::PPU
- static frequency for DMG (no multiplication on clock ticks)
- GB::PPU::interface->videoRefresh() moved outside scheduler (use host
  thread)
- namespace NES  -> Famicom
- namespace SNES -> SuperFamicom
- namespace GB   -> GameBoy
- namespace GBA  -> GameBoyAdvance
- removed boot.rom writeout in GB::System
2012-04-26 20:51:13 +10:00
1819 changed files with 152392 additions and 148123 deletions

29
bsnes/Makefile Executable file → Normal file
View File

@@ -1,14 +1,17 @@
include nall/Makefile
nes := nes
snes := snes
gb := gb
gba := gba
fc := fc
sfc := sfc
gb := gb
gba := gba
nds := nds
profile := accuracy
target := ui
target := ethos
# options += console
# options += debugger
# arch := win32
# console := true
# compiler
c := $(compiler) -std=gnu99
@@ -34,7 +37,15 @@ ifeq ($(platform),x)
link += -ldl -lX11 -lXext
else ifeq ($(platform),osx)
else ifeq ($(platform),win)
link += $(if $(findstring console,$(options)),-mconsole,-mwindows)
ifeq ($(arch),win32)
flags += -m32
link += -m32
endif
ifeq ($(console),true)
link += -mconsole
else
link += -mwindows
endif
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
else
@@ -94,7 +105,7 @@ sync:
rm -r phoenix/test
archive:
if [ -f bsnes.tar.bz2 ]; then rm bsnes.tar.bz2; fi
tar -cjf bsnes.tar.bz2 `ls`
if [ -f bsnes.tar.xz ]; then rm bsnes.tar.xz; fi
tar -cJf bsnes.tar.xz `ls`
help:;

View File

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

0
bsnes/data/bsnes.Manifest Executable file → Normal file
View File

0
bsnes/data/bsnes.desktop Executable file → Normal file
View File

0
bsnes/data/bsnes.ico Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

0
bsnes/data/bsnes.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

218557
bsnes/data/cheats.xml Executable file → Normal file

File diff suppressed because it is too large Load Diff

0
bsnes/data/laevateinn.hpp Executable file → Normal file
View File

27
bsnes/base/base.hpp → bsnes/emulator/emulator.hpp Executable file → Normal file
View File

@@ -1,30 +1,37 @@
#ifndef BASE_HPP
#define BASE_HPP
#ifndef EMULATOR_HPP
#define EMULATOR_HPP
static const char Version[] = "088";
namespace Emulator {
static const char Name[] = "bsnes";
static const char Version[] = "091";
static const char Author[] = "byuu";
static const char License[] = "GPLv3";
}
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>
#include <nall/any.hpp>
#include <nall/array.hpp>
#include <nall/bitarray.hpp>
#include <nall/directory.hpp>
#include <nall/dl.hpp>
#include <nall/dsp.hpp>
#include <nall/endian.hpp>
#include <nall/file.hpp>
#include <nall/function.hpp>
#include <nall/moduloarray.hpp>
#include <nall/priorityqueue.hpp>
#include <nall/priority-queue.hpp>
#include <nall/property.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/sha256.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
#include <nall/varint.hpp>
#include <nall/vector.hpp>
#include <nall/stream/memory.hpp>
#include <nall/stream/vector.hpp>
using namespace nall;
#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
@@ -43,8 +50,8 @@ template<typename R, typename... P> struct hook<R (P...)> {
hook(const hook &hook) { callback = hook.callback; }
hook(void *function) { callback = function; }
hook(R (*function)(P...)) { callback = function; }
template<typename C> hook(R (C::*function)(P...), C *object) { callback = { function, object }; }
template<typename C> hook(R (C::*function)(P...) const, C *object) { callback = { function, object }; }
template<typename C> hook(R (C::*function)(P...), C *object) { callback = {function, object}; }
template<typename C> hook(R (C::*function)(P...) const, C *object) { callback = {function, object}; }
template<typename L> hook(const L& function) { callback = function; }
hook& operator=(const hook& hook) { callback = hook.callback; return *this; }

View File

@@ -0,0 +1,118 @@
#ifndef EMULATOR_INTERFACE_HPP
#define EMULATOR_INTERFACE_HPP
namespace Emulator {
struct Interface {
struct Information {
string name;
unsigned width;
unsigned height;
bool overscan;
double aspectRatio;
bool resettable;
struct Capability {
bool states;
bool cheats;
} capability;
} information;
struct Media {
unsigned id;
string name;
string type;
string load;
};
vector<Media> media;
struct Device {
unsigned id;
unsigned portmask;
string name;
struct Input {
unsigned id;
unsigned type; //0 = digital, 1 = analog (relative), 2 = analog (absolute)
string name;
unsigned guid;
};
vector<Input> input;
vector<unsigned> order;
};
struct Port {
unsigned id;
string name;
vector<Device> device;
};
vector<Port> port;
struct Bind {
virtual void loadRequest(unsigned, const string&, const string&) {}
virtual void loadRequest(unsigned, const string&) {}
virtual void saveRequest(unsigned, const string&) {}
virtual uint32_t videoColor(unsigned, uint16_t, uint16_t, uint16_t) { return 0u; }
virtual void videoRefresh(const uint32_t*, unsigned, unsigned, unsigned) {}
virtual void audioSample(int16_t, int16_t) {}
virtual int16_t inputPoll(unsigned, unsigned, unsigned) { return 0; }
virtual unsigned dipSettings(const XML::Node&) { return 0; }
virtual string path(unsigned) { return ""; }
virtual void notify(const string &text) { print(text, "\n"); }
} *bind;
//callback bindings (provided by user interface)
void loadRequest(unsigned id, const string &name, const string &type) { return bind->loadRequest(id, name, type); }
void loadRequest(unsigned id, const string &path) { return bind->loadRequest(id, path); }
void saveRequest(unsigned id, const string &path) { return bind->saveRequest(id, path); }
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) { return bind->videoColor(source, red, green, blue); }
void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) { return bind->videoRefresh(data, pitch, width, height); }
void audioSample(int16_t lsample, int16_t rsample) { return bind->audioSample(lsample, rsample); }
int16_t inputPoll(unsigned port, unsigned device, unsigned input) { return bind->inputPoll(port, device, input); }
unsigned dipSettings(const XML::Node &node) { return bind->dipSettings(node); }
string path(unsigned group) { return bind->path(group); }
template<typename... Args> void notify(Args&... args) { return bind->notify({std::forward<Args>(args)...}); }
//information
virtual double videoFrequency() = 0;
virtual double audioFrequency() = 0;
//media interface
virtual bool loaded() { return false; }
virtual string sha256() { return ""; }
virtual unsigned group(unsigned id) = 0;
virtual void load(unsigned id, const string &manifest) {}
virtual void save() {}
virtual void load(unsigned id, const stream &memory, const string &markup = "") {}
virtual void save(unsigned id, const stream &memory) {}
virtual void unload() {}
//system interface
virtual void connect(unsigned port, unsigned device) {}
virtual void power() {}
virtual void reset() {}
virtual void run() {}
//time functions
virtual bool rtc() { return false; }
virtual void rtcsync() {}
//state functions
virtual serializer serialize() = 0;
virtual bool unserialize(serializer&) = 0;
//cheat functions
virtual void cheatSet(const lstring& = lstring{}) {}
//utility functions
virtual void paletteUpdate() {}
//debugger functions
virtual bool tracerEnable(bool) { return false; }
virtual void exportMemory() {}
Interface() : bind(nullptr) {}
};
}
#endif

16
bsnes/fc/Makefile Normal file
View File

@@ -0,0 +1,16 @@
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/)

6
bsnes/nes/apu/apu.cpp → bsnes/fc/apu/apu.cpp Executable file → Normal file
View File

@@ -1,6 +1,6 @@
#include <nes/nes.hpp>
#include <fc/fc.hpp>
namespace NES {
namespace Famicom {
#include "envelope.cpp"
#include "sweep.cpp"
@@ -60,7 +60,7 @@ void APU::main() {
//output = filter.run_lopass(output);
output = sclamp<16>(output);
interface->audioSample(output);
interface->audioSample(output, output);
tick();
}

0
bsnes/nes/apu/apu.hpp → bsnes/fc/apu/apu.hpp Executable file → Normal file
View File

0
bsnes/nes/apu/dmc.cpp → bsnes/fc/apu/dmc.cpp Executable file → Normal file
View File

0
bsnes/nes/apu/dmc.hpp → bsnes/fc/apu/dmc.hpp Executable file → Normal file
View File

View File

View File

0
bsnes/nes/apu/noise.cpp → bsnes/fc/apu/noise.cpp Executable file → Normal file
View File

0
bsnes/nes/apu/noise.hpp → bsnes/fc/apu/noise.hpp Executable file → Normal file
View File

0
bsnes/nes/apu/pulse.cpp → bsnes/fc/apu/pulse.cpp Executable file → Normal file
View File

0
bsnes/nes/apu/pulse.hpp → bsnes/fc/apu/pulse.hpp Executable file → Normal file
View File

View File

0
bsnes/nes/apu/sweep.cpp → bsnes/fc/apu/sweep.cpp Executable file → Normal file
View File

0
bsnes/nes/apu/sweep.hpp → bsnes/fc/apu/sweep.hpp Executable file → Normal file
View File

View File

View File

View File

@@ -111,7 +111,7 @@ void serialize(serializer &s) {
s.integer(irq_latch);
}
BandaiFCG(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size) {
BandaiFCG(XML::Document &document) : Board(document) {
}
};

View File

@@ -0,0 +1,174 @@
#include "bandai-fcg.cpp"
#include "konami-vrc1.cpp"
#include "konami-vrc2.cpp"
#include "konami-vrc3.cpp"
#include "konami-vrc4.cpp"
#include "konami-vrc6.cpp"
#include "konami-vrc7.cpp"
#include "nes-axrom.cpp"
#include "nes-bnrom.cpp"
#include "nes-cnrom.cpp"
#include "nes-exrom.cpp"
#include "nes-fxrom.cpp"
#include "nes-gxrom.cpp"
#include "nes-hkrom.cpp"
#include "nes-nrom.cpp"
#include "nes-pxrom.cpp"
#include "nes-sxrom.cpp"
#include "nes-txrom.cpp"
#include "nes-uxrom.cpp"
#include "sunsoft-5b.cpp"
uint8 Board::Memory::read(unsigned addr) const {
return data[mirror(addr, size)];
}
void Board::Memory::write(unsigned addr, uint8 byte) {
if(writable) data[mirror(addr, size)] = byte;
}
unsigned Board::mirror(unsigned addr, unsigned size) {
unsigned base = 0;
if(size) {
unsigned mask = 1 << 23;
while(addr >= size) {
while(!(addr & mask)) mask >>= 1;
addr -= mask;
if(size > mask) {
size -= mask;
base += mask;
}
mask >>= 1;
}
base += addr;
}
return base;
}
void Board::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
cartridge.clock += 12 * 4095;
tick();
}
}
void Board::tick() {
cartridge.clock += 12;
if(cartridge.clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
}
uint8 Board::chr_read(unsigned addr) {
if(chrram.size) return chrram.data[mirror(addr, chrram.size)];
if(chrrom.size) return chrrom.data[mirror(addr, chrrom.size)];
return 0u;
}
void Board::chr_write(unsigned addr, uint8 data) {
if(chrram.size) chrram.data[mirror(addr, chrram.size)] = data;
}
void Board::power() {
}
void Board::reset() {
}
void Board::serialize(serializer &s) {
if(prgram.size) s.array(prgram.data, prgram.size);
if(chrram.size) s.array(chrram.data, chrram.size);
}
Board::Board(XML::Document &document) {
cartridge.board = this;
auto &cartridge = document["cartridge"];
information.type = cartridge["board"]["type"].data;
information.battery = cartridge["prg"]["ram"]["nonvolatile"].data == "true";
auto &prom = cartridge["prg"]["rom"];
auto &pram = cartridge["prg"]["ram"];
auto &crom = cartridge["chr"]["rom"];
auto &cram = cartridge["chr"]["ram"];
prgrom.size = numeral(prom["size"].data);
prgram.size = numeral(pram["size"].data);
chrrom.size = numeral(crom["size"].data);
chrram.size = numeral(cram["size"].data);
if(prgrom.size) prgrom.data = new uint8[prgrom.size]();
if(prgram.size) prgram.data = new uint8[prgram.size]();
if(chrrom.size) chrrom.data = new uint8[chrrom.size]();
if(chrram.size) chrram.data = new uint8[chrram.size]();
if(prom["name"].data) interface->loadRequest(ID::ProgramROM, prom["name"].data);
if(pram["name"].data) interface->loadRequest(ID::ProgramRAM, pram["name"].data);
if(crom["name"].data) interface->loadRequest(ID::CharacterROM, crom["name"].data);
if(cram["name"].data) interface->loadRequest(ID::CharacterRAM, cram["name"].data);
if(pram["name"].data) Famicom::cartridge.memory.append({ID::ProgramRAM, pram["name"].data});
if(cram["name"].data) Famicom::cartridge.memory.append({ID::CharacterRAM, cram["name"].data});
prgram.writable = true;
chrram.writable = true;
}
Board::~Board() {
}
Board* Board::load(const string &manifest) {
XML::Document document(manifest);
string type = document["cartridge"]["board"]["type"].data;
if(type == "BANDAI-FCG" ) return new BandaiFCG(document);
if(type == "KONAMI-VRC-1") return new KonamiVRC1(document);
if(type == "KONAMI-VRC-2") return new KonamiVRC2(document);
if(type == "KONAMI-VRC-3") return new KonamiVRC3(document);
if(type == "KONAMI-VRC-4") return new KonamiVRC4(document);
if(type == "KONAMI-VRC-6") return new KonamiVRC6(document);
if(type == "KONAMI-VRC-7") return new KonamiVRC7(document);
if(type == "NES-AMROM" ) return new NES_AxROM(document);
if(type == "NES-ANROM" ) return new NES_AxROM(document);
if(type == "NES-AN1ROM" ) return new NES_AxROM(document);
if(type == "NES-AOROM" ) return new NES_AxROM(document);
if(type == "NES-BNROM" ) return new NES_BNROM(document);
if(type == "NES-CNROM" ) return new NES_CNROM(document);
if(type == "NES-EKROM" ) return new NES_ExROM(document);
if(type == "NES-ELROM" ) return new NES_ExROM(document);
if(type == "NES-ETROM" ) return new NES_ExROM(document);
if(type == "NES-EWROM" ) return new NES_ExROM(document);
if(type == "NES-FJROM" ) return new NES_FxROM(document);
if(type == "NES-FKROM" ) return new NES_FxROM(document);
if(type == "NES-GNROM" ) return new NES_GxROM(document);
if(type == "NES-MHROM" ) return new NES_GxROM(document);
if(type == "NES-HKROM" ) return new NES_HKROM(document);
if(type == "NES-NROM-128") return new NES_NROM(document);
if(type == "NES-NROM-256") return new NES_NROM(document);
if(type == "NES-PEEOROM" ) return new NES_PxROM(document);
if(type == "NES-PNROM" ) return new NES_PxROM(document);
if(type == "NES-SNROM" ) return new NES_SxROM(document);
if(type == "NES-SXROM" ) return new NES_SxROM(document);
if(type == "NES-TLROM" ) return new NES_TxROM(document);
if(type == "NES-UNROM" ) return new NES_UxROM(document);
if(type == "NES-UOROM" ) return new NES_UxROM(document);
if(type == "SUNSOFT-5B" ) return new Sunsoft5B(document);
return nullptr;
}

View File

@@ -25,16 +25,14 @@ struct Board {
virtual inline void scanline(unsigned y) {}
virtual Memory& memory();
virtual void power();
virtual void reset();
virtual void serialize(serializer&);
Board(XML::Document &document, const uint8_t *data, unsigned size);
Board(XML::Document &document);
virtual ~Board();
static Board* load(const string &markup, const uint8_t *data, unsigned size);
static Board* load(const string &manifest);
struct Information {
string type;

View File

@@ -34,7 +34,7 @@ void serialize(serializer &s) {
vrc1.serialize(s);
}
KonamiVRC1(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size), vrc1(*this) {
KonamiVRC1(XML::Document &document) : Board(document), vrc1(*this) {
}
};

View File

@@ -49,7 +49,7 @@ void serialize(serializer &s) {
vrc2.serialize(s);
}
KonamiVRC2(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size), vrc2(*this) {
KonamiVRC2(XML::Document &document) : Board(document), vrc2(*this) {
settings.pinout.a0 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a0"].data);
settings.pinout.a1 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a1"].data);
}

View File

@@ -50,7 +50,7 @@ void serialize(serializer &s) {
vrc3.serialize(s);
}
KonamiVRC3(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size), vrc3(*this) {
KonamiVRC3(XML::Document &document) : Board(document), vrc3(*this) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@@ -53,7 +53,7 @@ void serialize(serializer &s) {
vrc4.serialize(s);
}
KonamiVRC4(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size), vrc4(*this) {
KonamiVRC4(XML::Document &document) : Board(document), vrc4(*this) {
settings.pinout.a0 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a0"].data);
settings.pinout.a1 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a1"].data);
}

View File

@@ -36,7 +36,7 @@ void main() { vrc6.main(); }
void power() { vrc6.power(); }
void reset() { vrc6.reset(); }
KonamiVRC6(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size), vrc6(*this) {
KonamiVRC6(XML::Document &document) : Board(document), vrc6(*this) {
}
};

View File

@@ -41,7 +41,7 @@ void serialize(serializer &s) {
vrc7.serialize(s);
}
KonamiVRC7(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size), vrc7(*this) {
KonamiVRC7(XML::Document &document) : Board(document), vrc7(*this) {
}
};

View File

@@ -45,7 +45,7 @@ void serialize(serializer &s) {
s.integer(mirror_select);
}
NES_AxROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size) {
NES_AxROM(XML::Document &document) : Board(document) {
}
};

View File

@@ -45,7 +45,7 @@ void serialize(serializer &s) {
s.integer(prg_bank);
}
NES_BNROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size) {
NES_BNROM(XML::Document &document) : Board(document) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@@ -47,7 +47,7 @@ void serialize(serializer &s) {
s.integer(chr_bank);
}
NES_CNROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size) {
NES_CNROM(XML::Document &document) : Board(document) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@@ -46,7 +46,7 @@ void serialize(serializer &s) {
mmc5.serialize(s);
}
NES_ExROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size), mmc5(*this) {
NES_ExROM(XML::Document &document) : Board(document), mmc5(*this) {
revision = Revision::ELROM;
}

View File

@@ -84,7 +84,7 @@ void serialize(serializer &s) {
s.array(latch);
}
NES_FxROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size) {
NES_FxROM(XML::Document &document) : Board(document) {
revision = Revision::FKROM;
}

View File

@@ -54,7 +54,7 @@ void serialize(serializer &s) {
s.integer(chr_bank);
}
NES_GxROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size) {
NES_GxROM(XML::Document &document) : Board(document) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@@ -42,7 +42,7 @@ void serialize(serializer &s) {
mmc6.serialize(s);
}
NES_HKROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size), mmc6(*this) {
NES_HKROM(XML::Document &document) : Board(document), mmc6(*this) {
}
};

View File

@@ -36,7 +36,7 @@ void serialize(serializer &s) {
Board::serialize(s);
}
NES_NROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size) {
NES_NROM(XML::Document &document) : Board(document) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@@ -90,7 +90,7 @@ void serialize(serializer &s) {
s.array(latch);
}
NES_PxROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size) {
NES_PxROM(XML::Document &document) : Board(document) {
revision = Revision::PNROM;
}

View File

@@ -94,7 +94,7 @@ void serialize(serializer &s) {
mmc1.serialize(s);
}
NES_SxROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size), mmc1(*this) {
NES_SxROM(XML::Document &document) : Board(document), mmc1(*this) {
revision = Revision::SXROM;
}

View File

@@ -60,7 +60,7 @@ void serialize(serializer &s) {
mmc3.serialize(s);
}
NES_TxROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size), mmc3(*this) {
NES_TxROM(XML::Document &document) : Board(document), mmc3(*this) {
revision = Revision::TLROM;
}

View File

@@ -48,7 +48,7 @@ void serialize(serializer &s) {
s.integer(prg_bank);
}
NES_UxROM(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size) {
NES_UxROM(XML::Document &document) : Board(document) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@@ -220,7 +220,7 @@ void serialize(serializer &s) {
pulse[2].serialize(s);
}
Sunsoft5B(XML::Document &document, const uint8_t *data, unsigned size) : Board(document, data, size) {
Sunsoft5B(XML::Document &document) : Board(document) {
}
};

View File

@@ -1,6 +1,6 @@
#include <nes/nes.hpp>
#include <fc/fc.hpp>
namespace NES {
namespace Famicom {
#include "chip/chip.cpp"
#include "board/board.cpp"
@@ -14,18 +14,23 @@ void Cartridge::main() {
board->main();
}
void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
information.markup = markup;
void Cartridge::load(const string &manifest) {
information.markup = manifest;
if((size & 0xff) == 0) {
sha256 = nall::sha256(data, size);
board = Board::load(markup, data, size);
} else {
sha256 = nall::sha256(data + 16, size - 16);
board = Board::load(markup, data + 16, size - 16);
}
Board::load(manifest); //this call will set Cartridge::board if successful
if(board == nullptr) return;
sha256_ctx sha;
uint8 hash[32];
sha256_init(&sha);
sha256_chunk(&sha, board->prgrom.data, board->prgrom.size);
sha256_chunk(&sha, board->chrrom.data, board->chrrom.size);
sha256_final(&sha);
sha256_hash(&sha, hash);
string result;
for(auto &byte : hash) result.append(hex<2>(byte));
sha256 = result;
system.load();
loaded = true;
}
@@ -33,14 +38,7 @@ void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
void Cartridge::unload() {
if(loaded == false) return;
loaded = false;
}
unsigned Cartridge::ram_size() {
return board->memory().size;
}
uint8* Cartridge::ram_data() {
return board->memory().data;
memory.reset();
}
void Cartridge::power() {

View File

@@ -5,12 +5,9 @@ struct Cartridge : Thread, property<Cartridge> {
static void Main();
void main();
void load(const string &markup, const uint8_t *data, unsigned size);
void load(const string &manifest);
void unload();
unsigned ram_size();
uint8* ram_data();
void power();
void reset();
@@ -21,6 +18,12 @@ struct Cartridge : Thread, property<Cartridge> {
string markup;
} information;
struct Memory {
unsigned id;
string name;
};
vector<Memory> memory;
void serialize(serializer&);
Cartridge();

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

4
bsnes/nes/cheat/cheat.cpp → bsnes/fc/cheat/cheat.cpp Executable file → Normal file
View File

@@ -1,6 +1,6 @@
#include <nes/nes.hpp>
#include <fc/fc.hpp>
namespace NES {
namespace Famicom {
Cheat cheat;

2
bsnes/nes/cheat/cheat.hpp → bsnes/fc/cheat/cheat.hpp Executable file → Normal file
View File

@@ -4,7 +4,7 @@ struct CheatCode {
unsigned comp;
};
struct Cheat : public linear_vector<CheatCode> {
struct Cheat : public vector<CheatCode> {
static bool decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp);
void synchronize();

70
bsnes/nes/cpu/cpu.cpp → bsnes/fc/cpu/cpu.cpp Executable file → Normal file
View File

@@ -1,40 +1,30 @@
#include <nes/nes.hpp>
#include <fc/fc.hpp>
namespace NES {
namespace Famicom {
#include "core/core.cpp"
#include "memory/memory.cpp"
#include "timing.cpp"
#include "serialization.cpp"
CPU cpu;
void CPU::Main() {
cpu.main();
}
void CPU::main() {
//trace = true;
//FILE *fp = fopen("/home/byuu/Desktop/log.txt", "wb");
unsigned lpc = 0xffff;
void CPU::Enter() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(status.interrupt_pending) {
interrupt();
continue;
}
if(trace) {
if(lpc != regs.pc) { print(disassemble(), "\n"); } lpc = regs.pc;
//if(lpc != regs.pc) { fprintf(fp, "%s\n", (const char*)disassemble()); fflush(fp); } lpc = regs.pc;
}
op_exec();
cpu.main();
}
}
void CPU::main() {
if(status.interrupt_pending) {
interrupt();
return;
}
exec();
}
void CPU::add_clocks(unsigned clocks) {
apu.clock -= clocks;
if(apu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(apu.thread);
@@ -47,11 +37,7 @@ void CPU::add_clocks(unsigned clocks) {
}
void CPU::power() {
regs.a = 0x00;
regs.x = 0x00;
regs.y = 0x00;
regs.s = 0x00;
regs.p = 0x04;
R6502::power();
for(unsigned addr = 0; addr < 0x0800; addr++) ram[addr] = 0xff;
ram[0x0008] = 0xf7;
@@ -61,11 +47,8 @@ void CPU::power() {
}
void CPU::reset() {
create(CPU::Main, 21477272);
regs.mdr = 0x00;
regs.s -= 3;
regs.p.i = 1;
R6502::reset();
create(CPU::Enter, 21477272);
regs.pc = bus.read(0xfffc) << 0;
regs.pc |= bus.read(0xfffd) << 8;
@@ -87,16 +70,8 @@ void CPU::reset() {
status.controller_port1 = 0;
}
uint8 CPU::mdr() const {
return regs.mdr;
}
void CPU::set_rdy_line(bool line) {
status.rdy_line = line;
}
void CPU::set_rdy_addr(optional<uint16> addr) {
status.rdy_addr = addr;
uint8 CPU::debugger_read(uint16 addr) {
return bus.read(addr);
}
uint8 CPU::ram_read(uint16 addr) {
@@ -132,11 +107,4 @@ void CPU::write(uint16 addr, uint8 data) {
return apu.write(addr, data);
}
void CPU::oam_dma() {
for(unsigned n = 0; n < 256; n++) {
uint8 data = op_read((status.oam_dma_page << 8) + n);
op_write(0x2004, data);
}
}
}

28
bsnes/nes/cpu/cpu.hpp → bsnes/fc/cpu/cpu.hpp Executable file → Normal file
View File

@@ -1,6 +1,4 @@
struct CPU : Thread {
#include "core/core.hpp"
#include "memory/memory.hpp"
struct CPU : Processor::R6502, Thread {
uint8 ram[0x0800];
struct Status {
@@ -21,16 +19,14 @@ struct CPU : Thread {
unsigned controller_port1;
} status;
static void Main();
static void Enter();
void main();
void add_clocks(unsigned clocks);
void power();
void reset();
uint8 mdr() const;
void set_rdy_line(bool);
void set_rdy_addr(optional<uint16>);
uint8 debugger_read(uint16 addr);
uint8 ram_read(uint16 addr);
void ram_write(uint16 addr, uint8 data);
@@ -38,12 +34,22 @@ struct CPU : Thread {
uint8 read(uint16 addr);
void write(uint16 addr, uint8 data);
void oam_dma();
void serialize(serializer&);
//internal:
bool trace;
//timing.cpp
uint8 op_read(uint16 addr);
void op_write(uint16 addr, uint8 data);
void last_cycle();
void nmi(uint16 &vector);
void oam_dma();
void set_nmi_line(bool);
void set_irq_line(bool);
void set_irq_apu_line(bool);
void set_rdy_line(bool);
void set_rdy_addr(optional<uint16>);
};
extern CPU cpu;

View File

@@ -1,27 +1,9 @@
void CPU::serialize(serializer &s) {
R6502::serialize(s);
Thread::serialize(s);
s.array(ram);
s.integer(regs.mdr);
s.integer(regs.pc);
s.integer(regs.a);
s.integer(regs.x);
s.integer(regs.y);
s.integer(regs.s);
s.integer(regs.p.n);
s.integer(regs.p.v);
s.integer(regs.p.d);
s.integer(regs.p.i);
s.integer(regs.p.z);
s.integer(regs.p.c);
s.integer(abs.w);
s.integer(iabs.w);
s.integer(rd);
s.integer(zp);
s.integer(aa);
s.integer(status.interrupt_pending);
s.integer(status.nmi_pending);
s.integer(status.nmi_line);

63
bsnes/fc/cpu/timing.cpp Normal file
View File

@@ -0,0 +1,63 @@
uint8 CPU::op_read(uint16 addr) {
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 ? status.rdy_addr() : addr);
add_clocks(12);
}
regs.mdr = bus.read(addr);
add_clocks(12);
return regs.mdr;
}
void CPU::op_write(uint16 addr, uint8 data) {
bus.write(addr, regs.mdr = data);
add_clocks(12);
}
void CPU::last_cycle() {
status.interrupt_pending = ((status.irq_line | status.irq_apu_line) & ~regs.p.i) | status.nmi_pending;
}
void CPU::nmi(uint16 &vector) {
if(status.nmi_pending) {
status.nmi_pending = false;
vector = 0xfffa;
}
}
void CPU::oam_dma() {
for(unsigned n = 0; n < 256; n++) {
uint8 data = op_read((status.oam_dma_page << 8) + n);
op_write(0x2004, data);
}
}
void CPU::set_nmi_line(bool line) {
//edge-sensitive (0->1)
if(!status.nmi_line && line) status.nmi_pending = true;
status.nmi_line = line;
}
void CPU::set_irq_line(bool line) {
//level-sensitive
status.irq_line = line;
}
void CPU::set_irq_apu_line(bool line) {
//level-sensitive
status.irq_apu_line = line;
}
void CPU::set_rdy_line(bool line) {
status.rdy_line = line;
}
void CPU::set_rdy_addr(optional<uint16> addr) {
status.rdy_addr = addr;
}

37
bsnes/nes/nes.hpp → bsnes/fc/fc.hpp Executable file → Normal file
View File

@@ -1,17 +1,18 @@
#ifndef NES_HPP
#define NES_HPP
#ifndef FC_HPP
#define FC_HPP
#include <base/base.hpp>
#include <emulator/emulator.hpp>
#include <processor/r6502/r6502.hpp>
namespace NES {
namespace Famicom {
namespace Info {
static const char Name[] = "bnes";
static const unsigned SerializerVersion = 1;
static const unsigned SerializerVersion = 2;
}
}
/*
bnes - NES emulator
bnes - Famicom emulator
authors: byuu, Ryphecha
license: GPLv3
project started: 2011-09-05
@@ -19,7 +20,7 @@ namespace NES {
#include <libco/libco.h>
namespace NES {
namespace Famicom {
struct Thread {
cothread_t thread;
unsigned frequency;
@@ -45,17 +46,17 @@ namespace NES {
}
};
#include <nes/system/system.hpp>
#include <nes/scheduler/scheduler.hpp>
#include <nes/input/input.hpp>
#include <nes/memory/memory.hpp>
#include <nes/cartridge/cartridge.hpp>
#include <nes/cpu/cpu.hpp>
#include <nes/apu/apu.hpp>
#include <nes/ppu/ppu.hpp>
#include <nes/cheat/cheat.hpp>
#include <nes/video/video.hpp>
#include <nes/interface/interface.hpp>
#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

4
bsnes/nes/input/input.cpp → bsnes/fc/input/input.cpp Executable file → Normal file
View File

@@ -1,6 +1,6 @@
#include <nes/nes.hpp>
#include <fc/fc.hpp>
namespace NES {
namespace Famicom {
#include "serialization.cpp"
Input input;

2
bsnes/nes/input/input.hpp → bsnes/fc/input/input.hpp Executable file → Normal file
View File

@@ -1,7 +1,7 @@
struct Input {
enum class Device : unsigned {
None,
Joypad,
None,
};
void latch(bool data);

View File

View File

@@ -0,0 +1,155 @@
#include <fc/fc.hpp>
namespace Famicom {
Interface *interface = nullptr;
double Interface::videoFrequency() {
return 21477272.0 / (262.0 * 1364.0 - 4.0);
}
double Interface::audioFrequency() {
return 21477272.0 / 12.0;
}
bool Interface::loaded() {
return cartridge.loaded();
}
string Interface::sha256() {
return cartridge.sha256();
}
unsigned Interface::group(unsigned id) {
switch(id) {
case ID::ProgramROM:
case ID::ProgramRAM:
case ID::CharacterROM:
case ID::CharacterRAM:
return 1;
}
throw;
}
void Interface::load(unsigned id, const string &manifest) {
cartridge.load(manifest);
}
void Interface::save() {
for(auto &memory : cartridge.memory) {
saveRequest(memory.id, memory.name);
}
}
void Interface::load(unsigned id, const stream &stream, const string &manifest) {
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()));
}
}
void Interface::save(unsigned id, const stream &stream) {
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);
}
}
void Interface::unload() {
save();
cartridge.unload();
}
void Interface::power() {
system.power();
}
void Interface::reset() {
system.reset();
}
void Interface::run() {
system.run();
}
serializer Interface::serialize() {
system.runtosave();
return system.serialize();
}
bool Interface::unserialize(serializer &s) {
return system.unserialize(s);
}
void Interface::cheatSet(const lstring &list) {
cheat.reset();
for(auto &code : list) {
lstring codelist = code.split("+");
for(auto &part : codelist) {
unsigned addr, data, comp;
if(Cheat::decode(part, addr, data, comp)) cheat.append({addr, data, comp});
}
}
cheat.synchronize();
}
void Interface::paletteUpdate() {
video.generate_palette();
}
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"});
{
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);
}
}
}
}
}

View File

@@ -0,0 +1,58 @@
#ifndef FC_HPP
namespace Famicom {
#endif
struct ID {
enum : unsigned {
System,
Famicom,
};
enum : unsigned {
ProgramROM,
ProgramRAM,
CharacterROM,
CharacterRAM,
};
enum : unsigned {
Port1 = 1,
Port2 = 2,
};
};
struct Interface : Emulator::Interface {
double videoFrequency();
double audioFrequency();
bool loaded();
string sha256();
unsigned group(unsigned id);
void load(unsigned id, const string &manifest);
void save();
void load(unsigned id, const stream &stream, const string &manifest = "");
void save(unsigned id, const stream &stream);
void unload();
void power();
void reset();
void run();
serializer serialize();
bool unserialize(serializer&);
void cheatSet(const lstring&);
void paletteUpdate();
Interface();
private:
vector<Device> device;
};
extern Interface *interface;
#ifndef FC_HPP
}
#endif

View File

@@ -1,6 +1,6 @@
#include <nes/nes.hpp>
#include <fc/fc.hpp>
namespace NES {
namespace Famicom {
Bus bus;

View File

9
bsnes/nes/ppu/ppu.cpp → bsnes/fc/ppu/ppu.cpp Executable file → Normal file
View File

@@ -1,6 +1,6 @@
#include <nes/nes.hpp>
#include <fc/fc.hpp>
namespace NES {
namespace Famicom {
#include "serialization.cpp"
PPU ppu;
@@ -47,7 +47,6 @@ void PPU::scanline() {
void PPU::frame() {
status.field ^= 1;
interface->videoRefresh(buffer);
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
@@ -282,7 +281,7 @@ void PPU::scrolly_increment() {
//
void PPU::raster_pixel() {
uint16 *output = buffer + status.ly * 256;
uint32 *output = buffer + status.ly * 256;
unsigned mask = 0x8000 >> (status.xaddr + (status.lx & 7));
unsigned palette = 0, object_palette = 0;
@@ -325,7 +324,7 @@ void PPU::raster_pixel() {
}
if(raster_enable() == false) palette = 0;
output[status.lx] = (status.emphasis << 6) | cgram_read(palette);
output[status.lx] = video.palette[(status.emphasis << 6) | cgram_read(palette)];
}
void PPU::raster_sprite() {

2
bsnes/nes/ppu/ppu.hpp → bsnes/fc/ppu/ppu.hpp Executable file → Normal file
View File

@@ -98,7 +98,7 @@ struct PPU : Thread {
} oam[8], soam[8];
} raster;
uint16 buffer[256 * 262];
uint32 buffer[256 * 262];
uint8 ciram[2048];
uint8 cgram[32];
uint8 oam[256];

View File

View File

@@ -1,6 +1,6 @@
#include <nes/nes.hpp>
#include <fc/fc.hpp>
namespace NES {
namespace Famicom {
Scheduler scheduler;

View File

View File

@@ -1,13 +1,14 @@
serializer System::serialize() {
serializer s(serialize_size);
unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = 0;
char description[512];
unsigned 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.integer(crc32);
s.array(hash);
s.array(description);
serialize_all(s);
@@ -15,17 +16,16 @@ serializer System::serialize() {
}
bool System::unserialize(serializer &s) {
unsigned signature, version, crc32;
char description[512];
unsigned signature, version;
char hash[64], description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(hash);
s.array(description);
if(signature != 0x31545342) return false;
if(version != Info::SerializerVersion) return false;
//if(crc32 != 0) return false;
power();
serialize_all(s);
@@ -47,12 +47,12 @@ void System::serialize_all(serializer &s) {
void System::serialize_init() {
serializer s;
unsigned signature = 0, version = 0, crc32 = 0;
char description[512];
unsigned signature = 0, version = 0;
char hash[64], description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(hash);
s.array(description);
serialize_all(s);

View File

@@ -1,12 +1,15 @@
#include <nes/nes.hpp>
#include <fc/fc.hpp>
namespace NES {
namespace Famicom {
#include "serialization.cpp"
System system;
void System::run() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.buffer, 4 * 256, 256, 240);
}
}
void System::runtosave() {
@@ -32,7 +35,9 @@ void System::runthreadtosave() {
while(true) {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent);
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.buffer, 4 * 256, 256, 240);
}
}
}

View File

58
bsnes/nes/video/video.cpp → bsnes/fc/video/video.cpp Executable file → Normal file
View File

@@ -1,11 +1,23 @@
#include <nes/nes.hpp>
#include <fc/fc.hpp>
#define VIDEO_CPP
namespace NES {
namespace Famicom {
Video video;
unsigned Video::palette30(
void Video::generate_palette() {
for(unsigned n = 0; n < (1 << 9); n++) palette[n] = generate_color(n, 2.0, 0.0, 1.0, 1.0, 1.8);
}
Video::Video() {
palette = new unsigned[1 << 9];
}
Video::~Video() {
delete[] palette;
}
uint32_t Video::generate_color(
unsigned n, double saturation, double hue,
double contrast, double brightness, double gamma
) {
@@ -46,43 +58,11 @@ unsigned Video::palette30(
q *= saturation;
auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); };
unsigned r = 1023.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q);
unsigned g = 1023.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
unsigned b = 1023.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q);
return (uclamp<10>(r) << 20) + (uclamp<10>(g) << 10) + (uclamp<10>(b) << 0);
}
unsigned r = 65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q);
unsigned g = 65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
unsigned b = 65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q);
void Video::generate(Format format) {
for(unsigned n = 0; n < (1 << 9); n++) palette[n] = palette30(n, 2.0, 0.0, 1.0, 1.0, 1.8);
if(format == Format::RGB24) {
for(unsigned n = 0; n < (1 << 9); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff);
}
}
if(format == Format::RGB16) {
for(unsigned n = 0; n < (1 << 9); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f);
}
}
if(format == Format::RGB15) {
for(unsigned n = 0; n < (1 << 9); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f);
}
}
}
Video::Video() {
palette = new unsigned[1 << 9];
}
Video::~Video() {
delete[] palette;
return interface->videoColor(n, uclamp<16>(r), uclamp<16>(g), uclamp<16>(b));
}
}

12
bsnes/fc/video/video.hpp Normal file
View File

@@ -0,0 +1,12 @@
struct Video {
unsigned *palette;
void generate_palette();
Video();
~Video();
private:
uint32_t generate_color(unsigned, double, double, double, double, double);
};
extern Video video;

6
bsnes/gb/Makefile Executable file → Normal file
View File

@@ -1,8 +1,6 @@
options += gameboy
gb_objects := gb-interface gb-system gb-scheduler
gb_objects += gb-memory gb-cartridge
gb_objects += gb-cpu gb-apu gb-lcd
gb_objects += gb-cpu gb-ppu gb-apu
gb_objects += gb-cheat gb-video
objects += $(gb_objects)
@@ -12,7 +10,7 @@ obj/gb-scheduler.o: $(gb)/scheduler/scheduler.cpp $(call rwildcard,$(gb)/schedul
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-lcd.o: $(gb)/lcd/lcd.cpp $(call rwildcard,$(gb)/lcd/)
obj/gb-cheat.o: $(gb)/cheat/cheat.cpp $(call rwildcard,$(gb)/cheat/)
obj/gb-video.o: $(gb)/video/video.cpp $(call rwildcard,$(gb)/video/)

6
bsnes/gb/apu/apu.cpp Executable file → Normal file
View File

@@ -1,7 +1,7 @@
#include <gb/gb.hpp>
#define APU_CPP
namespace GB {
namespace GameBoy {
#include "square1/square1.cpp"
#include "square2/square2.cpp"
@@ -46,9 +46,9 @@ void APU::main() {
noise.run();
master.run();
interface->audioSample(master.center, master.left, master.right);
interface->audioSample(master.left, master.right);
clock += 1 * cpu.frequency;
clock += cpu.frequency;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(scheduler.active_thread = cpu.thread);
}
}

0
bsnes/gb/apu/apu.hpp Executable file → Normal file
View File

0
bsnes/gb/apu/master/master.cpp Executable file → Normal file
View File

0
bsnes/gb/apu/master/master.hpp Executable file → Normal file
View File

0
bsnes/gb/apu/noise/noise.cpp Executable file → Normal file
View File

0
bsnes/gb/apu/noise/noise.hpp Executable file → Normal file
View File

0
bsnes/gb/apu/serialization.cpp Executable file → Normal file
View File

0
bsnes/gb/apu/square1/square1.cpp Executable file → Normal file
View File

0
bsnes/gb/apu/square1/square1.hpp Executable file → Normal file
View File

0
bsnes/gb/apu/square2/square2.cpp Executable file → Normal file
View File

0
bsnes/gb/apu/square2/square2.hpp Executable file → Normal file
View File

0
bsnes/gb/apu/wave/wave.cpp Executable file → Normal file
View File

0
bsnes/gb/apu/wave/wave.hpp Executable file → Normal file
View File

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