Compare commits

...

45 Commits
v083 ... v087

Author SHA1 Message Date
Tim Allen
386ac87d21 Update to v087 release.
byuu says:

This release adds ST018 emulation. As this was the final unsupported
SNES coprocessor, this means that bsnes v087 is the first SNES emulator
to be able to claim 100% known compatibility with all officially
released games. And it does this with absolutely no hacks.

Again, I really have to stress the word known. No emulator is perfect.
No emulator ever really can be perfect for a system of this complexity.
The concept doesn't even really exist, since every SNES behaves subtly
different. What I mean by this, is that every single game ever
officially sold has been tested, and zero bugs (of any severity level)
are currently known.

It is of course extremely likely that bugs will be found in this
release, as well as in future releases. But this will always be
a problem for every emulator ever made: there is no way to test every
possible codepath of every single game to guarantee perfection. I will,
of course, continue to do my best to fix newfound bugs so long as I'm
around.

I'd really like to thank Cydrak and LostTemplar for their assistance in
emulating the ST018. I could not have done it without their help.

The ST018 ROM, like the other coprocessor ROMs, is copyrighted. This
means I am unable to distribute the image.

Changelog (since v086):
- emulated the 21.47MHz ST018 (ARMv3) coprocessor used by Hayazashi
  Nidan Morita Shougi 2
- fixed PPU TM/TS edge case; fixes bottom scanline of text boxes in
  Moryo Senki Madara 2
- fixed saving and loading of Super Game Boy save RAM
- NEC uPD7725,96050 ROMs now stored in little-endian format for
  consistency
- cartridge folder concept has been reworked to use fixed file names
- added emulation of serial USART interface (replaces asynchronous UART
  support previously)
2012-03-08 00:29:38 +11:00
Tim Allen
533aa97011 Update to v086r16 release.
byuu says:

Cydrak, I moved the step from the opcode decoder and opcodes themselves
into bus_(read,write)(byte,word), to minimize code.
If that's not feasible for some reason, please let me know and I'll
change it back to your latest WIP.
This has your carry flag fix, the timer skeleton (doesn't really work
yet), the Booth two-bit steps, and the carry flag clear thing inside
multiply ops.
Also added the aforementioned reset delay and reset bit stuff, and fixed
the steps to 21MHz for instructions and 64KHz for reset pulse.
I wasn't sure about the shifter extra cycles. I only saw it inside one
of the four (or was it three?) opcodes that have shifter functions.
Shouldn't it be in all of them?

The game does indeed appear to be fully playable now, but the AI doesn't
exactly match my real cartridge.
This could be for any number of reasons: ARM CPU bug, timer behavior
bug, oscillator differences between my real hardware and the emulator,
etc.
However ... the AI is 100% predictable every time, both under emulation
and on real hardware.

- For the first step, move 九-1 to 八-1.
- The opponent moves 三-3 to 四-3.
- Now move 七-1 to 六-1.
- The opponent moves 二-2 to 八-8.
  However, on my real SNES, the opponent moves 一-3 to 二-4.
2012-03-08 00:03:15 +11:00
Tim Allen
d118b70b30 Update to v086r15 release.
byuu says:

Most importantly ... I'm now using "st018.rom" which is the program ROM
+ data ROM in one "firmware" file. Since all three Seta DSPs have the
ST01N stamp, unlike some of the arcade variants, I'm just going to go
with ST01N from now on instead of ST-001N. I was using the latter as
that's what Overload called them.

Moving on ...
The memory map should match real hardware now, and I even match the open
bus read results.
I also return the funky 0x40404001 for 60000000-7fffffff, for whatever
that's worth.
The CPU-side registers are also mirrored correctly, as they were in the
last WIP, so we should be good there.
I also simulate the reset pulse now, and a 0->!0 transition of $3804
will destroy the ARM CPU thread.
It will wait until the value is set back to zero to resume execution.
At startup, the ARM CPU will sleep for a while, thus simulating the
reset delay behavior.
Still need to figure out the exact cycle length, but that's really not
important for emulation.

Note in registers.hpp, the |4 in status() is basically what allows the
CPU program to keep going, and hit the checkmate condition.
If we remove that, the CPU deadlocks. Still need to figure out how and
when d4 is set on $3804 reads.
I can run any test program on both real hardware and in my emulator and
compare results, so by all means ... if you can come up with a test,
I'll run it.
2012-03-02 22:07:17 +11:00
Tim Allen
aa8ac7bbb8 Update to v086r14 release.
byuu says:

Attempted to fix the bugs pointed out by Cydrak for the shifter carry
and subtraction flags. No way to know if I was successful.
The memory map should exactly match real hardware now.
Also simplified bus reading/writing: we can get fancy when it works,
I suppose.
Reduced some of the code repetition to try and minimize the chances for
bugs.
I hopefully fixed up register-based ror shifting to what the docs were
saying.
And lastly, the disassembler should handle every opcode in every mode
now.
ldr rn,[pc,n] adds (pc,n) [absolute address] after opcode. I didn't want
to actually read from ROM here (in case it ever touches I/O or
something), but I suppose we could try anyway.
At startup, it will write out "disassembly.txt" which is a disassembly
of the entire program ROM.
If anyone wants to look for disassembly errors, I'll go ahead and fix
them. Just note that I won't do common substitutions like mov pc,lr ==
ret.

At this point, we can make two moves and then the game tells us that
we've won.
So ... I'm back to thinking the problem is with bugs in the ARM core,
and that our bidirectional communication is strong enough to play the
game.
Although that's not perfect. The game definitely looks at d4 (and
possibly others later), but my hardware tests can't get anything but
d0/d3 set.
2012-03-01 23:23:05 +11:00
Tim Allen
ad71e18e02 Update to v086r13 release.
byuu says:

That's my best implementation of the shifter carry. It's horribly
inefficient and possibly wrong (especially on ROR by register, but that
doesn't ever appear to be used in this program), but oh well. It's the
best I can do.

Game is basically getting stuck after a board upload and issuing another
command. It's sitting in a loop waiting on $3804.d0 to be set, meaning
the ARM is never writing anything for the CPU to read. There's some
chance that my $3804/r40000000 flags are wrong. Short of guessing
though, I'm not sure how we can get more info on how those work.

... I really can't debug this any better than I have. If no one else
sees anything, then we're going to have to give up and wait for MESS to
create opcode logs for us to compare against.
2012-02-29 23:59:48 +11:00
Tim Allen
a00c7cb639 Update to v086r12 release.
byuu says:

Attract demonstration game is fully playable.
2012-02-29 23:56:21 +11:00
Tim Allen
112520cf45 Update to v086r11 release.
byuu says:

More ARM work. Can get in-game, and upload the board (0xaa) successfully.
Bug in checkmate command makes the CPU really difficult to defeat :P
2012-02-28 22:21:18 +11:00
Tim Allen
11d6f09359 Update to v086r10 release.
byuu says:

More ARM work. ARM core now begins to act upon initial 0xf1 command, but
hangs.
2012-02-28 22:10:02 +11:00
Tim Allen
3ed42af8a1 Update to v086r09 release.
byuu says:

A lot more work on the ARMv3 core.
2012-02-27 11:18:50 +11:00
Tim Allen
482b4119f6 Update to v086r08 release.
byuu says:

Contains the fledgling beginnings of an ARM CPU core, which can execute
the first three and a half instructions of the ST-0018.
It's a start, I guess.
2012-02-26 18:59:44 +11:00
Tim Allen
f1d6325bcd Update to v086r07 release.
byuu says:

USART improvements. The clock pulse from reading data() drives both
reading and writing.
Also added a usart_init() to bind the initializer functions, so all you
need now is:
extern "C" usartproc void usart_main() { ... }
And inside, you use usart_read(), usart_write(), etc.
So we can add all the new functions we want (eg I'd like to have
usart_readable() to check if data is available) without changing the
entry point signature.

blargg enhanced his Teensy driver to ignore frame error reads, as well.
2012-02-25 20:12:08 +11:00
Tim Allen
e48671694e Update to v086r06 release.
byuu says:

It is done. bsnes can now emulate sending and receiving data via USART.
As such, the UART code has been removed.
The final UART code can be downloaded here: http://byuu.org/snes/uart/
I won't maintain it going forward, because nobody ever used it, and
USART is superior in every way.

I've also verified both sending and receiving on the real SNES now :D
It's so easy ... a caveman with electrical engineering and computer
programming experience can do it.
2012-02-25 20:03:11 +11:00
Tim Allen
338f13e57b Update to v086r05 release.
byuu says:

USART implements reading and writing, but I don't yet have code to test
SNES reading yet.
So ... obviously I need to do that next.

Went ahead and required nall::function, so the modules will have to be
C++11. I don't see anyone else making these, and it avoids the annoyance
of deducing the correct controller port based on dynamic casting the
active thread.
Apparently a library can have a main() function to no ill effect, so
there's no need for USART_HARDWARE. Same exact code with different flags
will make the binary and the library.
2012-02-25 19:52:42 +11:00
Tim Allen
6cbc312f11 Update to v086r04 release.
byuu says:

There will probably be a series of small WIPs as I experiment here.

snes/controller/serial is now snes/controller/uart. Asynchronous serial
communications, typically capped at 57,600 baud.
snes/controller/usart is new. It aims to emulate the SNES connected to
a Teensy++ board, and can easily handle 524,288 baud.
And much more importantly, it's synchronous, so there are no timing
issues anymore. Just bit-bang as fast as you can.

Right now, the USART code is just enough for SNES->PC to transfer data
to ... well, nothing yet.

Unless anyone is actually using the UART stuff, I'll be removing it once
the USART is totally up and running.
No sense maintaining code that is 10x slower, more error prone, and used
by nobody.

Note: this is all thanks to blargg being absolutely amazing.
2012-02-25 19:49:27 +11:00
Tim Allen
7a96321e78 Update to v086r03 release.
byuu says:

Cart unload save path was using the new game rather than the old game.
Caused by trying to allow a failed cartridge load to not unload the
current game.
But that's so uncommon that it's not worth worrying about. It'll always
unload before trying to load a new game now.

Removed the TM/TS disable speedup, to fix Madara 2's text boxes.
This actually did cause a slight performance penalty on games that
disable layers via TM/TS. Zelda 3 inside Link's house is a good example.
It knocked the FPS from 98.5 to 94.5. So to counter that, I removed
conditionals from tiledata loading and decoding, and used fall through
switches.
This boosted us back to 97.0. The -march=native flag apparently works
better with SB now, so that was added, putting us up to 99.0fps.
So it should be the same speed in the worst case, and slightly faster in
the best case.

Bumped the pre-render time to 68 clocks from 60 clocks. Adjusted sprite
tile fetch time from 22 to 14 to compensate.
This should give us perfectly stable Dai Kaijuu Monogatari 2 battles.
2012-02-18 18:49:52 +11:00
Tim Allen
6cfb9e89e7 Update to v086r02 release.
byuu says:

Fixed Super Game Boy RAM saving and loading. It plainly wasn't hooked up
at all. Was apparently hard-coded before it became a multi-emulator.
I also fixed a crashing issue when loading Satellaview-slotted or
Satellaview games without specifying the sub-cart, wasn't setting
has_bsx_slot = true, so the raw memory wasn't being allocated internally
when it wasn't mapped in. Of course a better fix would be to just not
physically map the ranges if the things aren't present. Kind of a lazy
hack to map blank cartridges there, but oh well.  Oh, fixed title
displays as well; and did the best I could for now with regards to
multi-file path saving.
2012-02-16 23:48:05 +11:00
Tim Allen
a37ce1cb2f Update to v086r01 release.
byuu says:

The goals for v087 are to have a unified cartridge-folder concept, as
well as a more functional SNES debugger.

Starting with the cartridge folders. What I have so far:

Code:
NES:
- program.rom
- character.rom
- program.ram
- …

SNES:
- program.rom
- program.rtc
- data.rom (SPC7110)
- { dsp1.rom, dsp1b.rom, cx4.rom, … }
- msu1.rom
- track-#.pcm

Game Boy, Game Boy Color:
- program.rom
- program.ram
- program.rtc

Sub-cartridges (BS-X, Sufami Turbo, …) are stored as separate folders
Folder names must be UTF-8 based, with all-lowercase extensions
File names must be all-lowercase

SNES:
- "program.ram" (.srm, .sts)
- "msu1.rom" (name.msu)
- "track-#.pcm" (name-#.pcm)
- "upd96050.ram" -> "name.ram"
- "bsx.ram" (.bss)
- "bsx.psram" (.bsp)
- "serial.so" -> "libserial.so" (broken)

Need:
- Super Game Boy (not even sure how this loads and saves memory, it's
  obviously broken)

And I need to think of some way of handling multi-cart loaded games.
Eg Satellaview-slotted and Sufami Turbo. It was { base + slot ( + slot
... } }, but this gets trickier with folders and fixed names.
Actual markup for the NES needs to change as well.
2012-02-16 01:01:22 +11:00
Tim Allen
10fd29e7bb Update to v086 release.
byuu says:

The main focus of this release is Laevateinn, which is the new bsnes
debugger. Unlike previous debuggers, Laevateinn is a standalone
application with its own GUI entirely focused on debugging.

Changelog:
- created ui-debugger target (Laevateinn)
- fixed multitap ports 2-4 [quequotion]
- fixed ui-libsnes target compilation
- fixed a crashing issue with NSS XML markup
- improved cartridge-folder loading support
- NES can now load .fc (headerless NES) or .prg+.chr (split NES) images
- fixed cursor being visible in fullscreen mode when using
  Linux/Metacity window manager [ncbncb]
- show normal cursor when using Linux/SDL video driver [ncbncb]
- added menu accelerators
- fixed a bug in performance profile SMP incw/decw instructions
- SNES core can now optionally be built without Game Boy emulation core
- added 2012-02-04 cheats.xml database [mightymo]
2012-02-13 22:44:02 +11:00
Tim Allen
0370229444 Update to v085r09 release.
byuu says:

Added VRAM viewer (mouse over to get tile# and VRAM address), CPU+SMP
register editors, settings.cfg to cache path+sync audio+mute audio
settings (Windows Vista+ ignore my request for the default folder
because they are fucking stupid, so they always default to your home
folder. I'm going to have to recommend using a batch file to start
laevateinn there. Sorry, blame Microsoft for being fuck-ups),
geometry.cfg to remember where you placed windows and what size you made
them (a bug in Qt prevents me from making some windows fixed-size for
now, but that'll change when I can work around the Qt issue), usage map
invalidation if the ROM was modified after the usage files, that empty
line insertion thing creaothceann wanted on emulation resume, all chips
now synchronize immediately rather than just-in-time, which is important
for a debugger.

Going to postpone the properties viewer until after v086.

So this is pretty much ready for release. Please bug-test. I don't care
so much about little frills like "oh the memory editor window should
default to a little bigger", you can work around that by resizing it.
I care about things like, "VRAM write breakpoints don't work at all."

If we miss any bugs and it gets released, not the end of the world, but
you'll be waiting a while for the next release to address any missed
bugs now.
2012-02-12 20:58:04 +11:00
Tim Allen
82afd511fc Update to v085r08 release.
byuu says:

Changelog:
- follow the Laevateinn topic to get most of it
- also added NMI, IRQ step buttons to CPU debugger
- also added trace masking + trace mask reset
- also added memory export
- cartridge loading is entirely folder-based now

FitzRoy, I'll go ahead and make a second compromise with you for v086:
I'll match the following:

    /path/to/SNES.sfc/*.sfc
    /path/to/NES.fc/*.prg, *.chr (split format)
    /path/to/NES.fc/*.fc (merged format)
    /path/to/GB.gb/*.gb
    /path/to/GBC.gbc/*.gbc

Condition will be that there can only be one of each file. If there's
more than one, it'll abort. That lets me name my ROMs as
"Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg,
cartridge.chr". Or whatever you want.
We'll just go with that, see what fares out as the most popular, and
then restrict it back to that method.
The folder must have the .fc, etc extension though. That will be how we
avoid false-positive folder matches.

[Editor's note - the Laevateinn topic mentions these changes for
v085r08:

    Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing,
    memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu,
    WRAM mirroring on breakpoints, protected MMIO memory regions
    (otherwise, viewing $002100 could crash your game.)

    Major missing components:
    - trace mask
    - trace mask clear / usage map clear
    - window geometry caching / sizing improvements
    - VRAM viewer
    - properties viewer
    - working memory export button

    The rest will most likely appear after v086 is released.
]
2012-02-12 16:35:40 +11:00
Tim Allen
ad3eafd735 Update to v085r07 release.
byuu says:

Changelog:
- stuff

[Editor's note - pretty much just more debugger implementation]
2012-02-12 16:07:24 +11:00
Tim Allen
4bc5f66aa5 Update to v085r06 release.
byuu says:

Lots of debugger enhancements. Memory editor works for CPU-bus only,
breakpoint editor does nothing yet.
Tracing works, writes to 001-999 files sequentially. Stepping works,
too. But only on the CPU.
Added "privileged", which becomes "public" if DEBUGGER is defined,
"private" otherwise.
Meant so the debugger can stab deeply into the cores for state
manipulation. Interface is guaranteed to be unstable and dependent upon
the accuracy core.
The about screen logo adds 100KB onto the source download (won't affect
regular bsnes binaries), but too bad. I want some visual flair this
time.
2012-02-09 23:53:55 +11:00
Tim Allen
730e6ae4cc Update to v085r04 release.
byuu says:

Changelog:
- added base/ folder
- base/base.hpp defines the version number for all UI targets, all the
  varint-types, and a hook() class for debugger functions (see below)
- fixed compatibility profile compilation
- removed within<> template from the SNES target
- the SNES core can be built without Game Boy support now, if you so
  choose (my SNES debugger is not going to support debugging the GBZ80,
  sorry.)
- added ui-debugger; not at all useful right now, will be a long while
  to get something usable ready

So hook is a class wrapper around nall::function. It allows you to
invoke potentially empty functions (and as such, the return type must
have a trivial constructor.)
It also doesn't actually perform the test+invocation when DEBUGGER
(options=debugger) is not defined. So you should have no overhead in
regular builds.
The core classes now have a subclass with all the debugging hooks, so
you'll see eg:

    void CPU::op_step() {
      debugger.op_exec(regs.pc);
      (this->*opcode_table[op_read()])();
    }

Clear what it's doing, clear what it's for. A whole lot less work than
inheriting the whole CPU core and virtualizing the functions we want to
hook.
All the logic for what to do inside these callbacks will be handled by
individual debuggers, so they can have all the functionality they want.
2012-02-06 23:03:45 +11:00
Tim Allen
892bb3ab01 Update to v085r03 release.
byuu says:

Changelog:
- fixed cursor being visible under Metacity window manager (hopefully
  doesn't cause regression with other WMs)
- show normal cursor when using SDL video driver
- added menu accelerators (meh, why not?)
- removed debugvirtual, ChipDebugger and chip/debugger functionality
  entirely
- alt/smp disassembler moved up
- fixed alt/smp incw/decw instructions (unsigned->uint16 for internal
  variables)

My plan going forward for a debugger is not to hardcode functionality
that causes the 10-15% slowdown right into the emulator itself.
Instead, I'm going to make a callback class, which will be a specialized
version of nall::function:
- can call function even if not assigned (results in no-op, return type
  must have a trivial default constructor)
- if compiled without #define DEBUGGER, the entire thing turns into
  a huge no-op; and will be eliminated entirely when compiled
- strategically place the functions: cb_step, cb_read, cb_write, etc.

From here, the ui-debugger GUI will bind the callbacks, implement
breakpoint checking, usage table generation, etc itself.
I'll probably have to add some breakout commands to exit the emulation
core prior to a frame event in some cases as well.

I didn't initially want any debugger-related stuff in the base cores,
but the #if debugger sCPUDebugger #else sCPU #endif stuff was already
more of a burden than this will be.
2012-02-04 20:23:53 +11:00
Tim Allen
e4e50308d2 Update to v085r02 release.
byuu says:

Fixed NSS XML crashing issue.
Improved folder-loading support.
NES can now load game.fc/game.fc, or game.fc/game.prg+game.chr.
Both types should have no iNES header at all.
And both types require an XML file (until we have a built-in database.)
2012-01-26 17:50:09 +11:00
Tim Allen
cc518dcc3c Update to v085r01 release.
byuu says:

Changelog:
- updated bsnes to use the newest versions of nall and phoenix
- fixed ui-libsnes compilation (testing would be a good idea, especially
  the cheat codes. I just copy-pasted that from the regular UI.)
- fixed multitap controllers 2-4 [quequotion]
2012-01-15 19:29:57 +11:00
Tim Allen
ba081d309e Update to v085 release.
byuu says:

A new release for the new year.

Changelog:
fixed auto joypad polling edge case; fixes Ys 5 controls
fixed Justifier polling code; Lethal Enforcers should be fully
responsive once again
rewrote SNES S-SMP processor core (~20% code reduction)
fixed Game Boy 8x16 sprite mode; fixed some sprites in Zelda: Link's
Awakening
treat Game Boy HuC1 RAM enable flag as writable flag instead; fixes
Pokemon Card GB
created far faster XML parser; bsnes can now load XML files once again
updated to mightymo's most recent cheat code database
internal color calculations now performed at 30-bits per pixel
gamma slider now acts as fine-tuned gamma ramp option
Linux OpenGL driver will output at 30bpp on capable displays
Linux port defaults to GTK+ now instead of Qt (both are still available)
2012-01-04 00:10:46 +11:00
Tim Allen
1bf9265b7c Update to v084r08 release.
byuu says:

Okay, everything can now load XML again, including board layouts for all
three systems. New is the ability to load external Game Boy layouts (not
really that useful, but it's there.)
I'd like to aim for a v085 release soon. I've included a binary, so I'd
appreciate testing. I had to redo all of the XML mappings for every
system (I like consistency), so basically the following things need to
be tested:
* load one of every type of game for every system (every NES board type,
* every Game Boy MBC type, every SNES chip and layout type.)
* test cheat codes and the cheat database
* test pixel shaders for OpenGL and Direct3D (sepia for the win)
* test anything else for v085 release
2011-12-31 20:24:58 +11:00
Tim Allen
f947d84309 Update to v084r07 release.
byuu says:

Added the new super-fast XML parser. So far, the shaders, cheat files,
and cheat database have been updated to allow XML mode once again. Which
is sure to please Screwtape =)
So it's down to just the cartridge mapping files now, which are always
a major pain.

I still think BML is better for parsing simplicity, memory usage, disk
size, lack of red tape and speed (but horrendously bad for ease of
creating files manually), but since the base API is identical, there's
no reason not to support both. Especially since the pixel shaders have
kind of taken on a life of their own.
2011-12-30 17:41:29 +11:00
Tim Allen
0bd21185b8 Update to v084r06 release.
byuu says:

Changelog:
- fixed sprite tile masking for 8x16 mode (fixes Zelda: DX sprites)
- HuC1 flag sets RAM writable, not RAM enable (fixes Pokemon Card)
- removed within<> template, didn't turn out to be all that useful

I would be almost certain no games would break by allowing reads when it
is disabled, no game would rely on that behavior.
I prefer to be overly restrictive. Better to not allow valid behavior
than to allow invalid behavior. The latter is what gives us a dozen
broken SNES translations.
2011-12-26 21:49:48 +11:00
Tim Allen
6227974bf6 Update to v084r05 release.
(note: before the post announcing this release, there had been
a discussion of a performance optimisation that made the Super Scope
emulation a lot faster, but caused problems for the Justifier perpheral)

byuu says:

Spent a good two hours trying things to no avail.
I was trying to allow the CPU to run ahead, and sync on accesses to
$4016/4017/4201/4213, but that doesn't work because the controllers have
access to strobe IObit at will.
The codebase is really starting to get difficult to work with. I am
guessing because the days of massive development are long over, and the
code is starting to age.
Jonas' fix works 98% of the time, but there's still a few missed shots
here and there. So that's not going to work either.
So ... I give up. I've disabled the speed hack, so that it works 100% of
the time.
Did the same for the Super Scope: it may not have the same problem, but
I like consistency and don't feel like taking the chance.
This doesn't affect the mouse, since the mouse does not latch the
counters to indicate its X/Y position.
Speed hit is 92->82fps (accuracy profile), but only for Super Scope and
Justifier games.
But ... at least it works now. Slow and working is better than fast and
broken.

I appreciate the help in researching the issue, Jonas and krom.

Also pulled in phoenix/Makefile, which simplifies ui/Makefile.
Linux port defaults to GTK+ now. I can't get QGtkStyle to look good on
Debian.
2011-12-18 14:19:45 +11:00
Tim Allen
ea95eaca3c Update to v084r04 release.
byuu says:

Fixed the Ys 5 input bug in the auto joypad polling code. Can't
guarantee it's hardware-accurate (I have no way to extensively test it),
but I can guarantee it is closer to being correct now.
Also uses updated version of phoenix.

The justifier input is indeed all fucked up now. Seems like it stops
updating input after firing for a few frames.
I really don't want to debug that code anymore ... anyone want to make
$10 by fixing it? :P
2011-12-12 21:59:53 +11:00
Tim Allen
ad0805b168 Update to v084r03 release.
(r02 was not posted to the WIP thread)

byuu says:

Internally, all color is processed with 30-bit precision. The filters
also operate at 30-bit depth.
There's a new config file setting, video.depth, which defaults to 24.
This causes the final output to downsample to 24-bit, as most will
require.
If you set it to 30-bit, the downsampling will not occur, and bsnes will
ask ruby for a 30-bit surface. If you don't have one available, you're
going to get bad colors. Or maybe even a crash with OpenGL.
I don't yet have detection code to make sure you have an appropriate
visual in place.

30-bit mode will really only work if you are running Linux, running Xorg
at Depth 30, use the OpenGL or XShm driver, have an nVidia Quadro or AMD
FireGL card with the official drivers, and have a 30-bit capable
monitor.
Lots of planning and work for very little gain here, but it's nice that
it's finally finished.

Oh, I had to change the contrast/brightness formulas a tiny bit, but
they still work and look nice.
2011-12-03 14:22:54 +11:00
Tim Allen
2cc077e12b Update to v084r01 release.
I rewrote the S-SMP processor core (implementation of the 256 opcodes),
utilizing my new 6502-like syntax. It matches what bass v05r01 uses.
Took 10 hours.
Due to being able to group the "mov reg,mem" opcodes together with
"adc/sbc/ora/and/eor/cmp" sets, the total code size was reduced from
55.7KB to 42.5KB for identical accuracy and speed.
I also dropped the trick I was using to pass register variables as
template arguments, and instead just use a switch table to pass them as
function arguments. Makes the table a lot easier to read.

Passes all of my S-SMP tests, and all of blargg's
arithmetic/cycle-timing S-SMP tests. Runs Zelda 3 great as well. Didn't
test further.
This does have the potential to cause some regressions if I've messed
anything up, and none of the above tests caught it, so as always,
testing would be appreciated.

Anyway, yeah. By writing the actual processor with this new mnemonic
set, it confirms the parallels I've made.
My guess is that Sony really did clone the 6502, but was worried about
legal implications or something and changed the mnemonics last-minute.

(Note to self: need to re-enable snes.random before v085 official.)

EDIT: oh yeah, I also commented out the ALSA snd_pcm_drain() inside
term(). Without it, there is a tiny pop when the driver is
re-initialized. But with it, the entire emulator would lock up for five
whole seconds waiting on that call to complete. I'll take the pop any
day over that.
2011-11-17 23:05:35 +11:00
Tim Allen
ae6c3c377d Update to v084 ninja bug-fix.
byuu says:

Hiding the viewport is necessary on Windows to prevent it from
overlapping the status bar. I've changed it to set the size to 1,1 when
nothing is loaded.
That still puts a 1x1 pixel over the status bar when you resize the
window to 1xHeight, but ... you know, don't do that.
Also corrected the mask overscan option for NES/SNES.

Silently updated the bsnes_v084-source.tar.bz2 archive with those fixes,
there were only 48 downloads.
2011-11-08 22:58:50 +11:00
Tim Allen
01750e9c83 Update to v084 release.
byuu says:

This release adds preliminary Game Boy Color emulation. Due to lack of
technical information, this is undoubtedly the least stable module
I provide at this time; but improvements should continue as it is
developed.

This release also polishes the NES emulation and user interface code.

Changelog (since v083):
- added preliminary Game Boy Color emulation
- NES: added MMC6, VRC1, VRC2, VRC3 emulation
- NES: fixed MMC5 banking and added split-screen support [Cydrak]
- NES: pass all of blargg's ppu_vbl_nmi tests, pass more sprite tests
- NES: palette is now generated algorithmically [Bisqwit]
- SNES: fixed SA-1 IRQ regression caused by code refactoring
- Game Boy: rewrote audio channel mixing code; sound output is greatly
  improved as a result
- Game Boy: uses DMG boot ROM instead of SGB boot ROM
- Game Boy: fixed potential bug when loading save states
- phoenix: fixed ListView focus issue [X-Fi6]
- phoenix: fixed dialog message parsing [X-Fi6]
- ui: video output is truly 24-bit now; SNES luma=0 edge case emulated
- ui: audio frequency, latency, resampler are now user configurable
- ui: gamma ramp is dynamically adjustable
- ui: all filters ported to 24-bit mode (speed hit to HQ2x)
- ui: added turbo button mappings for all generic controllers
- ui: fixed audio volume on unmute via menu [Ver Greeneyes]
- ui: shrink window option does nothing when no cartridge is loaded
- ui: re-added compositor disable, driver verification from v082
2011-11-08 00:04:58 +11:00
Tim Allen
891f1ab7af Update to v083r10 release.
byuu says:

Changelog:
- NES: added VRC1, VRC2, VRC3, MMC6 emulation
- shrink window doesn't do anything when no cartridge is loaded
- phoenix Horizontal,VerticalLayout use const Size& instead of unsigned
  width,height [for consistency]

So, all official NES ASICs are supported now. Just need sound output for
MMC5+VRC7 to complete them; and then some board re-arrangement stuff for
VRC2+MMC3.

Note that MMC6 uses the same mapper ID as MMC3, and VRC2 uses the same
ID as VRC4, so you have to make a BML board mapping or toggle which type
is chosen in the source file to use these two chips.

Side note: NES overscan clamping is obviously still assuming 16-bit, as
only half the lines are erased. Need to fix that.
2011-11-04 22:57:54 +11:00
Tim Allen
bf78e66027 Update to v083r09 release.
byuu says:

Added frequency, latency, resampler selection to the audio settings
panel (I really only wanted it there for resampler selection ... having
three options matches the driver selection style though, so whatever.)
The linear/hermite sampler will double the framerate when running Game
Boy games, and sounds the same. Same framerate and sound quality on
SNES. But it will cause buzzing in many NES titles.
Also re-added the composition { never, fullscreen, always } modes.
I think that option is clutter, but it's just impossible to get good
audio+video on Windows 7 without it ...
Lastly, HQ2x was ported over, but not very well. I just convert source
pixels from RGB888 to RGB555, and output pixels in the opposite
direction.
Need someone good to port the diff() and blend functions over to RGB888
in a way that's not terribly slow.
2011-10-31 20:55:48 +11:00
Tim Allen
483f9f8f20 Update to v083r08 release.
byuu says:

Fixed SA-1 IRQ regression for Super Mario RPG
Added turbo B,A to NES+GB; B,A,X,Y to SNES (please don't ask for turbo
L,R; you never use those keys rapidly.)
Re-added video color adjustments, which are now done in full 8-bit
colorspace for more precision

Gamma ramp option is gone. It's now the gamma option, which now only
affects the lower-half of the colors.
A value of 1.0 gives you the original, washed out colors. 1.75 is what
the gamma ramp checkbox used to do (roughly).
The new default is 1.5, which still prevents color washout, but isn't as
overly dark as before.

I wanted to make the core/interface stuff abstract the complexity of
setting up a new C++ class, but it really didn't make anything easier.
It was all one-line stubs to internal functions, and there was just too
many platform-specific things that needed to be captured, so I did away
with that. Made a base class for the ui/interface stuff to get rid of
a lot of switch(mode()) stuff, still a work in progress.
2011-10-29 18:32:20 +11:00
Tim Allen
f3feaa3e86 Update to v083r07 release.
byuu says:

Game Boy: audio should sound a lot better, eg Zelda: DX first opening
scene
Game Boy Color: now uses cothread Processor::frequency to dynamically
clock GB-CPU to 8MHz. Proper OAM DMA and timer speed. Fixes SMT: DC - WB
audio.
Added the break; statements to phoenix/windows/platform message loop
Added audio latency/frequency to config file only
2011-10-28 20:51:43 +11:00
Tim Allen
aaffd000a4 Update to v083r06 release.
byuu says:

All cores: Video classes have internal->{RGB30,24,16,15} palette
generation support
All cores: video output is now RGB24, all filters except HQ2x were
updated to reflect this (HQ2x will be very hard)
NES: MMC5 CHR mapping fixes (Bandit Kings, RTK2, Uchuu Keibitai SDF)
[Cydrak]
NES: MMC5 vertical split screen support (Uchuu Keibitai SDF) [Cydrak]
Game Boy + Game Boy Color: fixed a potential freezing bug when loading
save states (re-create cothreads on state load; was implied when using
SGB mode.)
Game Boy Color: fixed freezing bug with Zelda: LA opening (SVBK is
readable.)
Game Boy Color: more accurate colors (better than GiiBii, probably worse
than KiGB)
SNES: luminance of zero is no longer pure black, as on real hardware.
This is possible thanks to using RGB888 output now.

The current major problems I'd like to solve:
- Zelda: Link's Awakening music when Link first wakes up in the house is
  atrociously bad
- Shin Megami Tensei: Devil Children - White Book (Shiro no Sho) plays
  music at 50% speed; yet Black Book (Kuro no Sho) does not ... one of
  my favorite games, so it'd be great to fix it
2011-10-28 00:30:19 +11:00
Tim Allen
118a393c4c Update to v083r05 release.
(r04 was not posted to the WIP thread)

byuu says:

NES: passes ppu_sprite_overflow tests 01, 02, 05.
Game Boy: uses DMG BIOS (the one with the slow title scroll) or SGB
BIOS, based upon how you load the game.
Game Boy Color: Everything except the IR port is emulated. I don't have
any plans to allow linking two instances of bsnes. And that's frankly
never going to happen over netplay anyway, due to latency requirements
of the serial/IR ports.
The new DMA stuff is possibly incorrect, my test games don't seem to use
it.
Zelda: DX usually resets or crashes on the intro right before the beach
scene. I'm not sure why. Skip the intro and the game plays fine.

This is the best I can do when the most up-to-date GB/C reference
document is over ten years old and half-assed (pandocs.)
I could really use some help from anyone who understands the system.
Probably the worst part of my emulation at the moment is the interrupt
system.
Lots of things real hardware doesn't allow (DMA outside HRAM, CGB DMA to
invalid addresses, etc) isn't blocked yet.
LCD renderer is still scanline-based, which is just terrible. Doesn't
seem to be any good docs on cycle-level operation. I only know that it's
incredibly pathological and variable.
2011-10-27 11:00:17 +11:00
Tim Allen
6b708de893 Update to v083r03 release.
byuu says:

Lots of phoenix issues fixed, especially for Windows and GTK+.
NES emulation passes all ten ppu_vbl_nmi tests from blargg.
Sprite timing is nowhere near accurate yet (always consumes four clocks
per sprite), but oh well.
2011-10-24 22:35:34 +11:00
Tim Allen
db5e2107b4 Update to v083r02 release.
byuu says:

It seems impossible to pass blargg's NES ppu_vbl_nmi test 03 and 07 at
the same time. Wrote up a description of the problem here:
http://nesdev.parodius.com/bbs/viewtopic.php?p=85156#85156
2011-10-18 21:05:29 +11:00
Tim Allen
13ac6104e3 Update to v083r01 release.
byuu says:

This adds Bisqwit's NES palette generation code:

    http://nesdev.parodius.com/bbs/viewtopic.php?p=85060#85060

I set the saturation to 2.0 to closer match the existing "bright"
palette, although it still has a greater contrast range (some colors are
darker.) The gamma ramp option works now. Like SNES, best to also set
gamma to 0.8 afterward.  Once I think of a good way to expose the
saturation/hue settings, I'll do so.

I've also merged in the updated nall. Adds Cygwin uname check, and
replaces linear_vector with vector in lstring and the GUI.
2011-10-16 20:44:48 +11:00
597 changed files with 137131 additions and 89195 deletions

View File

@@ -4,15 +4,14 @@ nes := nes
snes := snes
gameboy := gameboy
profile := accuracy
ui := ui
target := ui
# options += console
# options += debugger
# compiler
c := $(compiler) -std=gnu99
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
flags := -O3 -fomit-frame-pointer -I.
flags := -I. -O3 -fomit-frame-pointer
link :=
objects := libco
@@ -29,8 +28,6 @@ endif
# platform
ifeq ($(platform),x)
# tree vectorization causes code generation errors with Linux/GCC 4.6.1
flags += -fno-tree-vectorize
link += -s -ldl -lX11 -lXext
else ifeq ($(platform),osx)
else ifeq ($(platform),win)
@@ -41,7 +38,7 @@ else
unknown_platform: help;
endif
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
ui := target-$(target)
# implicit rules
compile = \
@@ -61,6 +58,7 @@ all: build;
obj/libco.o: libco/libco.c libco/*
include $(ui)/Makefile
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
# targets
clean:
@@ -76,7 +74,23 @@ clean:
-@$(call delete,*.pdb)
-@$(call delete,*.manifest)
sync:
if [ -d ./libco ]; then rm -r ./libco; fi
if [ -d ./nall ]; then rm -r ./nall; fi
if [ -d ./ruby ]; then rm -r ./ruby; fi
if [ -d ./phoenix ]; then rm -r ./phoenix; fi
cp -r ../libco ./libco
cp -r ../nall ./nall
cp -r ../ruby ./ruby
cp -r ../phoenix ./phoenix
rm -r libco/doc
rm -r libco/test
rm -r nall/test
rm -r ruby/_test
rm -r phoenix/nall
rm -r phoenix/test
archive-all:
tar -cjf bsnes.tar.bz2 data gameboy libco nall nes obj out phoenix ruby snes ui ui-libsnes Makefile cc.bat clean.bat sync.sh
tar -cjf bsnes.tar.bz2 base data gameboy libco nall nes obj out phoenix ruby snes target-debugger target-libsnes target-ui Makefile cc.bat purge.bat
help:;

129
bsnes/base/base.hpp Executable file
View File

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

File diff suppressed because it is too large Load Diff

112663
bsnes/data/cheats.xml Executable file

File diff suppressed because it is too large Load Diff

3812
bsnes/data/laevateinn.hpp Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,9 @@
options += gameboy
gameboy_objects := gameboy-interface gameboy-system gameboy-scheduler
gameboy_objects += gameboy-memory gameboy-cartridge
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
gameboy_objects += gameboy-cheat
gameboy_objects += gameboy-cheat gameboy-video
objects += $(gameboy_objects)
obj/gameboy-interface.o: $(gameboy)/interface/interface.cpp $(call rwildcard,$(gameboy)/interface/)
@@ -13,3 +15,4 @@ obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/)
obj/gameboy-apu.o: $(gameboy)/apu/apu.cpp $(call rwildcard,$(gameboy)/apu/)
obj/gameboy-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/)
obj/gameboy-cheat.o: $(gameboy)/cheat/cheat.cpp $(call rwildcard,$(gameboy)/cheat/)
obj/gameboy-video.o: $(gameboy)/video/video.cpp $(call rwildcard,$(gameboy)/video/)

View File

@@ -47,12 +47,14 @@ void APU::main() {
master.run();
interface->audioSample(master.center, master.left, master.right);
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
clock += 1 * cpu.frequency;
if(clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
}
}
void APU::power() {
create(Main, 4194304);
create(Main, 4 * 1024 * 1024);
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
for(auto &n : mmio_data) n = 0x00;

View File

@@ -1,11 +1,6 @@
#ifdef APU_CPP
void APU::Master::run() {
static int16_t volume[] = {
-16384, -14336, -12288, -10240, -8192, -6144, -4096, -2048,
+2048, +4096, +6144, +8192, +10240, +12288, +14336, +16384,
};
if(enable == false) {
center = 0;
left = 0;
@@ -13,22 +8,19 @@ void APU::Master::run() {
return;
}
signed sample = 0, channels;
signed sample = 0;
sample += apu.square1.output;
sample += apu.square2.output;
sample += apu.wave.output;
sample += apu.noise.output;
sample >>= 2;
center = volume[sample];
center = (sample * 512) - 16384;
sample = 0;
channels = 0;
if(channel1_left_enable) { sample += apu.square1.output; channels++; }
if(channel2_left_enable) { sample += apu.square2.output; channels++; }
if(channel3_left_enable) { sample += apu.wave.output; channels++; }
if(channel4_left_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
left = volume[sample];
if(channel1_left_enable) sample += apu.square1.output;
if(channel2_left_enable) sample += apu.square2.output;
if(channel3_left_enable) sample += apu.wave.output;
if(channel4_left_enable) sample += apu.noise.output;
left = (sample * 512) - 16384;
switch(left_volume) {
case 0: left >>= 3; break; // 12.5%
@@ -42,13 +34,11 @@ void APU::Master::run() {
}
sample = 0;
channels = 0;
if(channel1_right_enable) { sample += apu.square1.output; channels++; }
if(channel2_right_enable) { sample += apu.square2.output; channels++; }
if(channel3_right_enable) { sample += apu.wave.output; channels++; }
if(channel4_right_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
right = volume[sample];
if(channel1_right_enable) sample += apu.square1.output;
if(channel2_right_enable) sample += apu.square2.output;
if(channel3_right_enable) sample += apu.wave.output;
if(channel4_right_enable) sample += apu.noise.output;
right = (sample * 512) - 16384;
switch(right_volume) {
case 0: right >>= 3; break; // 12.5%

View File

@@ -1,6 +1,8 @@
#ifdef APU_CPP
void APU::serialize(serializer &s) {
Processor::serialize(s);
s.array(mmio_data);
s.integer(sequencer_base);
s.integer(sequencer_step);

View File

@@ -16,14 +16,11 @@ namespace GameBoy {
#include "serialization.cpp"
Cartridge cartridge;
void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
void Cartridge::load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size) {
if(size == 0) size = 32768;
romdata = allocate<uint8>(romsize = size, 0xff);
if(data) memcpy(romdata, data, size);
//uint32_t crc = crc32_calculate(data, size);
//print("CRC32 = ", hex<4>(crc), "\n");
info.mapper = Mapper::Unknown;
info.ram = false;
info.battery = false;
@@ -33,9 +30,9 @@ void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
info.romsize = 0;
info.ramsize = 0;
BML::Document document(markup);
XML::Document document(markup);
auto &mapperid = document["cartridge"]["mapper"].value;
auto &mapperid = document["cartridge"]["mapper"].data;
if(mapperid == "none" ) info.mapper = Mapper::MBC0;
if(mapperid == "MBC1" ) info.mapper = Mapper::MBC1;
if(mapperid == "MBC2" ) info.mapper = Mapper::MBC2;
@@ -45,12 +42,12 @@ void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
if(mapperid == "HuC1" ) info.mapper = Mapper::HuC1;
if(mapperid == "HuC3" ) info.mapper = Mapper::HuC3;
info.rtc = document["cartridge"]["rtc"].exists();
info.rumble = document["cartridge"]["rumble"].exists();
info.rtc = document["cartridge"]["rtc"].data == "true";
info.rumble = document["cartridge"]["rumble"].data == "true";
info.romsize = hex(document["cartridge"]["rom"]["size"].value);
info.ramsize = hex(document["cartridge"]["ram"]["size"].value);
info.battery = document["cartridge"]["ram"]["non-volatile"].exists();
info.romsize = numeral(document["cartridge"]["rom"]["size"].data);
info.ramsize = numeral(document["cartridge"]["ram"]["size"].data);
info.battery = document["cartridge"]["ram"]["battery"].data == "true";
switch(info.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
@@ -64,7 +61,7 @@ void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
}
ramdata = new uint8_t[ramsize = info.ramsize]();
system.load();
system.load(revision);
loaded = true;
sha256 = nall::sha256(romdata, romsize);
@@ -101,12 +98,28 @@ void Cartridge::ram_write(unsigned addr, uint8 data) {
}
uint8 Cartridge::mmio_read(uint16 addr) {
if(bootrom_enable && within<0x0000, 0x00ff>(addr)) return System::BootROM::sgb[addr];
if(addr == 0xff50) return 0x00;
if(bootrom_enable) {
const uint8 *data = nullptr;
switch(system.revision()) { default:
case System::Revision::GameBoy: data = System::BootROM::dmg; break;
case System::Revision::SuperGameBoy: data = System::BootROM::sgb; break;
case System::Revision::GameBoyColor: data = System::BootROM::cgb; break;
}
if(addr >= 0x0000 && addr <= 0x00ff) return data[addr];
if(addr >= 0x0200 && addr <= 0x08ff && system.cgb()) return data[addr - 256];
}
return mapper->mmio_read(addr);
}
void Cartridge::mmio_write(uint16 addr, uint8 data) {
if(bootrom_enable && addr == 0xff50) bootrom_enable = false;
if(bootrom_enable && addr == 0xff50) {
bootrom_enable = false;
return;
}
mapper->mmio_write(addr, data);
}

View File

@@ -45,7 +45,7 @@ struct Cartridge : MMIO, property<Cartridge> {
MMIO *mapper;
bool bootrom_enable;
void load(const string &markup, const uint8_t *data, unsigned size);
void load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size);
void unload();
uint8 rom_read(unsigned addr);

View File

@@ -1,53 +1,54 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::HuC1::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
if((addr & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
if((addr & 0xc000) == 0x4000) { //$4000-7fff
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
return 0x00;
if((addr & 0xe000) == 0xa000) { //$a000-bfff
return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
}
return 0x00;
}
void Cartridge::HuC1::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
ram_enable = (data & 0x0f) == 0x0a;
if((addr & 0xe000) == 0x0000) { //$0000-1fff
ram_writable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x3fff>(addr)) {
if((addr & 0xe000) == 0x2000) { //$2000-3fff
rom_select = data;
if(rom_select == 0) rom_select = 1;
return;
}
if(within<0x4000, 0x5fff>(addr)) {
if((addr & 0xe000) == 0x4000) { //$4000-5fff
ram_select = data;
return;
}
if(within<0x6000, 0x7fff>(addr)) {
//unknown purpose
if((addr & 0xe000) == 0x6000) { //$6000-7fff
model = data & 0x01;
return;
}
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
return;
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_writable == false) return;
return cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
}
}
void Cartridge::HuC1::power() {
ram_enable = false;
ram_writable = false;
rom_select = 0x01;
ram_select = 0x00;
model = 0;
}
#endif

View File

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

View File

@@ -1,15 +1,15 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::HuC3::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
if((addr & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
if((addr & 0xc000) == 0x4000) { //$4000-7fff
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(within<0xa000, 0xbfff>(addr)) {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
return 0x00;
}
@@ -18,27 +18,27 @@ uint8 Cartridge::HuC3::mmio_read(uint16 addr) {
}
void Cartridge::HuC3::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
if((addr & 0xe000) == 0x0000) { //$0000-1fff
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x3fff>(addr)) {
if((addr & 0xe000) == 0x2000) { //$2000-3fff
rom_select = data;
return;
}
if(within<0x4000, 0x5fff>(addr)) {
if((addr & 0xe000) == 0x4000) { //$4000-5fff
ram_select = data;
return;
}
if(within<0x6000, 0x7fff>(addr)) {
if((addr & 0xe000) == 0x6000) { //$6000-7fff
//unknown purpose
return;
}
if(within<0xa000, 0xbfff>(addr)) {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
return;
}

View File

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

View File

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

View File

@@ -1,11 +1,11 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC1::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
if((addr & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
if((addr & 0xc000) == 0x4000) { //$4000-7fff
if(mode_select == 0) {
return cartridge.rom_read((ram_select << 19) | (rom_select << 14) | (addr & 0x3fff));
} else {
@@ -13,7 +13,7 @@ uint8 Cartridge::MBC1::mmio_read(uint16 addr) {
}
}
if(within<0xa000, 0xbfff>(addr)) {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_enable) {
if(mode_select == 0) {
return cartridge.ram_read(addr & 0x1fff);
@@ -28,27 +28,27 @@ uint8 Cartridge::MBC1::mmio_read(uint16 addr) {
}
void Cartridge::MBC1::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
if((addr & 0xe000) == 0x0000) { //$0000-1fff
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x3fff>(addr)) {
if((addr & 0xe000) == 0x2000) { //$2000-3fff
rom_select = (data & 0x1f) + ((data & 0x1f) == 0);
return;
}
if(within<0x4000, 0x5fff>(addr)) {
if((addr & 0xe000) == 0x4000) { //$4000-5fff
ram_select = data & 0x03;
return;
}
if(within<0x6000, 0x7fff>(addr)) {
if((addr & 0xe000) == 0x6000) { //$6000-7fff
mode_select = data & 0x01;
return;
}
if(within<0xa000, 0xbfff>(addr)) {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_enable) {
if(mode_select == 0) {
cartridge.ram_write(addr & 0x1fff, data);

View File

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

View File

@@ -1,15 +1,15 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC2::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
if((addr & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
if((addr & 0xc000) == 0x4000) { //$4000-7fff
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(within<0xa000, 0xa1ff>(addr)) {
if((addr & 0xee00) == 0xa000) { //$a000-a1ff
if(ram_enable) return cartridge.ram_read(addr & 0x1ff);
return 0x00;
}
@@ -18,17 +18,17 @@ uint8 Cartridge::MBC2::mmio_read(uint16 addr) {
}
void Cartridge::MBC2::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
if((addr & 0xe000) == 0x0000) { //$0000-1fff
if(!(addr & 0x0100)) ram_enable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x3fff>(addr)) {
if((addr & 0xe000) == 0x2000) { //$2000-3fff
if( (addr & 0x0100)) rom_select = (data & 0x0f) + ((data & 0x0f) == 0);
return;
}
if(within<0xa000, 0xa1ff>(addr)) {
if((addr & 0xee00) == 0xa000) { //$a000-a1ff
if(ram_enable) cartridge.ram_write(addr & 0x1ff, data & 0x0f);
return;
}

View File

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

View File

@@ -19,15 +19,15 @@ void Cartridge::MBC3::second() {
}
uint8 Cartridge::MBC3::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
if((addr & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
if((addr & 0xc000) == 0x4000) { //$4000-7fff
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(within<0xa000, 0xbfff>(addr)) {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_enable) {
if(ram_select >= 0x00 && ram_select <= 0x03) {
return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
@@ -45,22 +45,22 @@ uint8 Cartridge::MBC3::mmio_read(uint16 addr) {
}
void Cartridge::MBC3::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
if((addr & 0xe000) == 0x0000) { //$0000-1fff
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x3fff>(addr)) {
if((addr & 0xe000) == 0x2000) { //$2000-3fff
rom_select = (data & 0x7f) + ((data & 0x7f) == 0);
return;
}
if(within<0x4000, 0x5fff>(addr)) {
if((addr & 0xe000) == 0x4000) { //$4000-5fff
ram_select = data;
return;
}
if(within<0x6000, 0x7fff>(addr)) {
if((addr & 0xe000) == 0x6000) { //$6000-7fff
if(rtc_latch == 0 && data == 1) {
rtc_latch_second = rtc_second;
rtc_latch_minute = rtc_minute;
@@ -72,7 +72,7 @@ void Cartridge::MBC3::mmio_write(uint16 addr, uint8 data) {
return;
}
if(within<0xa000, 0xbfff>(addr)) {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_enable) {
if(ram_select >= 0x00 && ram_select <= 0x03) {
cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);

View File

@@ -1,8 +1,8 @@
struct MBC3 : MMIO {
bool ram_enable; //0000-1fff
uint8 rom_select; //2000-3fff
uint8 ram_select; //4000-5fff
bool rtc_latch; //6000-7fff
bool ram_enable; //$0000-1fff
uint8 rom_select; //$2000-3fff
uint8 ram_select; //$4000-5fff
bool rtc_latch; //$6000-7fff
bool rtc_halt;
unsigned rtc_second;

View File

@@ -1,15 +1,15 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC5::mmio_read(uint16 addr) {
if(within<0x0000, 0x3fff>(addr)) {
if((addr & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.rom_read(addr);
}
if(within<0x4000, 0x7fff>(addr)) {
if((addr & 0xc000) == 0x4000) { //$4000-7fff
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(within<0xa000, 0xbfff>(addr)) {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
return 0x00;
}
@@ -18,27 +18,27 @@ uint8 Cartridge::MBC5::mmio_read(uint16 addr) {
}
void Cartridge::MBC5::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
if((addr & 0xe000) == 0x0000) { //$0000-1fff
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if(within<0x2000, 0x2fff>(addr)) {
if((addr & 0xf000) == 0x2000) { //$2000-2fff
rom_select = (rom_select & 0x0100) | data;
return;
}
if(within<0x3000, 0x3fff>(addr)) {
if((addr & 0xf000) == 0x3000) { //$3000-3fff
rom_select = ((data & 1) << 8) | (rom_select & 0x00ff);
return;
}
if(within<0x4000, 0x5fff>(addr)) {
if((addr & 0xe000) == 0x4000) { //$4000-5fff
ram_select = data & 0x0f;
return;
}
if(within<0xa000, 0xbfff>(addr)) {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
return;
}

View File

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

View File

@@ -1,19 +1,19 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MMM01::mmio_read(uint16 addr) {
if(within<0x0000, 0x7fff>(addr)) {
if((addr & 0x8000) == 0x0000) { //$0000-7fff
if(rom_mode == 0) return cartridge.rom_read(addr);
}
if(within<0x0000, 0x3fff>(addr)) {
if((addr & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.rom_read(0x8000 + (rom_base << 14) + (addr & 0x3fff));
}
if(within<0x4000, 0x7fff>(addr)) {
if((addr & 0xc000) == 0x4000) { //$4000-7fff
return cartridge.rom_read(0x8000 + (rom_base << 14) + (rom_select << 14) + (addr & 0x3fff));
}
if(within<0xa000, 0xbfff>(addr)) {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_enable) return cartridge.ram_read((ram_select << 13) + (addr & 0x1fff));
return 0x00;
}
@@ -22,7 +22,7 @@ uint8 Cartridge::MMM01::mmio_read(uint16 addr) {
}
void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
if(within<0x0000, 0x1fff>(addr)) {
if((addr & 0xe000) == 0x0000) { //$0000-1fff
if(rom_mode == 0) {
rom_mode = 1;
} else {
@@ -30,7 +30,7 @@ void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
}
}
if(within<0x2000, 0x3fff>(addr)) {
if((addr & 0xe000) == 0x2000) { //$2000-3fff
if(rom_mode == 0) {
rom_base = data & 0x3f;
} else {
@@ -38,17 +38,17 @@ void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
}
}
if(within<0x4000, 0x5fff>(addr)) {
if((addr & 0xe000) == 0x4000) { //$4000-5fff
if(rom_mode == 1) {
ram_select = data;
}
}
if(within<0x6000, 0x7fff>(addr)) {
if((addr & 0xe000) == 0x6000) { //$6000-7fff
//unknown purpose
}
if(within<0xa000, 0xbfff>(addr)) {
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram_enable) cartridge.ram_write((ram_select << 13) + (addr & 0x1fff), data);
}
}

View File

@@ -41,9 +41,10 @@ void Cartridge::serialize(serializer &s) {
s.integer(mmm01.rom_select);
s.integer(mmm01.ram_select);
s.integer(huc1.ram_enable);
s.integer(huc1.ram_writable);
s.integer(huc1.rom_select);
s.integer(huc1.ram_select);
s.integer(huc1.model);
s.integer(huc3.ram_enable);
s.integer(huc3.rom_select);

View File

@@ -571,6 +571,13 @@ void CPU::op_halt() {
}
void CPU::op_stop() {
if(status.speed_switch) {
status.speed_switch = 0;
status.speed_double ^= 1;
frequency = 4 * 1024 * 1024;
if(status.speed_double) frequency *= 2;
return;
}
status.stop = true;
while(status.stop == true) op_io();
}

View File

@@ -94,15 +94,43 @@ void CPU::interrupt_exec(uint16 pc) {
}
void CPU::power() {
create(Main, 4194304);
create(Main, 4 * 1024 * 1024);
for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
for(unsigned n = 0xff00; n <= 0xff0f; n++) bus.mmio[n] = this; //MMIO
for(unsigned n = 0xff80; n <= 0xffff; n++) bus.mmio[n] = this; //HRAM+IE
for(unsigned n = 0xff80; n <= 0xfffe; n++) bus.mmio[n] = this; //HRAM
for(unsigned n = 0; n < 8192; n++) wram[n] = 0x00;
for(unsigned n = 0; n < 128; n++) hram[n] = 0x00;
bus.mmio[0xff00] = this; //JOYP
bus.mmio[0xff01] = this; //SB
bus.mmio[0xff02] = this; //SC
bus.mmio[0xff04] = this; //DIV
bus.mmio[0xff05] = this; //TIMA
bus.mmio[0xff06] = this; //TMA
bus.mmio[0xff07] = this; //TAC
bus.mmio[0xff0f] = this; //IF
bus.mmio[0xff46] = this; //DMA
bus.mmio[0xffff] = this; //IE
if(system.cgb()) {
bus.mmio[0xff4d] = this; //KEY1
bus.mmio[0xff51] = this; //HDMA1
bus.mmio[0xff52] = this; //HDMA2
bus.mmio[0xff53] = this; //HDMA3
bus.mmio[0xff54] = this; //HDMA4
bus.mmio[0xff55] = this; //HDMA5
bus.mmio[0xff56] = this; //RP
bus.mmio[0xff6c] = this; //???
bus.mmio[0xff70] = this; //SVBK
bus.mmio[0xff72] = this; //???
bus.mmio[0xff73] = this; //???
bus.mmio[0xff74] = this; //???
bus.mmio[0xff75] = this; //???
bus.mmio[0xff76] = this; //???
bus.mmio[0xff77] = this; //???
}
for(auto &n : wram) n = 0x00;
for(auto &n : hram) n = 0x00;
r[PC] = 0x0000;
r[SP] = 0x0000;
@@ -143,6 +171,23 @@ void CPU::power() {
status.interrupt_request_stat = 0;
status.interrupt_request_vblank = 0;
status.speed_double = 0;
status.speed_switch = 0;
status.dma_source = 0;
status.dma_target = 0;
status.dma_mode = 0;
status.dma_length = 0;
status.ff6c = 0;
status.ff72 = 0;
status.ff73 = 0;
status.ff74 = 0;
status.ff75 = 0;
status.wram_bank = 1;
status.interrupt_enable_joypad = 0;
status.interrupt_enable_serial = 0;
status.interrupt_enable_timer = 0;

View File

@@ -54,6 +54,32 @@ struct CPU : Processor, MMIO {
bool interrupt_request_stat;
bool interrupt_request_vblank;
//$ff4d KEY1
bool speed_double;
bool speed_switch;
//$ff51,$ff52 HDMA1,HDMA2
uint16 dma_source;
//$ff53,$ff54 HDMA3,HDMA4
uint16 dma_target;
//$ff55 HDMA5
bool dma_mode;
uint16 dma_length;
//$ff6c ???
uint8 ff6c;
//$ff70 SVBK
uint3 wram_bank;
//$ff72-$ff75 ???
uint8 ff72;
uint8 ff73;
uint8 ff74;
uint8 ff75;
//$ffff IE
bool interrupt_enable_joypad;
bool interrupt_enable_serial;
@@ -62,7 +88,7 @@ struct CPU : Processor, MMIO {
bool interrupt_enable_vblank;
} status;
uint8 wram[8192];
uint8 wram[32768]; //GB=8192, GBC=32768
uint8 hram[128];
static void Main();

View File

@@ -1,5 +1,12 @@
#ifdef CPU_CPP
unsigned CPU::wram_addr(uint16 addr) const {
addr &= 0x1fff;
if(addr < 0x1000) return addr;
auto bank = status.wram_bank + (status.wram_bank == 0);
return (bank * 0x1000) + (addr & 0x0fff);
}
void CPU::mmio_joyp_poll() {
unsigned button = 0, dpad = 0;
@@ -21,8 +28,7 @@ void CPU::mmio_joyp_poll() {
}
uint8 CPU::mmio_read(uint16 addr) {
if(addr >= 0xc000 && addr <= 0xdfff) return wram[addr & 0x1fff];
if(addr >= 0xe000 && addr <= 0xfdff) return wram[addr & 0x1fff];
if(addr >= 0xc000 && addr <= 0xfdff) return wram[wram_addr(addr)];
if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f];
if(addr == 0xff00) { //JOYP
@@ -65,6 +71,50 @@ uint8 CPU::mmio_read(uint16 addr) {
| (status.interrupt_request_vblank << 0);
}
if(addr == 0xff4d) { //KEY1
return (status.speed_double << 7);
}
if(addr == 0xff55) { //HDMA5
return (status.dma_length / 16) - 1;
}
if(addr == 0xff56) { //RP
return 0x02;
}
if(addr == 0xff6c) { //???
return 0xfe | status.ff6c;
}
if(addr == 0xff70) { //SVBK
return status.wram_bank;
}
if(addr == 0xff72) { //???
return status.ff72;
}
if(addr == 0xff73) { //???
return status.ff73;
}
if(addr == 0xff74) { //???
return status.ff74;
}
if(addr == 0xff75) { //???
return 0x8f | status.ff75;
}
if(addr == 0xff76) { //???
return 0x00;
}
if(addr == 0xff77) { //???
return 0x00;
}
if(addr == 0xffff) { //IE
return (status.interrupt_enable_joypad << 4)
| (status.interrupt_enable_serial << 3)
@@ -77,8 +127,7 @@ uint8 CPU::mmio_read(uint16 addr) {
}
void CPU::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0xc000 && addr <= 0xdfff) { wram[addr & 0x1fff] = data; return; }
if(addr >= 0xe000 && addr <= 0xfdff) { wram[addr & 0x1fff] = data; return; }
if(addr >= 0xc000 && addr <= 0xfdff) { wram[wram_addr(addr)] = data; return; }
if(addr >= 0xff80 && addr <= 0xfffe) { hram[addr & 0x7f] = data; return; }
if(addr == 0xff00) { //JOYP
@@ -131,6 +180,84 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
return;
}
if(addr == 0xff46) { //DMA
for(unsigned n = 0x00; n <= 0x9f; n++) {
bus.write(0xfe00 + n, bus.read((data << 8) + n));
add_clocks(4);
}
return;
}
if(addr == 0xff4d) { //KEY1
status.speed_switch = data & 0x01;
return;
}
if(addr == 0xff51) { //HDMA1
status.dma_source = (status.dma_source & 0x00ff) | (data << 8);
return;
}
if(addr == 0xff52) { //HDMA2
status.dma_source = (status.dma_source & 0xff00) | (data << 0);
return;
}
if(addr == 0xff53) { //HDMA3
status.dma_target = (status.dma_target & 0x00ff) | (data << 8);
return;
}
if(addr == 0xff54) { //HDMA4
status.dma_target = (status.dma_target & 0xff00) | (data << 0);
return;
}
if(addr == 0xff55) { //HDMA5
status.dma_mode = data & 0x80;
status.dma_length = ((data & 0x7f) + 1) * 16;
if(status.dma_mode == 0) do {
bus.write(status.dma_target++, bus.read(status.dma_source++));
add_clocks(4 << status.speed_double);
} while(--status.dma_length);
return;
}
if(addr == 0xff56) { //RP
return;
}
if(addr == 0xff6c) { //???
status.ff6c = data & 0x01;
return;
}
if(addr == 0xff72) { //???
status.ff72 = data;
return;
}
if(addr == 0xff73) { //???
status.ff73 = data;
return;
}
if(addr == 0xff74) { //???
status.ff74 = data;
return;
}
if(addr == 0xff75) { //???
status.ff75 = data & 0x70;
return;
}
if(addr == 0xff70) { //SVBK
status.wram_bank = data & 0x07;
return;
}
if(addr == 0xffff) { //IE
status.interrupt_enable_joypad = data & 0x10;
status.interrupt_enable_serial = data & 0x08;

View File

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

View File

@@ -1,6 +1,8 @@
#ifdef CPU_CPP
void CPU::serialize(serializer &s) {
Processor::serialize(s);
s.array(wram);
s.array(hram);
@@ -47,6 +49,23 @@ void CPU::serialize(serializer &s) {
s.integer(status.interrupt_request_stat);
s.integer(status.interrupt_request_vblank);
s.integer(status.speed_double);
s.integer(status.speed_switch);
s.integer(status.dma_source);
s.integer(status.dma_target);
s.integer(status.dma_mode);
s.integer(status.dma_length);
s.integer(status.ff6c);
s.integer(status.wram_bank);
s.integer(status.ff72);
s.integer(status.ff73);
s.integer(status.ff74);
s.integer(status.ff75);
s.integer(status.interrupt_enable_joypad);
s.integer(status.interrupt_enable_serial);
s.integer(status.interrupt_enable_timer);

View File

@@ -1,5 +1,3 @@
//4194304hz (4 * 1024 * 1024)
//70224 clocks/frame
// 456 clocks/scanline
// 154 scanlines/frame
@@ -10,25 +8,25 @@
void CPU::add_clocks(unsigned clocks) {
system.clocks_executed += clocks;
scheduler.exit(Scheduler::ExitReason::StepEvent);
if(system.sgb()) scheduler.exit(Scheduler::ExitReason::StepEvent);
status.clock += clocks;
if(status.clock >= 4194304) {
status.clock -= 4194304;
if(status.clock >= 4 * 1024 * 1024) {
status.clock -= 4 * 1024 * 1024;
cartridge.mbc3.second();
}
//4194304 / N(hz) - 1 = mask
//4MHz / N(hz) - 1 = mask
if((status.clock & 15) == 0) timer_262144hz();
if((status.clock & 63) == 0) timer_65536hz();
if((status.clock & 255) == 0) timer_16384hz();
if((status.clock & 511) == 0) timer_8192hz();
if((status.clock & 1023) == 0) timer_4096hz();
lcd.clock -= clocks;
lcd.clock -= clocks * lcd.frequency;
if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread);
apu.clock -= clocks;
apu.clock -= clocks * apu.frequency;
if(apu.clock <= 0) co_switch(scheduler.active_thread = apu.thread);
}
@@ -79,4 +77,14 @@ void CPU::timer_4096hz() {
}
}
void CPU::hblank() {
if(status.dma_mode == 1 && status.dma_length) {
for(unsigned n = 0; n < 16; n++) {
bus.write(status.dma_target++, bus.read(status.dma_source++));
add_clocks(4);
}
status.dma_length -= 16;
}
}
#endif

View File

@@ -4,6 +4,7 @@ void timer_65536hz();
void timer_16384hz();
void timer_8192hz();
void timer_4096hz();
void hblank();
//opcode.cpp
void op_io();

View File

@@ -1,93 +1,49 @@
#ifndef GAMEBOY_HPP
#define GAMEBOY_HPP
#include <base/base.hpp>
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static const unsigned SerializerVersion = 2;
static const unsigned SerializerVersion = 3;
}
}
/*
bgameboy - Game Boy emulator
bgameboy - Game Boy, Super Game Boy, and Game Boy Color emulator
author: byuu
license: GPLv3
project started: 2010-12-27
*/
#include <libco/libco.h>
#include <nall/platform.hpp>
#include <nall/property.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/varint.hpp>
using namespace nall;
#include <nall/gameboy/cartridge.hpp>
namespace GameBoy {
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef uint_t< 1> uint1;
typedef uint_t< 2> uint2;
typedef uint_t< 3> uint3;
typedef uint_t< 4> uint4;
typedef uint_t< 5> uint5;
typedef uint_t< 6> uint6;
typedef uint_t< 7> uint7;
typedef uint_t< 9> uint9;
typedef uint_t<10> uint10;
typedef uint_t<11> uint11;
typedef uint_t<12> uint12;
typedef uint_t<13> uint13;
typedef uint_t<14> uint14;
typedef uint_t<15> uint15;
typedef uint_t<17> uint17;
typedef uint_t<18> uint18;
typedef uint_t<19> uint19;
typedef uint_t<20> uint20;
typedef uint_t<21> uint21;
typedef uint_t<22> uint22;
typedef uint_t<23> uint23;
typedef uint_t<24> uint24;
typedef uint_t<25> uint25;
typedef uint_t<26> uint26;
typedef uint_t<27> uint27;
typedef uint_t<28> uint28;
typedef uint_t<29> uint29;
typedef uint_t<30> uint30;
typedef uint_t<31> uint31;
template<uint16 lo, uint16 hi>
alwaysinline bool within(uint16 addr) {
static const uint16 mask = ~(hi ^ lo);
return (addr & mask) == lo;
}
struct Processor {
cothread_t thread;
unsigned frequency;
int64 clock;
inline void create(void (*entrypoint_)(), unsigned frequency_) {
inline void create(void (*entrypoint)(), unsigned frequency) {
if(thread) co_delete(thread);
thread = co_create(65536 * sizeof(void*), entrypoint_);
frequency = frequency_;
thread = co_create(65536 * sizeof(void*), entrypoint);
this->frequency = frequency;
clock = 0;
}
inline Processor() : thread(nullptr) {}
inline void serialize(serializer &s) {
s.integer(frequency);
s.integer(clock);
}
inline Processor() : thread(nullptr) {
}
inline ~Processor() {
if(thread) co_delete(thread);
}
};
#include <gameboy/memory/memory.hpp>
@@ -98,6 +54,7 @@ namespace GameBoy {
#include <gameboy/apu/apu.hpp>
#include <gameboy/lcd/lcd.hpp>
#include <gameboy/cheat/cheat.hpp>
#include <gameboy/video/video.hpp>
};
#endif

View File

@@ -2,7 +2,7 @@
namespace GameBoy {
Interface *interface = 0;
Interface *interface = nullptr;
void Interface::lcdScanline() {
}
@@ -10,7 +10,7 @@ void Interface::lcdScanline() {
void Interface::joypWrite(bool p15, bool p14) {
}
void Interface::videoRefresh(const uint8_t *data) {
void Interface::videoRefresh(const uint16_t *data) {
}
void Interface::audioSample(int16_t center, int16_t left, int16_t right) {
@@ -20,68 +20,6 @@ bool Interface::inputPoll(unsigned id) {
return false;
}
void Interface::initialize(Interface *derived_interface) {
interface = derived_interface;
system.init();
}
bool Interface::cartridgeLoaded() {
return cartridge.loaded();
}
void Interface::loadCartridge(const string &markup, const uint8_t *data, unsigned size) {
cartridge.load(markup, data, size);
system.power();
}
void Interface::unloadCartridge() {
cartridge.unload();
}
unsigned Interface::memorySize(Memory memory) {
if(memory == Memory::RAM) return cartridge.ramsize;
return 0u;
}
uint8_t* Interface::memoryData(Memory memory) {
if(memory == Memory::RAM) return cartridge.ramdata;
return 0u;
}
void Interface::power() {
system.power();
}
void Interface::run() {
do {
system.run();
} while(scheduler.exit_reason() != Scheduler::ExitReason::FrameEvent);
}
serializer Interface::serialize() {
system.runtosave();
return system.serialize();
}
bool Interface::unserialize(serializer &s) {
return system.unserialize(s);
}
void Interface::setCheats(const lstring &list) {
cheat.reset();
for(auto &code : list) {
lstring codelist;
codelist.split("+", code);
for(auto &part : codelist) {
unsigned addr, data, comp;
if(Cheat::decode(part, addr, data, comp)) {
cheat.append({ addr, data, comp });
}
}
}
cheat.synchronize();
}
void Interface::message(const string &text) {
print(text, "\n");
}

View File

@@ -1,33 +1,11 @@
class Interface {
public:
struct Interface {
virtual void lcdScanline();
virtual void joypWrite(bool p15, bool p14);
virtual void videoRefresh(const uint8_t *data);
virtual void videoRefresh(const uint16_t *data);
virtual void audioSample(int16_t center, int16_t left, int16_t right);
virtual bool inputPoll(unsigned id);
virtual void initialize(Interface*);
virtual bool cartridgeLoaded();
virtual void loadCartridge(const string &markup, const uint8_t *data, unsigned size);
virtual void unloadCartridge();
enum class Memory : unsigned {
RAM,
};
virtual unsigned memorySize(Memory);
virtual uint8_t* memoryData(Memory);
virtual void power();
virtual void run();
virtual serializer serialize();
virtual bool unserialize(serializer&);
virtual void setCheats(const lstring &list = lstring{});
virtual void message(const string &text);
};

185
bsnes/gameboy/lcd/cgb.cpp Executable file
View File

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

145
bsnes/gameboy/lcd/dmg.cpp Executable file
View File

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

View File

@@ -1,14 +1,20 @@
#include <gameboy/gameboy.hpp>
//LY = 0-153
//Raster = 0-143
//Vblank = 144-153
//LX = 0-455
#define LCD_CPP
namespace GameBoy {
#include "dmg.cpp"
#include "cgb.cpp"
#include "mmio/mmio.cpp"
#include "serialization.cpp"
LCD lcd;
static unsigned linectr;
void LCD::Main() {
lcd.main();
}
@@ -29,12 +35,13 @@ void LCD::main() {
if(status.display_enable && status.lx == 252) {
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
cpu.hblank();
}
}
}
void LCD::add_clocks(unsigned clocks) {
clock += clocks;
clock += clocks * cpu.frequency;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread);
}
@@ -48,7 +55,9 @@ void LCD::scanline() {
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.ly < 144) render();
if(status.ly < 144) {
system.cgb() == false ? dmg_render() : cgb_render();
}
if(status.display_enable && status.ly == 144) {
cpu.interrupt_raise(CPU::Interrupt::Vblank);
@@ -65,157 +74,50 @@ void LCD::frame() {
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
void LCD::render() {
for(unsigned n = 0; n < 160; n++) {
line[n] = 0x00;
origin[n] = Origin::None;
}
if(status.display_enable == true) {
if(status.bg_enable == true) render_bg();
if(status.window_display_enable == true) render_window();
if(status.obj_enable == true) render_obj();
}
uint8_t *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
interface->lcdScanline();
}
uint16 LCD::read_tile(bool select, unsigned x, unsigned y) {
unsigned tmaddr = 0x1800 + (select << 10), tdaddr;
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
if(status.bg_tiledata_select == 0) {
tdaddr = 0x1000 + ((int8)vram[tmaddr] << 4);
} else {
tdaddr = 0x0000 + (vram[tmaddr] << 4);
}
tdaddr += (y & 7) << 1;
return (vram[tdaddr + 0] << 0) | (vram[tdaddr + 1] << 8);
}
void LCD::render_bg() {
unsigned iy = (status.ly + status.scy) & 255;
unsigned ix = status.scx, tx = ix & 7;
unsigned data = read_tile(status.bg_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
line[ox] = status.bgp[palette];
origin[ox] = Origin::BG;
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = read_tile(status.bg_tilemap_select, ix, iy);
}
}
void LCD::render_window() {
if(status.ly - status.wy >= 144u) return;
if(status.wx >= 167u) return;
unsigned iy = status.wyc++;
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
unsigned data = read_tile(status.window_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(ox - (status.wx - 7) < 160U) {
line[ox] = status.bgp[palette];
origin[ox] = Origin::Window;
}
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = read_tile(status.window_tilemap_select, ix, iy);
}
}
void LCD::render_obj() {
enum : unsigned { Priority = 0x80, YFlip = 0x40, XFlip = 0x20, Palette = 0x10 };
unsigned obj_size = (status.obj_size == 0 ? 8 : 16);
unsigned sprite[10], sprites = 0;
//find first ten sprites on this scanline
for(unsigned s = 0; s < 40; s++) {
unsigned sy = oam[(s << 2) + 0] - 16;
unsigned sx = oam[(s << 2) + 1] - 8;
sy = status.ly - sy;
if(sy >= obj_size) continue;
sprite[sprites++] = s;
if(sprites == 10) break;
}
//sort by X-coordinate, when equal, lower address comes first
for(unsigned x = 0; x < sprites; x++) {
for(unsigned y = x + 1; y < sprites; y++) {
signed sx = oam[(sprite[x] << 2) + 1] - 8;
signed sy = oam[(sprite[y] << 2) + 1] - 8;
if(sy < sx) {
sprite[x] ^= sprite[y];
sprite[y] ^= sprite[x];
sprite[x] ^= sprite[y];
}
}
}
//render backwards, so that first sprite has highest priority
for(signed s = sprites - 1; s >= 0; s--) {
unsigned n = sprite[s] << 2;
unsigned sy = oam[n + 0] - 16;
unsigned sx = oam[n + 1] - 8;
unsigned tile = oam[n + 2];
unsigned attribute = oam[n + 3];
sy = status.ly - sy;
if(sy >= obj_size) continue;
if(attribute & YFlip) sy ^= (obj_size - 1);
unsigned tdaddr = (tile << 4) + (sy << 1);
uint8 d0 = vram[tdaddr + 0];
uint8 d1 = vram[tdaddr + 1];
unsigned xflip = attribute & XFlip ? 7 : 0;
for(unsigned tx = 0; tx < 8; tx++) {
uint8 palette = ((d0 & (0x80 >> tx)) ? 1 : 0)
| ((d1 & (0x80 >> tx)) ? 2 : 0);
if(palette == 0) continue;
palette = status.obp[(bool)(attribute & Palette)][palette];
unsigned ox = sx + (tx ^ xflip);
if(ox <= 159) {
if(attribute & Priority) {
if(origin[ox] == Origin::BG || origin[ox] == Origin::Window) {
if(line[ox] > 0) continue;
}
}
line[ox] = palette;
origin[ox] = Origin::OBJ;
}
}
}
unsigned LCD::hflip(unsigned data) const {
return ((data & 0x8080) >> 7) | ((data & 0x4040) >> 5)
| ((data & 0x2020) >> 3) | ((data & 0x1010) >> 1)
| ((data & 0x0808) << 1) | ((data & 0x0404) << 3)
| ((data & 0x0202) << 5) | ((data & 0x0101) << 7);
}
void LCD::power() {
create(Main, 4194304);
create(Main, 4 * 1024 * 1024);
for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
for(unsigned n = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO
for(unsigned n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM
for(unsigned n = 0; n < 8192; n++) vram[n] = 0x00;
for(unsigned n = 0; n < 160; n++) oam [n] = 0x00;
bus.mmio[0xff40] = this; //LCDC
bus.mmio[0xff41] = this; //STAT
bus.mmio[0xff42] = this; //SCY
bus.mmio[0xff43] = this; //SCX
bus.mmio[0xff44] = this; //LY
bus.mmio[0xff45] = this; //LYC
bus.mmio[0xff47] = this; //BGP
bus.mmio[0xff48] = this; //OBP0
bus.mmio[0xff49] = this; //OBP1
bus.mmio[0xff4a] = this; //WY
bus.mmio[0xff4b] = this; //WX
for(unsigned n = 0; n < 160 * 144; n++) screen[n] = 0x00;
if(system.cgb()) {
bus.mmio[0xff4f] = this; //VBK
bus.mmio[0xff68] = this; //BGPI
bus.mmio[0xff69] = this; //BGPD
bus.mmio[0xff6a] = this; //OBPI
bus.mmio[0xff6b] = this; //OBPD
}
for(auto &n : screen) n = 0x0000;
for(auto &n : line) n = 0x0000;
for(auto &n : origin) n = Origin::None;
for(auto &n : vram) n = 0x00;
for(auto &n : oam) n = 0x00;
for(auto &n : bgp) n = 0x00;
for(auto &n : obp[0]) n = 0x00;
for(auto &n : obp[1]) n = 0x00;
for(auto &n : bgpd) n = 0x0000;
for(auto &n : obpd) n = 0x0000;
status.lx = 0;
status.wyc = 0;
@@ -225,8 +127,8 @@ void LCD::power() {
status.window_display_enable = 0;
status.bg_tiledata_select = 0;
status.bg_tilemap_select = 0;
status.obj_size = 0;
status.obj_enable = 0;
status.ob_size = 0;
status.ob_enable = 0;
status.bg_enable = 0;
status.interrupt_lyc = 0;
@@ -238,15 +140,16 @@ void LCD::power() {
status.scx = 0;
status.ly = 0;
status.lyc = 0;
for(unsigned n = 0; n < 4; n++) {
status.bgp[n] = n;
status.obp[0][n] = n;
status.obp[1][n] = n;
}
status.wy = 0;
status.wx = 0;
status.vram_bank = 0;
status.bgpi_increment = 0;
status.bgpi = 0;
status.obpi_increment = 0;
status.obpi = 0;
}
LCD::LCD() {

View File

@@ -11,8 +11,8 @@ struct LCD : Processor, MMIO {
bool window_display_enable;
bool bg_tiledata_select;
bool bg_tilemap_select;
bool obj_size;
bool obj_enable;
bool ob_size;
bool ob_enable;
bool bg_enable;
//$ff41 STAT
@@ -33,38 +33,57 @@ struct LCD : Processor, MMIO {
//$ff45 LYC
uint8 lyc;
//$ff47 BGP
uint8 bgp[4];
//$ff48 OBP0
//$ff49 OBP1
uint8 obp[2][4];
//$ff4a WY
uint8 wy;
//$ff4b WX
uint8 wx;
//$ff4f VBK
bool vram_bank;
//$ff68 BGPI
bool bgpi_increment;
uint6 bgpi;
//$ff6a OBPI
bool obpi_increment;
uint8 obpi;
} status;
uint8 screen[160 * 144];
uint8 vram[8192];
uint8 oam[160];
uint8 line[160];
struct Origin { enum : unsigned { None, BG, Window, OBJ }; };
uint16 screen[160 * 144];
uint16 line[160];
struct Origin { enum : unsigned { None, BG, BGP, OB }; };
uint8 origin[160];
uint8 vram[16384]; //GB = 8192, GBC = 16384
uint8 oam[160];
uint8 bgp[4];
uint8 obp[2][4];
uint8 bgpd[64];
uint8 obpd[64];
static void Main();
void main();
void add_clocks(unsigned clocks);
void scanline();
void frame();
void render();
uint16 read_tile(bool select, unsigned x, unsigned y);
void render_bg();
void render_window();
void render_obj();
unsigned hflip(unsigned data) const;
//dmg.cpp
void dmg_render();
uint16 dmg_read_tile(bool select, unsigned x, unsigned y);
void dmg_render_bg();
void dmg_render_window();
void dmg_render_ob();
//cgb.cpp
void cgb_render();
void cgb_read_tile(bool select, unsigned x, unsigned y, unsigned &tile, unsigned &attr, unsigned &data);
void cgb_render_bg();
void cgb_render_window();
void cgb_render_ob();
void power();

View File

@@ -1,7 +1,11 @@
#ifdef LCD_CPP
unsigned LCD::vram_addr(uint16 addr) const {
return (status.vram_bank * 0x2000) + (addr & 0x1fff);
}
uint8 LCD::mmio_read(uint16 addr) {
if(addr >= 0x8000 && addr <= 0x9fff) return vram[addr & 0x1fff];
if(addr >= 0x8000 && addr <= 0x9fff) return vram[vram_addr(addr)];
if(addr >= 0xfe00 && addr <= 0xfe9f) return oam[addr & 0xff];
if(addr == 0xff40) { //LCDC
@@ -10,8 +14,8 @@ uint8 LCD::mmio_read(uint16 addr) {
| (status.window_display_enable << 5)
| (status.bg_tiledata_select << 4)
| (status.bg_tilemap_select << 3)
| (status.obj_size << 2)
| (status.obj_enable << 1)
| (status.ob_size << 2)
| (status.ob_enable << 1)
| (status.bg_enable << 0);
}
@@ -47,24 +51,24 @@ uint8 LCD::mmio_read(uint16 addr) {
}
if(addr == 0xff47) { //BGP
return (status.bgp[3] << 6)
| (status.bgp[2] << 4)
| (status.bgp[1] << 2)
| (status.bgp[0] << 0);
return (bgp[3] << 6)
| (bgp[2] << 4)
| (bgp[1] << 2)
| (bgp[0] << 0);
}
if(addr == 0xff48) { //OBP0
return (status.obp[0][3] << 6)
| (status.obp[0][2] << 4)
| (status.obp[0][1] << 2)
| (status.obp[0][0] << 0);
return (obp[0][3] << 6)
| (obp[0][2] << 4)
| (obp[0][1] << 2)
| (obp[0][0] << 0);
}
if(addr == 0xff49) { //OBP1
return (status.obp[1][3] << 6)
| (status.obp[1][2] << 4)
| (status.obp[1][1] << 2)
| (status.obp[1][0] << 0);
return (obp[1][3] << 6)
| (obp[1][2] << 4)
| (obp[1][1] << 2)
| (obp[1][0] << 0);
}
if(addr == 0xff4a) { //WY
@@ -75,11 +79,19 @@ uint8 LCD::mmio_read(uint16 addr) {
return status.wx;
}
if(addr == 0xff69) { //BGPD
return bgpd[status.bgpi];
}
if(addr == 0xff6b) { //OBPD
return obpd[status.obpi];
}
return 0x00;
}
void LCD::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0x8000 && addr <= 0x9fff) { vram[addr & 0x1fff] = data; return; }
if(addr >= 0x8000 && addr <= 0x9fff) { vram[vram_addr(addr)] = data; return; }
if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; }
if(addr == 0xff40) { //LCDC
@@ -92,8 +104,8 @@ void LCD::mmio_write(uint16 addr, uint8 data) {
status.window_display_enable = data & 0x20;
status.bg_tiledata_select = data & 0x10;
status.bg_tilemap_select = data & 0x08;
status.obj_size = data & 0x04;
status.obj_enable = data & 0x02;
status.ob_size = data & 0x04;
status.ob_enable = data & 0x02;
status.bg_enable = data & 0x01;
return;
}
@@ -126,32 +138,27 @@ void LCD::mmio_write(uint16 addr, uint8 data) {
return;
}
if(addr == 0xff46) { //DMA
for(unsigned n = 0x00; n <= 0x9f; n++) bus.write(0xfe00 + n, bus.read((data << 8) + n));
return;
}
if(addr == 0xff47) { //BGP
status.bgp[3] = (data >> 6) & 3;
status.bgp[2] = (data >> 4) & 3;
status.bgp[1] = (data >> 2) & 3;
status.bgp[0] = (data >> 0) & 3;
bgp[3] = (data >> 6) & 3;
bgp[2] = (data >> 4) & 3;
bgp[1] = (data >> 2) & 3;
bgp[0] = (data >> 0) & 3;
return;
}
if(addr == 0xff48) { //OBP0
status.obp[0][3] = (data >> 6) & 3;
status.obp[0][2] = (data >> 4) & 3;
status.obp[0][1] = (data >> 2) & 3;
status.obp[0][0] = (data >> 0) & 3;
obp[0][3] = (data >> 6) & 3;
obp[0][2] = (data >> 4) & 3;
obp[0][1] = (data >> 2) & 3;
obp[0][0] = (data >> 0) & 3;
return;
}
if(addr == 0xff49) { //OBP1
status.obp[1][3] = (data >> 6) & 3;
status.obp[1][2] = (data >> 4) & 3;
status.obp[1][1] = (data >> 2) & 3;
status.obp[1][0] = (data >> 0) & 3;
obp[1][3] = (data >> 6) & 3;
obp[1][2] = (data >> 4) & 3;
obp[1][1] = (data >> 2) & 3;
obp[1][0] = (data >> 0) & 3;
return;
}
@@ -164,6 +171,33 @@ void LCD::mmio_write(uint16 addr, uint8 data) {
status.wx = data;
return;
}
if(addr == 0xff4f) { //VBK
status.vram_bank = data & 1;
return;
}
if(addr == 0xff68) { //BGPI
status.bgpi_increment = data & 0x80;
status.bgpi = data & 0x3f;
return;
}
if(addr == 0xff69) { //BGPD
bgpd[status.bgpi] = data;
if(status.bgpi_increment) status.bgpi++;
return;
}
if(addr == 0xff6a) { //OBPI
status.obpi_increment = data & 0x80;
status.obpi = data & 0x3f;
}
if(addr == 0xff6b) { //OBPD
obpd[status.obpi] = data;
if(status.obpi_increment) status.obpi++;
}
}
#endif

View File

@@ -1,2 +1,3 @@
unsigned vram_addr(uint16 addr) const;
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);

View File

@@ -1,6 +1,20 @@
#ifdef LCD_CPP
void LCD::serialize(serializer &s) {
Processor::serialize(s);
s.array(screen);
s.array(line);
s.array(origin);
s.array(vram);
s.array(oam);
s.array(bgp);
s.array(obp[0]);
s.array(obp[1]);
s.array(bgpd);
s.array(obpd);
s.integer(status.lx);
s.integer(status.wyc);
@@ -9,8 +23,8 @@ void LCD::serialize(serializer &s) {
s.integer(status.window_display_enable);
s.integer(status.bg_tiledata_select);
s.integer(status.bg_tilemap_select);
s.integer(status.obj_size);
s.integer(status.obj_enable);
s.integer(status.ob_size);
s.integer(status.ob_enable);
s.integer(status.bg_enable);
s.integer(status.interrupt_lyc);
@@ -20,21 +34,20 @@ void LCD::serialize(serializer &s) {
s.integer(status.scy);
s.integer(status.scx);
s.integer(status.ly);
s.integer(status.lyc);
s.array(status.bgp);
s.array(status.obp[0]);
s.array(status.obp[1]);
s.integer(status.wy);
s.integer(status.wx);
s.array(screen);
s.array(vram);
s.array(oam);
s.array(line);
s.array(origin);
s.integer(status.vram_bank);
s.integer(status.bgpi_increment);
s.integer(status.bgpi);
s.integer(status.obpi_increment);
s.integer(status.obpi);
}
#endif

View File

@@ -0,0 +1,135 @@
#ifdef SYSTEM_CPP
//SHA256 = 4bf5021be357ce523a59ac5f4efff5d6371ae50112a6db0adf4a75916ad760a9
const uint8_t System::BootROM::cgb[2048] = {
0x31,0xfe,0xff,0x3e,0x02,0xc3,0x7c,0x00,0xd3,0x00,0x98,0xa0,0x12,0xd3,0x00,0x80,
0x00,0x40,0x1e,0x53,0xd0,0x00,0x1f,0x42,0x1c,0x00,0x14,0x2a,0x4d,0x19,0x8c,0x7e,
0x00,0x7c,0x31,0x6e,0x4a,0x45,0x52,0x4a,0x00,0x00,0xff,0x53,0x1f,0x7c,0xff,0x03,
0x1f,0x00,0xff,0x1f,0xa7,0x00,0xef,0x1b,0x1f,0x00,0xef,0x1b,0x00,0x7c,0x00,0x00,
0xff,0x03,0xce,0xed,0x66,0x66,0xcc,0x0d,0x00,0x0b,0x03,0x73,0x00,0x83,0x00,0x0c,
0x00,0x0d,0x00,0x08,0x11,0x1f,0x88,0x89,0x00,0x0e,0xdc,0xcc,0x6e,0xe6,0xdd,0xdd,
0xd9,0x99,0xbb,0xbb,0x67,0x63,0x6e,0x0e,0xec,0xcc,0xdd,0xdc,0x99,0x9f,0xbb,0xb9,
0x33,0x3e,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,0x58,0x43,0xe0,0x70,0x3e,0xfc,
0xe0,0x47,0xcd,0x75,0x02,0xcd,0x00,0x02,0x26,0xd0,0xcd,0x03,0x02,0x21,0x00,0xfe,
0x0e,0xa0,0xaf,0x22,0x0d,0x20,0xfc,0x11,0x04,0x01,0x21,0x10,0x80,0x4c,0x1a,0xe2,
0x0c,0xcd,0xc6,0x03,0xcd,0xc7,0x03,0x13,0x7b,0xfe,0x34,0x20,0xf1,0x11,0x72,0x00,
0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,0xcd,0xf0,0x03,0x3e,0x01,0xe0,0x4f,
0x3e,0x91,0xe0,0x40,0x21,0xb2,0x98,0x06,0x4e,0x0e,0x44,0xcd,0x91,0x02,0xaf,0xe0,
0x4f,0x0e,0x80,0x21,0x42,0x00,0x06,0x18,0xf2,0x0c,0xbe,0x20,0xfe,0x23,0x05,0x20,
0xf7,0x21,0x34,0x01,0x06,0x19,0x78,0x86,0x2c,0x05,0x20,0xfb,0x86,0x20,0xfe,0xcd,
0x1c,0x03,0x18,0x02,0x00,0x00,0xcd,0xd0,0x05,0xaf,0xe0,0x70,0x3e,0x11,0xe0,0x50,
0x21,0x00,0x80,0xaf,0x22,0xcb,0x6c,0x28,0xfb,0xc9,0x2a,0x12,0x13,0x0d,0x20,0xfa,
0xc9,0xe5,0x21,0x0f,0xff,0xcb,0x86,0xcb,0x46,0x28,0xfc,0xe1,0xc9,0x11,0x00,0xff,
0x21,0x03,0xd0,0x0e,0x0f,0x3e,0x30,0x12,0x3e,0x20,0x12,0x1a,0x2f,0xa1,0xcb,0x37,
0x47,0x3e,0x10,0x12,0x1a,0x2f,0xa1,0xb0,0x4f,0x7e,0xa9,0xe6,0xf0,0x47,0x2a,0xa9,
0xa1,0xb0,0x32,0x47,0x79,0x77,0x3e,0x30,0x12,0xc9,0x3e,0x80,0xe0,0x68,0xe0,0x6a,
0x0e,0x6b,0x2a,0xe2,0x05,0x20,0xfb,0x4a,0x09,0x43,0x0e,0x69,0x2a,0xe2,0x05,0x20,
0xfb,0xc9,0xc5,0xd5,0xe5,0x21,0x00,0xd8,0x06,0x01,0x16,0x3f,0x1e,0x40,0xcd,0x4a,
0x02,0xe1,0xd1,0xc1,0xc9,0x3e,0x80,0xe0,0x26,0xe0,0x11,0x3e,0xf3,0xe0,0x12,0xe0,
0x25,0x3e,0x77,0xe0,0x24,0x21,0x30,0xff,0xaf,0x0e,0x10,0x22,0x2f,0x0d,0x20,0xfb,
0xc9,0xcd,0x11,0x02,0xcd,0x62,0x02,0x79,0xfe,0x38,0x20,0x14,0xe5,0xaf,0xe0,0x4f,
0x21,0xa7,0x99,0x3e,0x38,0x22,0x3c,0xfe,0x3f,0x20,0xfa,0x3e,0x01,0xe0,0x4f,0xe1,
0xc5,0xe5,0x21,0x43,0x01,0xcb,0x7e,0xcc,0x89,0x05,0xe1,0xc1,0xcd,0x11,0x02,0x79,
0xd6,0x30,0xd2,0x06,0x03,0x79,0xfe,0x01,0xca,0x06,0x03,0x7d,0xfe,0xd1,0x28,0x21,
0xc5,0x06,0x03,0x0e,0x01,0x16,0x03,0x7e,0xe6,0xf8,0xb1,0x22,0x15,0x20,0xf8,0x0c,
0x79,0xfe,0x06,0x20,0xf0,0x11,0x11,0x00,0x19,0x05,0x20,0xe7,0x11,0xa1,0xff,0x19,
0xc1,0x04,0x78,0x1e,0x83,0xfe,0x62,0x28,0x06,0x1e,0xc1,0xfe,0x64,0x20,0x07,0x7b,
0xe0,0x13,0x3e,0x87,0xe0,0x14,0xfa,0x02,0xd0,0xfe,0x00,0x28,0x0a,0x3d,0xea,0x02,
0xd0,0x79,0xfe,0x01,0xca,0x91,0x02,0x0d,0xc2,0x91,0x02,0xc9,0x0e,0x26,0xcd,0x4a,
0x03,0xcd,0x11,0x02,0xcd,0x62,0x02,0x0d,0x20,0xf4,0xcd,0x11,0x02,0x3e,0x01,0xe0,
0x4f,0xcd,0x3e,0x03,0xcd,0x41,0x03,0xaf,0xe0,0x4f,0xcd,0x3e,0x03,0xc9,0x21,0x08,
0x00,0x11,0x51,0xff,0x0e,0x05,0xcd,0x0a,0x02,0xc9,0xc5,0xd5,0xe5,0x21,0x40,0xd8,
0x0e,0x20,0x7e,0xe6,0x1f,0xfe,0x1f,0x28,0x01,0x3c,0x57,0x2a,0x07,0x07,0x07,0xe6,
0x07,0x47,0x3a,0x07,0x07,0x07,0xe6,0x18,0xb0,0xfe,0x1f,0x28,0x01,0x3c,0x0f,0x0f,
0x0f,0x47,0xe6,0xe0,0xb2,0x22,0x78,0xe6,0x03,0x5f,0x7e,0x0f,0x0f,0xe6,0x1f,0xfe,
0x1f,0x28,0x01,0x3c,0x07,0x07,0xb3,0x22,0x0d,0x20,0xc7,0xe1,0xd1,0xc1,0xc9,0x0e,
0x00,0x1a,0xe6,0xf0,0xcb,0x49,0x28,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x1a,
0xe6,0x0f,0xcb,0x49,0x20,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x13,0xcb,0x41,
0x28,0x0d,0xd5,0x11,0xf8,0xff,0xcb,0x49,0x28,0x03,0x11,0x08,0x00,0x19,0xd1,0x0c,
0x79,0xfe,0x18,0x20,0xcc,0xc9,0x47,0xd5,0x16,0x04,0x58,0xcb,0x10,0x17,0xcb,0x13,
0x17,0x15,0x20,0xf6,0xd1,0x22,0x23,0x22,0x23,0xc9,0x3e,0x19,0xea,0x10,0x99,0x21,
0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,0xf9,0x2e,0x0f,0x18,0xf3,0xc9,
0x3e,0x01,0xe0,0x4f,0xcd,0x00,0x02,0x11,0x07,0x06,0x21,0x80,0x80,0x0e,0xc0,0x1a,
0x22,0x23,0x22,0x23,0x13,0x0d,0x20,0xf7,0x11,0x04,0x01,0xcd,0x8f,0x03,0x01,0xa8,
0xff,0x09,0xcd,0x8f,0x03,0x01,0xf8,0xff,0x09,0x11,0x72,0x00,0x0e,0x08,0x23,0x1a,
0x22,0x13,0x0d,0x20,0xf9,0x21,0xc2,0x98,0x06,0x08,0x3e,0x08,0x0e,0x10,0x22,0x0d,
0x20,0xfc,0x11,0x10,0x00,0x19,0x05,0x20,0xf3,0xaf,0xe0,0x4f,0x21,0xc2,0x98,0x3e,
0x08,0x22,0x3c,0xfe,0x18,0x20,0x02,0x2e,0xe2,0xfe,0x28,0x20,0x03,0x21,0x02,0x99,
0xfe,0x38,0x20,0xed,0x21,0xd8,0x08,0x11,0x40,0xd8,0x06,0x08,0x3e,0xff,0x12,0x13,
0x12,0x13,0x0e,0x02,0xcd,0x0a,0x02,0x3e,0x00,0x12,0x13,0x12,0x13,0x13,0x13,0x05,
0x20,0xea,0xcd,0x62,0x02,0x21,0x4b,0x01,0x7e,0xfe,0x33,0x20,0x0b,0x2e,0x44,0x1e,
0x30,0x2a,0xbb,0x20,0x49,0x1c,0x18,0x04,0x2e,0x4b,0x1e,0x01,0x2a,0xbb,0x20,0x3e,
0x2e,0x34,0x01,0x10,0x00,0x2a,0x80,0x47,0x0d,0x20,0xfa,0xea,0x00,0xd0,0x21,0xc7,
0x06,0x0e,0x00,0x2a,0xb8,0x28,0x08,0x0c,0x79,0xfe,0x4f,0x20,0xf6,0x18,0x1f,0x79,
0xd6,0x41,0x38,0x1c,0x21,0x16,0x07,0x16,0x00,0x5f,0x19,0xfa,0x37,0x01,0x57,0x7e,
0xba,0x28,0x0d,0x11,0x0e,0x00,0x19,0x79,0x83,0x4f,0xd6,0x5e,0x38,0xed,0x0e,0x00,
0x21,0x33,0x07,0x06,0x00,0x09,0x7e,0xe6,0x1f,0xea,0x08,0xd0,0x7e,0xe6,0xe0,0x07,
0x07,0x07,0xea,0x0b,0xd0,0xcd,0xe9,0x04,0xc9,0x11,0x91,0x07,0x21,0x00,0xd9,0xfa,
0x0b,0xd0,0x47,0x0e,0x1e,0xcb,0x40,0x20,0x02,0x13,0x13,0x1a,0x22,0x20,0x02,0x1b,
0x1b,0xcb,0x48,0x20,0x02,0x13,0x13,0x1a,0x22,0x13,0x13,0x20,0x02,0x1b,0x1b,0xcb,
0x50,0x28,0x05,0x1b,0x2b,0x1a,0x22,0x13,0x1a,0x22,0x13,0x0d,0x20,0xd7,0x21,0x00,
0xd9,0x11,0x00,0xda,0xcd,0x64,0x05,0xc9,0x21,0x12,0x00,0xfa,0x05,0xd0,0x07,0x07,
0x06,0x00,0x4f,0x09,0x11,0x40,0xd8,0x06,0x08,0xe5,0x0e,0x02,0xcd,0x0a,0x02,0x13,
0x13,0x13,0x13,0x13,0x13,0xe1,0x05,0x20,0xf0,0x11,0x42,0xd8,0x0e,0x02,0xcd,0x0a,
0x02,0x11,0x4a,0xd8,0x0e,0x02,0xcd,0x0a,0x02,0x2b,0x2b,0x11,0x44,0xd8,0x0e,0x02,
0xcd,0x0a,0x02,0xc9,0x0e,0x60,0x2a,0xe5,0xc5,0x21,0xe8,0x07,0x06,0x00,0x4f,0x09,
0x0e,0x08,0xcd,0x0a,0x02,0xc1,0xe1,0x0d,0x20,0xec,0xc9,0xfa,0x08,0xd0,0x11,0x18,
0x00,0x3c,0x3d,0x28,0x03,0x19,0x20,0xfa,0xc9,0xcd,0x1d,0x02,0x78,0xe6,0xff,0x28,
0x0f,0x21,0xe4,0x08,0x06,0x00,0x2a,0xb9,0x28,0x08,0x04,0x78,0xfe,0x0c,0x20,0xf6,
0x18,0x2d,0x78,0xea,0x05,0xd0,0x3e,0x1e,0xea,0x02,0xd0,0x11,0x0b,0x00,0x19,0x56,
0x7a,0xe6,0x1f,0x5f,0x21,0x08,0xd0,0x3a,0x22,0x7b,0x77,0x7a,0xe6,0xe0,0x07,0x07,
0x07,0x5f,0x21,0x0b,0xd0,0x3a,0x22,0x7b,0x77,0xcd,0xe9,0x04,0xcd,0x28,0x05,0xc9,
0xcd,0x11,0x02,0xfa,0x43,0x01,0xcb,0x7f,0x28,0x04,0xe0,0x4c,0x18,0x28,0x3e,0x04,
0xe0,0x4c,0x3e,0x01,0xe0,0x6c,0x21,0x00,0xda,0xcd,0x7b,0x05,0x06,0x10,0x16,0x00,
0x1e,0x08,0xcd,0x4a,0x02,0x21,0x7a,0x00,0xfa,0x00,0xd0,0x47,0x0e,0x02,0x2a,0xb8,
0xcc,0xda,0x03,0x0d,0x20,0xf8,0xc9,0x01,0x0f,0x3f,0x7e,0xff,0xff,0xc0,0x00,0xc0,
0xf0,0xf1,0x03,0x7c,0xfc,0xfe,0xfe,0x03,0x07,0x07,0x0f,0xe0,0xe0,0xf0,0xf0,0x1e,
0x3e,0x7e,0xfe,0x0f,0x0f,0x1f,0x1f,0xff,0xff,0x00,0x00,0x01,0x01,0x01,0x03,0xff,
0xff,0xe1,0xe0,0xc0,0xf0,0xf9,0xfb,0x1f,0x7f,0xf8,0xe0,0xf3,0xfd,0x3e,0x1e,0xe0,
0xf0,0xf9,0x7f,0x3e,0x7c,0xf8,0xe0,0xf8,0xf0,0xf0,0xf8,0x00,0x00,0x7f,0x7f,0x07,
0x0f,0x9f,0xbf,0x9e,0x1f,0xff,0xff,0x0f,0x1e,0x3e,0x3c,0xf1,0xfb,0x7f,0x7f,0xfe,
0xde,0xdf,0x9f,0x1f,0x3f,0x3e,0x3c,0xf8,0xf8,0x00,0x00,0x03,0x03,0x07,0x07,0xff,
0xff,0xc1,0xc0,0xf3,0xe7,0xf7,0xf3,0xc0,0xc0,0xc0,0xc0,0x1f,0x1f,0x1e,0x3e,0x3f,
0x1f,0x3e,0x3e,0x80,0x00,0x00,0x00,0x7c,0x1f,0x07,0x00,0x0f,0xff,0xfe,0x00,0x7c,
0xf8,0xf0,0x00,0x1f,0x0f,0x0f,0x00,0x7c,0xf8,0xf8,0x00,0x3f,0x3e,0x1c,0x00,0x0f,
0x0f,0x0f,0x00,0x7c,0xff,0xff,0x00,0x00,0xf8,0xf8,0x00,0x07,0x0f,0x0f,0x00,0x81,
0xff,0xff,0x00,0xf3,0xe1,0x80,0x00,0xe0,0xff,0x7f,0x00,0xfc,0xf0,0xc0,0x00,0x3e,
0x7c,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x16,0x36,0xd1,0xdb,0xf2,0x3c,0x8c,
0x92,0x3d,0x5c,0x58,0xc9,0x3e,0x70,0x1d,0x59,0x69,0x19,0x35,0xa8,0x14,0xaa,0x75,
0x95,0x99,0x34,0x6f,0x15,0xff,0x97,0x4b,0x90,0x17,0x10,0x39,0xf7,0xf6,0xa2,0x49,
0x4e,0x43,0x68,0xe0,0x8b,0xf0,0xce,0x0c,0x29,0xe8,0xb7,0x86,0x9a,0x52,0x01,0x9d,
0x71,0x9c,0xbd,0x5d,0x6d,0x67,0x3f,0x6b,0xb3,0x46,0x28,0xa5,0xc6,0xd3,0x27,0x61,
0x18,0x66,0x6a,0xbf,0x0d,0xf4,0x42,0x45,0x46,0x41,0x41,0x52,0x42,0x45,0x4b,0x45,
0x4b,0x20,0x52,0x2d,0x55,0x52,0x41,0x52,0x20,0x49,0x4e,0x41,0x49,0x4c,0x49,0x43,
0x45,0x20,0x52,0x7c,0x08,0x12,0xa3,0xa2,0x07,0x87,0x4b,0x20,0x12,0x65,0xa8,0x16,
0xa9,0x86,0xb1,0x68,0xa0,0x87,0x66,0x12,0xa1,0x30,0x3c,0x12,0x85,0x12,0x64,0x1b,
0x07,0x06,0x6f,0x6e,0x6e,0xae,0xaf,0x6f,0xb2,0xaf,0xb2,0xa8,0xab,0x6f,0xaf,0x86,
0xae,0xa2,0xa2,0x12,0xaf,0x13,0x12,0xa1,0x6e,0xaf,0xaf,0xad,0x06,0x4c,0x6e,0xaf,
0xaf,0x12,0x7c,0xac,0xa8,0x6a,0x6e,0x13,0xa0,0x2d,0xa8,0x2b,0xac,0x64,0xac,0x6d,
0x87,0xbc,0x60,0xb4,0x13,0x72,0x7c,0xb5,0xae,0xae,0x7c,0x7c,0x65,0xa2,0x6c,0x64,
0x85,0x80,0xb0,0x40,0x88,0x20,0x68,0xde,0x00,0x70,0xde,0x20,0x78,0x20,0x20,0x38,
0x20,0xb0,0x90,0x20,0xb0,0xa0,0xe0,0xb0,0xc0,0x98,0xb6,0x48,0x80,0xe0,0x50,0x1e,
0x1e,0x58,0x20,0xb8,0xe0,0x88,0xb0,0x10,0x20,0x00,0x10,0x20,0xe0,0x18,0xe0,0x18,
0x00,0x18,0xe0,0x20,0xa8,0xe0,0x20,0x18,0xe0,0x00,0x20,0x18,0xd8,0xc8,0x18,0xe0,
0x00,0xe0,0x40,0x28,0x28,0x28,0x18,0xe0,0x60,0x20,0x18,0xe0,0x00,0x00,0x08,0xe0,
0x18,0x30,0xd0,0xd0,0xd0,0x20,0xe0,0xe8,0xff,0x7f,0xbf,0x32,0xd0,0x00,0x00,0x00,
0x9f,0x63,0x79,0x42,0xb0,0x15,0xcb,0x04,0xff,0x7f,0x31,0x6e,0x4a,0x45,0x00,0x00,
0xff,0x7f,0xef,0x1b,0x00,0x02,0x00,0x00,0xff,0x7f,0x1f,0x42,0xf2,0x1c,0x00,0x00,
0xff,0x7f,0x94,0x52,0x4a,0x29,0x00,0x00,0xff,0x7f,0xff,0x03,0x2f,0x01,0x00,0x00,
0xff,0x7f,0xef,0x03,0xd6,0x01,0x00,0x00,0xff,0x7f,0xb5,0x42,0xc8,0x3d,0x00,0x00,
0x74,0x7e,0xff,0x03,0x80,0x01,0x00,0x00,0xff,0x67,0xac,0x77,0x13,0x1a,0x6b,0x2d,
0xd6,0x7e,0xff,0x4b,0x75,0x21,0x00,0x00,0xff,0x53,0x5f,0x4a,0x52,0x7e,0x00,0x00,
0xff,0x4f,0xd2,0x7e,0x4c,0x3a,0xe0,0x1c,0xed,0x03,0xff,0x7f,0x5f,0x25,0x00,0x00,
0x6a,0x03,0x1f,0x02,0xff,0x03,0xff,0x7f,0xff,0x7f,0xdf,0x01,0x12,0x01,0x00,0x00,
0x1f,0x23,0x5f,0x03,0xf2,0x00,0x09,0x00,0xff,0x7f,0xea,0x03,0x1f,0x01,0x00,0x00,
0x9f,0x29,0x1a,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x7f,0x02,0x1f,0x00,0x00,0x00,
0xff,0x7f,0xe0,0x03,0x06,0x02,0x20,0x01,0xff,0x7f,0xeb,0x7e,0x1f,0x00,0x00,0x7c,
0xff,0x7f,0xff,0x3f,0x00,0x7e,0x1f,0x00,0xff,0x7f,0xff,0x03,0x1f,0x00,0x00,0x00,
0xff,0x03,0x1f,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x3f,0x03,0x93,0x01,0x00,0x00,
0x00,0x00,0x00,0x42,0x7f,0x03,0xff,0x7f,0xff,0x7f,0x8c,0x7e,0x00,0x7c,0x00,0x00,
0xff,0x7f,0xef,0x1b,0x80,0x61,0x00,0x00,0xff,0x7f,0x00,0x7c,0xe0,0x03,0x1f,0x7c,
0x1f,0x00,0xff,0x03,0x40,0x41,0x42,0x20,0x21,0x22,0x80,0x81,0x82,0x10,0x11,0x12,
0x12,0xb0,0x79,0xb8,0xad,0x16,0x17,0x07,0xba,0x05,0x7c,0x13,0x00,0x00,0x00,0x00,
};
#endif

View File

@@ -1,6 +1,6 @@
#ifdef SYSTEM_CPP
//MD5SUM = 32fbbd84168d3482956eb3c5051637f5
//SHA256 = cf053eccb4ccafff9e67339d4e78e98dce7d1ed59be819d2a1ba2232c6fce1c7
const uint8_t System::BootROM::dmg[256] = {
0x31,0xfe,0xff,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,0x21,0x26,0xff,0x0e,
0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,0x77,0x3e,0xfc,0xe0,

View File

@@ -1,6 +1,6 @@
#ifdef SYSTEM_CPP
//MD5SUM = d574d4f9c12f305074798f54c091a8b4
//SHA256 = 0e4ddff32fc9d1eeaae812a157dd246459b00c9e14f2f61751f661f32361e360
const uint8_t System::BootROM::sgb[256] = {
0x31,0xfe,0xff,0x3e,0x30,0xe0,0x00,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,
0x21,0x26,0xff,0x0e,0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,

View File

@@ -29,6 +29,7 @@ bool System::unserialize(serializer &s) {
if(version != Info::SerializerVersion) return false;
//if(crc32 != 0) return false;
power();
serialize_all(s);
return true;
}

View File

@@ -5,6 +5,7 @@ namespace GameBoy {
#include "bootrom-dmg.cpp"
#include "bootrom-sgb.cpp"
#include "bootrom-cgb.cpp"
#include "serialization.cpp"
System system;
@@ -37,7 +38,8 @@ void System::init() {
assert(interface != 0);
}
void System::load() {
void System::load(Revision revision) {
this->revision = revision;
serialize_init();
}

View File

@@ -4,10 +4,21 @@ enum class Input : unsigned {
Up, Down, Left, Right, B, A, Select, Start,
};
struct System {
struct System : property<System> {
enum class Revision : unsigned {
GameBoy,
SuperGameBoy,
GameBoyColor,
};
readonly<Revision> revision;
inline bool dmg() const { return revision == Revision::GameBoy; }
inline bool sgb() const { return revision == Revision::SuperGameBoy; }
inline bool cgb() const { return revision == Revision::GameBoyColor; }
struct BootROM {
static const uint8 dmg[256];
static const uint8 sgb[256];
static const uint8 dmg[ 256];
static const uint8 sgb[ 256];
static const uint8 cgb[2048];
} bootROM;
void run();
@@ -15,7 +26,7 @@ struct System {
void runthreadtosave();
void init();
void load();
void load(Revision);
void power();
unsigned clocks_executed;

82
bsnes/gameboy/video/video.cpp Executable file
View File

@@ -0,0 +1,82 @@
#include <gameboy/gameboy.hpp>
#define VIDEO_CPP
namespace GameBoy {
Video video;
unsigned Video::palette_dmg(unsigned color) const {
unsigned R = monochrome[color][0] * 1023.0;
unsigned G = monochrome[color][1] * 1023.0;
unsigned B = monochrome[color][2] * 1023.0;
return (R << 20) + (G << 10) + (B << 0);
}
unsigned Video::palette_sgb(unsigned color) const {
unsigned R = (3 - color) * 341;
unsigned G = (3 - color) * 341;
unsigned B = (3 - color) * 341;
return (R << 20) + (G << 10) + (B << 0);
}
unsigned Video::palette_cgb(unsigned color) const {
unsigned r = (color >> 0) & 31;
unsigned g = (color >> 5) & 31;
unsigned b = (color >> 10) & 31;
unsigned R = (r * 26 + g * 4 + b * 2);
unsigned G = ( g * 24 + b * 8);
unsigned B = (r * 6 + g * 4 + b * 22);
R = min(960, R);
G = min(960, G);
B = min(960, B);
return (R << 20) + (G << 10) + (B << 0);
}
void Video::generate(Format format) {
if(system.dmg()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_dmg(n);
if(system.sgb()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_sgb(n);
if(system.cgb()) for(unsigned n = 0; n < (1 << 15); n++) palette[n] = palette_cgb(n);
if(format == Format::RGB24) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff);
}
}
if(format == Format::RGB16) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f);
}
}
if(format == Format::RGB15) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f);
}
}
}
Video::Video() {
palette = new unsigned[1 << 15];
}
Video::~Video() {
delete[] palette;
}
const double Video::monochrome[4][3] = {
{ 0.605, 0.734, 0.059 },
{ 0.543, 0.672, 0.059 },
{ 0.188, 0.383, 0.188 },
{ 0.059, 0.219, 0.059 },
};
}

17
bsnes/gameboy/video/video.hpp Executable file
View File

@@ -0,0 +1,17 @@
struct Video {
enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 };
unsigned *palette;
unsigned palette_dmg(unsigned color) const;
unsigned palette_sgb(unsigned color) const;
unsigned palette_cgb(unsigned color) const;
void generate(Format format);
Video();
~Video();
private:
static const double monochrome[4][3];
};
extern Video video;

View File

@@ -19,6 +19,9 @@ ifeq ($(platform),)
ifeq ($(uname),)
platform := win
delete = del $(subst /,\,$1)
else ifneq ($(findstring CYGWIN,$(uname)),)
platform := win
delete = del $(subst /,\,$1)
else ifneq ($(findstring Darwin,$(uname)),)
platform := osx
delete = rm -f $1
@@ -38,6 +41,9 @@ ifeq ($(compiler),)
endif
endif
c := $(compiler) -std=gnu99
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
ifeq ($(prefix),)
prefix := /usr/local
endif

View File

@@ -2,17 +2,15 @@
#define NALL_ANY_HPP
#include <typeinfo>
#include <type_traits>
#include <nall/static.hpp>
#include <nall/type_traits.hpp>
namespace nall {
class any {
public:
struct any {
bool empty() const { return container; }
const std::type_info& type() const { return container ? container->type() : typeid(void); }
template<typename T> any& operator=(const T& value_) {
typedef typename static_if<
typedef typename type_if<
std::is_array<T>::value,
typename std::remove_extent<typename std::add_const<T>::type>::type*,
T

View File

@@ -4,138 +4,286 @@
#include <stdlib.h>
#include <algorithm>
#include <initializer_list>
#include <type_traits>
#include <utility>
#include <nall/algorithm.hpp>
#include <nall/bit.hpp>
#include <nall/sort.hpp>
#include <nall/type_traits.hpp>
#include <nall/utility.hpp>
namespace nall {
//dynamic vector array
//neither constructor nor destructor is ever invoked;
//thus, this should only be used for POD objects.
template<typename T> class array {
protected:
T *pool;
unsigned poolsize, buffersize;
public:
unsigned size() const { return buffersize; }
unsigned capacity() const { return poolsize; }
template<typename T, typename Enable = void> struct array;
void reset() {
if(pool) free(pool);
pool = nullptr;
poolsize = 0;
buffersize = 0;
//non-reference array
//===================
template<typename T> struct array<T, typename std::enable_if<!std::is_reference<T>::value>::type> {
struct exception_out_of_bounds{};
protected:
T *pool;
unsigned poolsize, objectsize;
public:
unsigned size() const { return objectsize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) free(pool);
pool = nullptr;
poolsize = 0;
objectsize = 0;
}
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
pool = (T*)realloc(pool, newsize * sizeof(T));
poolsize = newsize;
objectsize = min(objectsize, newsize);
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2
objectsize = newsize;
}
T* get(unsigned minsize = 0) {
if(minsize > objectsize) resize(minsize);
return pool;
}
void append(const T data) {
operator()(objectsize) = data;
}
void append(const T data[], unsigned length) {
for(unsigned n = 0; n < length; n++) operator()(objectsize) = data[n];
}
void remove() {
if(size > 0) resize(size - 1); //remove last element only
}
void remove(unsigned index, unsigned count = 1) {
for(unsigned i = index; count + i < objectsize; i++) {
pool[i] = pool[count + i];
}
if(count + index >= objectsize) resize(index); //every element >= index was removed
else resize(objectsize - count);
}
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
void sort() {
nall::sort(pool, objectsize);
}
pool = (T*)realloc(pool, newsize * sizeof(T));
poolsize = newsize;
buffersize = min(buffersize, newsize);
template<typename Comparator> void sort(const Comparator &lessthan) {
nall::sort(pool, objectsize, lessthan);
}
optional<unsigned> find(const T data) {
for(unsigned n = 0; n < size(); n++) if(pool[n] == data) return { true, n };
return { false, 0u };
}
void clear() {
memset(pool, 0, objectsize * sizeof(T));
}
array() : pool(nullptr), poolsize(0), objectsize(0) {
}
array(std::initializer_list<T> list) : pool(nullptr), poolsize(0), objectsize(0) {
for(auto &data : list) append(data);
}
~array() {
reset();
}
//copy
array& operator=(const array &source) {
if(pool) free(pool);
objectsize = source.objectsize;
poolsize = source.poolsize;
pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size,
memcpy(pool, source.pool, sizeof(T) * objectsize); //... but only copy used pool objects
return *this;
}
array(const array &source) : pool(nullptr), poolsize(0), objectsize(0) {
operator=(source);
}
//move
array& operator=(array &&source) {
if(pool) free(pool);
pool = source.pool;
poolsize = source.poolsize;
objectsize = source.objectsize;
source.pool = nullptr;
source.reset();
return *this;
}
array(array &&source) : pool(nullptr), poolsize(0), objectsize(0) {
operator=(std::move(source));
}
//access
inline T& operator[](unsigned position) {
if(position >= objectsize) throw exception_out_of_bounds();
return pool[position];
}
inline const T& operator[](unsigned position) const {
if(position >= objectsize) throw exception_out_of_bounds();
return pool[position];
}
inline T& operator()(unsigned position) {
if(position >= objectsize) resize(position + 1);
return pool[position];
}
inline const T& operator()(unsigned position, const T& data) {
if(position >= objectsize) return data;
return pool[position];
}
//iteration
T* begin() { return &pool[0]; }
T* end() { return &pool[objectsize]; }
const T* begin() const { return &pool[0]; }
const T* end() const { return &pool[objectsize]; }
};
//reference array
//===============
template<typename TR> struct array<TR, typename std::enable_if<std::is_reference<TR>::value>::type> {
struct exception_out_of_bounds{};
protected:
typedef typename std::remove_reference<TR>::type T;
T **pool;
unsigned poolsize, objectsize;
public:
unsigned size() const { return objectsize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) free(pool);
pool = nullptr;
poolsize = 0;
objectsize = 0;
}
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
pool = (T**)realloc(pool, sizeof(T*) * newsize);
poolsize = newsize;
objectsize = min(objectsize, newsize);
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(bit::round(newsize));
objectsize = newsize;
}
template<typename... Args>
bool append(T& data, Args&&... args) {
bool result = append(data);
append(std::forward<Args>(args)...);
return result;
}
bool append(T& data) {
if(find(data)) return false;
unsigned offset = objectsize++;
if(offset >= poolsize) resize(offset + 1);
pool[offset] = &data;
return true;
}
bool remove(T& data) {
if(auto position = find(data)) {
for(signed i = position(); i < objectsize - 1; i++) pool[i] = pool[i + 1];
resize(objectsize - 1);
return true;
}
return false;
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2
buffersize = newsize;
}
optional<unsigned> find(const T& data) {
for(unsigned n = 0; n < objectsize; n++) if(pool[n] == &data) return { true, n };
return { false, 0u };
}
T* get(unsigned minsize = 0) {
if(minsize > buffersize) resize(minsize);
if(minsize > buffersize) throw "array[] out of bounds";
return pool;
}
template<typename... Args> array(Args&&... args) : pool(nullptr), poolsize(0), objectsize(0) {
construct(std::forward<Args>(args)...);
}
void append(const T data) {
operator[](buffersize) = data;
}
~array() {
reset();
}
void append(const T data[], unsigned length) {
for(unsigned n = 0; n < length; n++) operator[](buffersize) = data[n];
}
array& operator=(const array &source) {
if(pool) free(pool);
objectsize = source.objectsize;
poolsize = source.poolsize;
pool = (T**)malloc(sizeof(T*) * poolsize);
memcpy(pool, source.pool, sizeof(T*) * objectsize);
return *this;
}
void remove() {
if(size > 0) resize(size - 1); //remove last element only
}
array& operator=(const array &&source) {
if(pool) free(pool);
pool = source.pool;
poolsize = source.poolsize;
objectsize = source.objectsize;
source.pool = nullptr;
source.reset();
return *this;
}
void remove(unsigned index, unsigned count = 1) {
for(unsigned i = index; count + i < buffersize; i++) {
pool[i] = pool[count + i];
}
if(count + index >= buffersize) resize(index); //every element >= index was removed
else resize(buffersize - count);
}
T& operator[](unsigned position) const {
if(position >= objectsize) throw exception_out_of_bounds();
return *pool[position];
}
optional<unsigned> find(const T data) {
for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return { true, i };
return { false, 0 };
}
void clear() {
memset(pool, 0, buffersize * sizeof(T));
}
array() : pool(nullptr), poolsize(0), buffersize(0) {
}
array(std::initializer_list<T> list) : pool(nullptr), poolsize(0), buffersize(0) {
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
}
~array() {
reset();
}
//copy
array& operator=(const array &source) {
if(pool) free(pool);
buffersize = source.buffersize;
poolsize = source.poolsize;
pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size,
memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects
return *this;
}
array(const array &source) : pool(nullptr), poolsize(0), buffersize(0) {
operator=(source);
}
//move
array& operator=(array &&source) {
if(pool) free(pool);
pool = source.pool;
poolsize = source.poolsize;
buffersize = source.buffersize;
source.pool = nullptr;
source.reset();
return *this;
}
array(array &&source) : pool(nullptr), poolsize(0), buffersize(0) {
operator=(std::move(source));
}
//index
inline T& operator[](unsigned index) {
if(index >= buffersize) resize(index + 1);
if(index >= buffersize) throw "array[] out of bounds";
return pool[index];
}
inline const T& operator[](unsigned index) const {
if(index >= buffersize) throw "array[] out of bounds";
return pool[index];
}
//iteration
T* begin() { return &pool[0]; }
T* end() { return &pool[buffersize]; }
const T* begin() const { return &pool[0]; }
const T* end() const { return &pool[buffersize]; }
//iteration
struct iterator {
bool operator!=(const iterator &source) const { return position != source.position; }
T& operator*() { return source.operator[](position); }
iterator& operator++() { position++; return *this; }
iterator(const array &source, unsigned position) : source(source), position(position) {}
private:
const array &source;
unsigned position;
};
iterator begin() { return iterator(*this, 0); }
iterator end() { return iterator(*this, objectsize); }
const iterator begin() const { return iterator(*this, 0); }
const iterator end() const { return iterator(*this, objectsize); }
private:
void construct() {
}
void construct(const array& source) { operator=(source); }
void construct(const array&& source) { operator=(std::move(source)); }
template<typename... Args> void construct(T& data, Args&&... args) {
append(data);
construct(std::forward<Args>(args)...);
}
};
}
#endif

View File

@@ -79,6 +79,19 @@ constexpr inline uintmax_t hex(const char *s) {
);
}
constexpr inline intmax_t numeral(const char *s) {
return (
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
*s == '0' ? octal_(s + 1) :
*s == '+' ? +decimal_(s + 1) :
*s == '-' ? -decimal_(s + 1) :
decimal_(s)
);
}
inline double fp(const char *s) {
return atof(s);
}

View File

@@ -2,45 +2,56 @@
#define NALL_BIT_HPP
namespace nall {
template<int bits> constexpr inline unsigned uclamp(const unsigned x) {
enum { y = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
template<unsigned bits>
constexpr inline uintmax_t uclamp(const uintmax_t x) {
enum : uintmax_t { b = 1ull << (bits - 1), y = b * 2 - 1 };
return y + ((x - y) & -(x < y)); //min(x, y);
}
template<int bits> constexpr inline unsigned uclip(const unsigned x) {
enum { m = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
template<unsigned bits>
constexpr inline uintmax_t uclip(const uintmax_t x) {
enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
return (x & m);
}
template<int bits> constexpr inline signed sclamp(const signed x) {
enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 };
template<unsigned bits>
constexpr inline intmax_t sclamp(const intmax_t x) {
enum : intmax_t { b = 1ull << (bits - 1), m = b - 1 };
return (x > m) ? m : (x < -b) ? -b : x;
}
template<int bits> constexpr inline signed sclip(const signed x) {
enum { b = 1U << (bits - 1), m = (1U << bits) - 1 };
template<unsigned bits>
constexpr inline intmax_t sclip(const intmax_t x) {
enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
return ((x & m) ^ b) - b;
}
namespace bit {
//lowest(0b1110) == 0b0010
template<typename T> constexpr inline T lowest(const T x) {
constexpr inline uintmax_t lowest(const uintmax_t x) {
return x & -x;
}
//clear_lowest(0b1110) == 0b1100
template<typename T> constexpr inline T clear_lowest(const T x) {
constexpr inline uintmax_t clear_lowest(const uintmax_t x) {
return x & (x - 1);
}
//set_lowest(0b0101) == 0b0111
template<typename T> constexpr inline T set_lowest(const T x) {
constexpr inline uintmax_t set_lowest(const uintmax_t x) {
return x | (x + 1);
}
//count number of bits set in a byte
inline unsigned count(uintmax_t x) {
unsigned count = 0;
do count += x & 1; while(x >>= 1);
return count;
}
//round up to next highest single bit:
//round(15) == 16, round(16) == 16, round(17) == 32
inline unsigned round(unsigned x) {
inline uintmax_t round(uintmax_t x) {
if((x & (x - 1)) == 0) return x;
while(x & (x - 1)) x &= x - 1;
return x << 1;

View File

@@ -8,11 +8,49 @@ namespace nall {
struct compositor {
inline static bool enabled();
inline static bool enable(bool status);
#if defined(PLATFORM_X)
enum class Compositor : unsigned { Unknown, Metacity, Xfwm4 };
inline static Compositor detect();
inline static bool enabled_metacity();
inline static bool enable_metacity(bool status);
inline static bool enabled_xfwm4();
inline static bool enable_xfwm4(bool status);
#endif
};
#if defined(PLATFORM_X)
bool compositor::enabled() {
//Metacity
bool compositor::enabled_metacity() {
FILE *fp = popen("gconftool-2 --get /apps/metacity/general/compositing_manager", "r");
if(fp == 0) return false;
char buffer[512];
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
if(!memcmp(buffer, "true", 4)) return true;
return false;
}
bool compositor::enable_metacity(bool status) {
FILE *fp;
if(status) {
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager true", "r");
} else {
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager false", "r");
}
if(fp == 0) return false;
pclose(fp);
return true;
}
//Xfwm4
bool compositor::enabled_xfwm4() {
FILE *fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
if(fp == 0) return false;
@@ -23,7 +61,7 @@ bool compositor::enabled() {
return false;
}
bool compositor::enable(bool status) {
bool compositor::enable_xfwm4(bool status) {
FILE *fp;
if(status) {
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
@@ -35,6 +73,41 @@ bool compositor::enable(bool status) {
return true;
}
//General
compositor::Compositor compositor::detect() {
Compositor result = Compositor::Unknown;
FILE *fp;
char buffer[512];
fp = popen("pidof metacity", "r");
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Metacity;
pclose(fp);
fp = popen("pidof xfwm4", "r");
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Xfwm4;
pclose(fp);
return result;
}
bool compositor::enabled() {
switch(detect()) {
case Compositor::Metacity: return enabled_metacity();
case Compositor::Xfwm4: return enabled_xfwm4();
default: return false;
}
}
bool compositor::enable(bool status) {
switch(detect()) {
case Compositor::Metacity: return enable_metacity(status);
case Compositor::Xfwm4: return enable_xfwm4(status);
default: return false;
}
}
#elif defined(PLATFORM_WINDOWS)
bool compositor::enabled() {

View File

@@ -32,7 +32,7 @@ namespace nall {
string desc;
type_t type;
string get() const {
inline string get() const {
switch(type) {
case boolean_t: return { *(bool*)data };
case signed_t: return { *(signed*)data };
@@ -43,7 +43,7 @@ namespace nall {
return "???";
}
void set(string s) {
inline void set(string s) {
switch(type) {
case boolean_t: *(bool*)data = (s == "true"); break;
case signed_t: *(signed*)data = integer(s); break;
@@ -53,24 +53,27 @@ namespace nall {
}
}
};
linear_vector<item_t> list;
vector<item_t> list;
template<typename T>
void attach(T &data, const char *name, const char *desc = "") {
unsigned n = list.size();
list[n].data = (uintptr_t)&data;
list[n].name = name;
list[n].desc = desc;
if(configuration_traits::is_boolean<T>::value) list[n].type = boolean_t;
else if(configuration_traits::is_signed<T>::value) list[n].type = signed_t;
else if(configuration_traits::is_unsigned<T>::value) list[n].type = unsigned_t;
else if(configuration_traits::is_double<T>::value) list[n].type = double_t;
else if(configuration_traits::is_string<T>::value) list[n].type = string_t;
else list[n].type = unknown_t;
inline void append(T &data, const char *name, const char *desc = "") {
item_t item = { (uintptr_t)&data, name, desc };
if(configuration_traits::is_boolean<T>::value) item.type = boolean_t;
else if(configuration_traits::is_signed<T>::value) item.type = signed_t;
else if(configuration_traits::is_unsigned<T>::value) item.type = unsigned_t;
else if(configuration_traits::is_double<T>::value) item.type = double_t;
else if(configuration_traits::is_string<T>::value) item.type = string_t;
else item.type = unknown_t;
list.append(item);
}
virtual bool load(const string &filename) {
//deprecated
template<typename T>
inline void attach(T &data, const char *name, const char *desc = "") {
append(data, name, desc);
}
inline virtual bool load(const string &filename) {
string data;
if(data.readfile(filename) == true) {
data.replace("\r", "");
@@ -100,7 +103,7 @@ namespace nall {
}
}
virtual bool save(const string &filename) const {
inline virtual bool save(const string &filename) const {
file fp;
if(fp.open(filename, file::mode::write)) {
for(unsigned i = 0; i < list.size(); i++) {

View File

@@ -56,7 +56,7 @@ struct directory {
}
FindClose(handle);
}
if(list.size() > 0) sort(&list[0], list.size());
if(list.size() > 0) list.sort();
for(auto &name : list) name.append("/"); //must append after sorting
return list;
}
@@ -83,7 +83,7 @@ struct directory {
}
FindClose(handle);
}
if(list.size() > 0) sort(&list[0], list.size());
if(list.size() > 0) list.sort();
return list;
}
@@ -116,7 +116,7 @@ struct directory {
}
closedir(dp);
}
if(list.size() > 0) sort(&list[0], list.size());
if(list.size() > 0) list.sort();
for(auto &name : list) name.append("/"); //must append after sorting
return list;
}
@@ -136,7 +136,7 @@ struct directory {
}
closedir(dp);
}
if(list.size() > 0) sort(&list[0], list.size());
if(list.size() > 0) list.sort();
return list;
}

View File

@@ -134,12 +134,12 @@ namespace nall {
file_offset = req_offset;
}
int offset() {
int offset() const {
if(!fp) return -1; //file not open
return file_offset;
}
int size() {
int size() const {
if(!fp) return -1; //file not open
return file_size;
}
@@ -194,7 +194,7 @@ namespace nall {
}
}
bool open() {
bool open() const {
return fp;
}

View File

@@ -1,6 +1,7 @@
#ifndef NALL_FILEMAP_HPP
#define NALL_FILEMAP_HPP
#include <nall/file.hpp>
#include <nall/stdint.hpp>
#include <nall/windows/utf8.hpp>

View File

@@ -100,6 +100,16 @@ GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
markup.append(
"<?xml version='1.0' encoding='UTF-8'?>\n",
"<cartridge mapper='", info.mapper, "' rtc='", info.rtc, "' rumble='", info.rumble, "'>\n",
" <rom size='0x", hex(romsize), "'/>\n");
if(info.ramsize > 0) markup.append(
" <ram size='0x", hex(info.ramsize), "' battery='", info.battery, "'/>\n");
markup.append(
"</cartridge>\n");
/*
markup.append("cartridge mapper=", info.mapper);
if(info.rtc) markup.append(" rtc");
if(info.rumble) markup.append(" rumble");
@@ -109,6 +119,7 @@ GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
if(info.ramsize > 0)
markup.append("\t" "ram size=", hex(info.ramsize), info.battery ? " non-volatile\n" : "\n");
*/
}
}

View File

@@ -11,11 +11,11 @@ struct gzip {
uint8_t *data;
unsigned size;
bool decompress(const string &filename);
bool decompress(const uint8_t *data, unsigned size);
inline bool decompress(const string &filename);
inline bool decompress(const uint8_t *data, unsigned size);
gzip();
~gzip();
inline gzip();
inline ~gzip();
};
bool gzip::decompress(const string &filename) {

View File

@@ -117,7 +117,7 @@ struct http {
}
}
} else if(auto position = header.iposition("\r\nContent-Length: ")) {
unsigned length = decimal((const char*)header + position() + 16);
unsigned length = decimal((const char*)header + position() + 18);
while(length) {
char buffer[256];
int packetlength = recv(serversocket, buffer, min(256, length), 0);

465
bsnes/nall/image.hpp Executable file
View File

@@ -0,0 +1,465 @@
#ifndef NALL_IMAGE_HPP
#define NALL_IMAGE_HPP
#include <nall/bmp.hpp>
#include <nall/filemap.hpp>
#include <nall/interpolation.hpp>
#include <nall/png.hpp>
#include <nall/stdint.hpp>
#include <algorithm>
namespace nall {
struct image {
uint8_t *data;
unsigned width;
unsigned height;
unsigned pitch;
bool endian; //0 = little, 1 = big
unsigned depth;
unsigned stride;
struct Channel {
uint64_t mask;
unsigned depth;
unsigned shift;
} alpha, red, green, blue;
typedef double (*interpolation)(double, double, double, double, double);
static inline unsigned bitDepth(uint64_t color);
static inline unsigned bitShift(uint64_t color);
static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth);
inline image& operator=(const image &source);
inline image& operator=(image &&source);
inline image(const image &source);
inline image(image &&source);
inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
inline image();
inline ~image();
inline uint64_t read(const uint8_t *data) const;
inline void write(uint8_t *data, uint64_t value) const;
inline void free();
inline void allocate(unsigned width, unsigned height);
inline void clear(uint64_t color);
inline bool load(const string &filename);
//inline bool loadBMP(const uint8_t *data, unsigned size);
inline bool loadPNG(const uint8_t *data, unsigned size);
inline void scale(unsigned width, unsigned height, interpolation op);
inline void transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
inline void alphaBlend(uint64_t alphaColor);
protected:
inline uint64_t interpolate(double mu, const uint64_t *s, interpolation op);
inline void scaleX(unsigned width, interpolation op);
inline void scaleY(unsigned height, interpolation op);
inline bool loadBMP(const string &filename);
inline bool loadPNG(const string &filename);
};
//static
unsigned image::bitDepth(uint64_t color) {
unsigned depth = 0;
if(color) while((color & 1) == 0) color >>= 1;
while((color & 1) == 1) { color >>= 1; depth++; }
return depth;
}
unsigned image::bitShift(uint64_t color) {
unsigned shift = 0;
if(color) while((color & 1) == 0) { color >>= 1; shift++; }
return shift;
}
uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) {
while(sourceDepth < targetDepth) {
color = (color << sourceDepth) | color;
sourceDepth += sourceDepth;
}
if(targetDepth < sourceDepth) color >>= (sourceDepth - targetDepth);
return color;
}
//public
image& image::operator=(const image &source) {
free();
width = source.width;
height = source.height;
pitch = source.pitch;
endian = source.endian;
stride = source.stride;
alpha = source.alpha;
red = source.red;
green = source.green;
blue = source.blue;
data = new uint8_t[width * height * stride];
memcpy(data, source.data, width * height * stride);
return *this;
}
image& image::operator=(image &&source) {
width = source.width;
height = source.height;
pitch = source.pitch;
endian = source.endian;
stride = source.stride;
alpha = source.alpha;
red = source.red;
green = source.green;
blue = source.blue;
data = source.data;
source.data = nullptr;
return *this;
}
image::image(const image &source) : data(nullptr) {
operator=(source);
}
image::image(image &&source) : data(nullptr) {
operator=(std::forward<image>(source));
}
image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) : data(nullptr) {
width = 0, height = 0, pitch = 0;
this->endian = endian;
this->depth = depth;
this->stride = (depth / 8) + ((depth & 7) > 0);
alpha.mask = alphaMask, red.mask = redMask, green.mask = greenMask, blue.mask = blueMask;
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
}
image::image() : data(nullptr) {
width = 0, height = 0, pitch = 0;
this->endian = 0;
this->depth = 32;
this->stride = 4;
alpha.mask = 255u << 24, red.mask = 255u << 16, green.mask = 255u << 8, blue.mask = 255u << 0;
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
}
image::~image() {
free();
}
uint64_t image::read(const uint8_t *data) const {
uint64_t result = 0;
if(endian == 0) {
for(signed n = stride - 1; n >= 0; n--) result = (result << 8) | data[n];
} else {
for(signed n = 0; n < stride; n++) result = (result << 8) | data[n];
}
return result;
}
void image::write(uint8_t *data, uint64_t value) const {
if(endian == 0) {
for(signed n = 0; n < stride; n++) { data[n] = value; value >>= 8; }
} else {
for(signed n = stride - 1; n >= 0; n--) { data[n] = value; value >>= 8; }
}
}
void image::free() {
if(data) delete[] data;
data = nullptr;
}
void image::allocate(unsigned width, unsigned height) {
if(data != nullptr && this->width == width && this->height == height) return;
free();
data = new uint8_t[width * height * stride]();
pitch = width * stride;
this->width = width;
this->height = height;
}
void image::clear(uint64_t color) {
uint8_t *dp = data;
for(unsigned n = 0; n < width * height; n++) {
write(dp, color);
dp += stride;
}
}
bool image::load(const string &filename) {
if(loadBMP(filename) == true) return true;
if(loadPNG(filename) == true) return true;
return false;
}
void image::scale(unsigned outputWidth, unsigned outputHeight, interpolation op) {
if(width != outputWidth) scaleX(outputWidth, op);
if(height != outputHeight) scaleY(outputHeight, op);
}
void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) {
image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask);
output.allocate(width, height);
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
uint8_t *dp = output.data + output.pitch * y;
uint8_t *sp = data + pitch * y;
for(unsigned x = 0; x < width; x++) {
uint64_t color = read(sp);
sp += stride;
uint64_t a = (color & alpha.mask) >> alpha.shift;
uint64_t r = (color & red.mask) >> red.shift;
uint64_t g = (color & green.mask) >> green.shift;
uint64_t b = (color & blue.mask) >> blue.shift;
a = normalize(a, alpha.depth, output.alpha.depth);
r = normalize(r, red.depth, output.red.depth);
g = normalize(g, green.depth, output.green.depth);
b = normalize(b, blue.depth, output.blue.depth);
output.write(dp, (a << output.alpha.shift) | (r << output.red.shift) | (g << output.green.shift) | (b << output.blue.shift));
dp += output.stride;
}
}
operator=(std::move(output));
}
void image::alphaBlend(uint64_t alphaColor) {
uint64_t alphaR = (alphaColor & red.mask) >> red.shift;
uint64_t alphaG = (alphaColor & green.mask) >> green.shift;
uint64_t alphaB = (alphaColor & blue.mask) >> blue.shift;
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
uint8_t *dp = data + pitch * y;
for(unsigned x = 0; x < width; x++) {
uint64_t color = read(dp);
uint64_t colorA = (color & alpha.mask) >> alpha.shift;
uint64_t colorR = (color & red.mask) >> red.shift;
uint64_t colorG = (color & green.mask) >> green.shift;
uint64_t colorB = (color & blue.mask) >> blue.shift;
double alphaScale = (double)colorA / (double)((1 << alpha.depth) - 1);
colorA = (1 << alpha.depth) - 1;
colorR = (colorR * alphaScale) + (alphaR * (1.0 - alphaScale));
colorG = (colorG * alphaScale) + (alphaG * (1.0 - alphaScale));
colorB = (colorB * alphaScale) + (alphaB * (1.0 - alphaScale));
write(dp, (colorA << alpha.shift) | (colorR << red.shift) | (colorG << green.shift) | (colorB << blue.shift));
dp += stride;
}
}
}
//protected
uint64_t image::interpolate(double mu, const uint64_t *s, double (*op)(double, double, double, double, double)) {
uint64_t aa = (s[0] & alpha.mask) >> alpha.shift, ar = (s[0] & red.mask) >> red.shift,
ag = (s[0] & green.mask) >> green.shift, ab = (s[0] & blue.mask) >> blue.shift;
uint64_t ba = (s[1] & alpha.mask) >> alpha.shift, br = (s[1] & red.mask) >> red.shift,
bg = (s[1] & green.mask) >> green.shift, bb = (s[1] & blue.mask) >> blue.shift;
uint64_t ca = (s[2] & alpha.mask) >> alpha.shift, cr = (s[2] & red.mask) >> red.shift,
cg = (s[2] & green.mask) >> green.shift, cb = (s[2] & blue.mask) >> blue.shift;
uint64_t da = (s[3] & alpha.mask) >> alpha.shift, dr = (s[3] & red.mask) >> red.shift,
dg = (s[3] & green.mask) >> green.shift, db = (s[3] & blue.mask) >> blue.shift;
int64_t A = op(mu, aa, ba, ca, da);
int64_t R = op(mu, ar, br, cr, dr);
int64_t G = op(mu, ag, bg, cg, dg);
int64_t B = op(mu, ab, bb, cb, db);
A = max(0, min(A, (1 << alpha.depth) - 1));
R = max(0, min(R, (1 << red.depth) - 1));
G = max(0, min(G, (1 << green.depth) - 1));
B = max(0, min(B, (1 << blue.depth) - 1));
return (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift);
}
void image::scaleX(unsigned outputWidth, interpolation op) {
uint8_t *outputData = new uint8_t[outputWidth * height * stride];
unsigned outputPitch = outputWidth * stride;
double step = (double)width / (double)outputWidth;
const uint8_t *terminal = data + pitch * height;
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
uint8_t *dp = outputData + outputPitch * y;
uint8_t *sp = data + pitch * y;
double fraction = 0.0;
uint64_t s[4] = { sp < terminal ? read(sp) : 0 }; //B,C (0,1) = center of kernel { 0, 0, 1, 2 }
s[1] = s[0];
s[2] = sp + stride < terminal ? read(sp += stride) : s[1];
s[3] = sp + stride < terminal ? read(sp += stride) : s[2];
for(unsigned x = 0; x < width; x++) {
while(fraction <= 1.0) {
if(dp >= outputData + outputPitch * height) break;
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
dp += stride;
fraction += step;
}
s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
if(sp + stride < terminal) s[3] = read(sp += stride);
fraction -= 1.0;
}
}
free();
data = outputData;
width = outputWidth;
pitch = width * stride;
}
void image::scaleY(unsigned outputHeight, interpolation op) {
uint8_t *outputData = new uint8_t[width * outputHeight * stride];
double step = (double)height / (double)outputHeight;
const uint8_t *terminal = data + pitch * height;
#pragma omp parallel for
for(unsigned x = 0; x < width; x++) {
uint8_t *dp = outputData + stride * x;
uint8_t *sp = data + stride * x;
double fraction = 0.0;
uint64_t s[4] = { sp < terminal ? read(sp) : 0 };
s[1] = s[0];
s[2] = sp + pitch < terminal ? read(sp += pitch) : s[1];
s[3] = sp + pitch < terminal ? read(sp += pitch) : s[2];
for(unsigned y = 0; y < height; y++) {
while(fraction <= 1.0) {
if(dp >= outputData + pitch * outputHeight) break;
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
dp += pitch;
fraction += step;
}
s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
if(sp + pitch < terminal) s[3] = read(sp += pitch);
fraction -= 1.0;
}
}
free();
data = outputData;
height = outputHeight;
}
bool image::loadBMP(const string &filename) {
uint32_t *outputData;
unsigned outputWidth, outputHeight;
if(bmp::read(filename, outputData, outputWidth, outputHeight) == false) return false;
allocate(outputWidth, outputHeight);
const uint32_t *sp = outputData;
uint8_t *dp = data;
for(unsigned y = 0; y < outputHeight; y++) {
for(unsigned x = 0; x < outputWidth; x++) {
uint32_t color = *sp++;
uint64_t a = normalize((uint8_t)(color >> 24), 8, alpha.depth);
uint64_t r = normalize((uint8_t)(color >> 16), 8, red.depth);
uint64_t g = normalize((uint8_t)(color >> 8), 8, green.depth);
uint64_t b = normalize((uint8_t)(color >> 0), 8, blue.depth);
write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift));
dp += stride;
}
}
delete[] outputData;
return true;
}
bool image::loadPNG(const uint8_t *pngData, unsigned pngSize) {
png source;
if(source.decode(pngData, pngSize) == false) return false;
allocate(source.info.width, source.info.height);
const uint8_t *sp = source.data;
uint8_t *dp = data;
auto decode = [&]() -> uint64_t {
uint64_t p, r, g, b, a;
switch(source.info.colorType) {
case 0: //L
r = g = b = source.readbits(sp);
a = (1 << source.info.bitDepth) - 1;
break;
case 2: //R,G,B
r = source.readbits(sp);
g = source.readbits(sp);
b = source.readbits(sp);
a = (1 << source.info.bitDepth) - 1;
break;
case 3: //P
p = source.readbits(sp);
r = source.info.palette[p][0];
g = source.info.palette[p][1];
b = source.info.palette[p][2];
a = (1 << source.info.bitDepth) - 1;
break;
case 4: //L,A
r = g = b = source.readbits(sp);
a = source.readbits(sp);
break;
case 6: //R,G,B,A
r = source.readbits(sp);
g = source.readbits(sp);
b = source.readbits(sp);
a = source.readbits(sp);
break;
}
a = normalize(a, source.info.bitDepth, alpha.depth);
r = normalize(r, source.info.bitDepth, red.depth);
g = normalize(g, source.info.bitDepth, green.depth);
b = normalize(b, source.info.bitDepth, blue.depth);
return (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift);
};
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
write(dp, decode());
dp += stride;
}
}
return true;
}
bool image::loadPNG(const string &filename) {
filemap map;
if(map.open(filename, filemap::mode::read) == false) return false;
return loadPNG(map.data(), map.size());
}
}
#endif

59
bsnes/nall/interpolation.hpp Executable file
View File

@@ -0,0 +1,59 @@
#ifndef NALL_INTERPOLATION_HPP
#define NALL_INTERPOLATION_HPP
namespace nall {
struct Interpolation {
static inline double Nearest(double mu, double a, double b, double c, double d) {
return (mu <= 0.5 ? b : c);
}
static inline double Sublinear(double mu, double a, double b, double c, double d) {
mu = ((mu - 0.5) * 2.0) + 0.5;
if(mu < 0) mu = 0;
if(mu > 1) mu = 1;
return b * (1.0 - mu) + c * mu;
}
static inline double Linear(double mu, double a, double b, double c, double d) {
return b * (1.0 - mu) + c * mu;
}
static inline double Cosine(double mu, double a, double b, double c, double d) {
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
return b * (1.0 - mu) + c * mu;
}
static inline double Cubic(double mu, double a, double b, double c, double d) {
double A = d - c - a + b;
double B = a - b - A;
double C = c - a;
double D = b;
return A * (mu * mu * mu) + B * (mu * mu) + C * mu + D;
}
static inline double Hermite(double mu1, double a, double b, double c, double d) {
const double tension = 0.0; //-1 = low, 0 = normal, +1 = high
const double bias = 0.0; //-1 = left, 0 = even, +1 = right
double mu2, mu3, m0, m1, a0, a1, a2, a3;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
};
}
#endif

View File

@@ -20,6 +20,7 @@ struct Intrinsics {
#define COMPILER_VISUALC
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::VisualC; }
#else
#warning "unable to detect compiler"
#define COMPILER_UNKNOWN
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Unknown; }
#endif
@@ -37,8 +38,9 @@ struct Intrinsics {
#define PLATFORM_WIN
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Windows; }
#else
#warning "unable to detect platform"
#define PLATFORM_UNKNOWN
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Unknown; }
#endif
/* Endian detection */
@@ -52,9 +54,10 @@ struct Intrinsics {
#define ARCH_MSB
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::MSB; }
#else
#warning "unable to detect endian"
#define ENDIAN_UNKNOWN
#define ARCH_UNKNOWN
Intrinsics::Endian Intrinsics::endia() { return Intrinsics::Endian::Unknown; }
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::Unknown; }
#endif
#endif

116
bsnes/nall/map.hpp Executable file
View File

@@ -0,0 +1,116 @@
#ifndef NALL_MAP_HPP
#define NALL_MAP_HPP
#include <nall/vector.hpp>
namespace nall {
template<typename LHS, typename RHS>
struct map {
struct pair {
LHS name;
RHS data;
};
inline void reset() {
list.reset();
}
inline unsigned size() const {
return list.size();
}
//O(log n) find
inline optional<unsigned> find(const LHS &name) const {
signed first = 0, last = size() - 1;
while(first <= last) {
signed middle = (first + last) / 2;
if(name < list[middle].name) last = middle - 1; //search lower half
else if(list[middle].name < name) first = middle + 1; //search upper half
else return { true, middle }; //match found
}
return { false, 0u };
}
//O(n) insert + O(log n) find
inline RHS& insert(const LHS &name, const RHS &data) {
if(auto position = find(name)) {
list[position()].data = data;
return list[position()].data;
}
signed offset = size();
for(unsigned n = 0; n < size(); n++) {
if(name < list[n].name) { offset = n; break; }
}
list.insert(offset, { name, data });
return list[offset].data;
}
//O(log n) find
inline void modify(const LHS &name, const RHS &data) {
if(auto position = find(name)) list[position()].data = data;
}
//O(n) remove + O(log n) find
inline void remove(const LHS &name) {
if(auto position = find(name)) list.remove(position());
}
//O(log n) find
inline RHS& operator[](const LHS &name) {
if(auto position = find(name)) return list[position()].data;
throw;
}
inline const RHS& operator[](const LHS &name) const {
if(auto position = find(name)) return list[position()].data;
throw;
}
inline RHS& operator()(const LHS &name) {
return insert(name, RHS());
}
inline const RHS& operator()(const LHS &name, const RHS &data) const {
if(auto position = find(name)) return list[position()].data;
return data;
}
inline pair* begin() { return list.begin(); }
inline pair* end() { return list.end(); }
inline const pair* begin() const { return list.begin(); }
inline const pair* end() const { return list.end(); }
protected:
vector<pair> list;
};
template<typename LHS, typename RHS>
struct bidirectional_map {
const map<LHS, RHS> &lhs;
const map<RHS, LHS> &rhs;
inline void reset() {
llist.reset();
rlist.reset();
}
inline unsigned size() const {
return llist.size();
}
inline void insert(const LHS &ldata, const RHS &rdata) {
llist.insert(ldata, rdata);
rlist.insert(rdata, ldata);
}
inline bidirectional_map() : lhs(llist), rhs(rlist) {}
protected:
map<LHS, RHS> llist;
map<RHS, LHS> rlist;
};
}
#endif

10
bsnes/nall/mosaic.hpp Executable file
View File

@@ -0,0 +1,10 @@
#ifndef NALL_MOSAIC_HPP
#define NALL_MOSAIC_HPP
#define NALL_MOSAIC_INTERNAL_HPP
#include <nall/mosaic/bitstream.hpp>
#include <nall/mosaic/context.hpp>
#include <nall/mosaic/parser.hpp>
#undef NALL_MOSAIC_INTERNAL_HPP
#endif

55
bsnes/nall/mosaic/bitstream.hpp Executable file
View File

@@ -0,0 +1,55 @@
#ifdef NALL_MOSAIC_INTERNAL_HPP
namespace nall {
namespace mosaic {
struct bitstream {
filemap fp;
uint8_t *data;
unsigned size;
bool readonly;
bool endian;
inline bool read(uint64_t addr) const {
if(data == nullptr || (addr >> 3) >= size) return 0;
unsigned mask = endian == 0 ? (0x01 << (addr & 7)) : (0x80 >> (addr & 7));
return data[addr >> 3] & mask;
}
inline void write(uint64_t addr, bool value) {
if(data == nullptr || readonly == true || (addr >> 3) >= size) return;
unsigned mask = endian == 0 ? (0x01 << (addr & 7)) : (0x80 >> (addr & 7));
if(value == 0) data[addr >> 3] &= ~mask;
if(value == 1) data[addr >> 3] |= mask;
}
inline bool open(const string &filename) {
readonly = false;
if(fp.open(filename, filemap::mode::readwrite) == false) {
readonly = true;
if(fp.open(filename, filemap::mode::read) == false) {
return false;
}
}
data = fp.data();
size = fp.size();
return true;
}
inline void close() {
fp.close();
data = nullptr;
}
inline bitstream() : data(nullptr), endian(1) {
}
inline ~bitstream() {
close();
}
};
}
}
#endif

224
bsnes/nall/mosaic/context.hpp Executable file
View File

@@ -0,0 +1,224 @@
#ifdef NALL_MOSAIC_INTERNAL_HPP
namespace nall {
namespace mosaic {
struct context {
unsigned offset;
unsigned width;
unsigned height;
unsigned count;
bool endian; //0 = lsb, 1 = msb
bool order; //0 = linear, 1 = planar
unsigned depth; //1 - 24bpp
unsigned blockWidth;
unsigned blockHeight;
unsigned blockStride;
unsigned blockOffset;
array<unsigned> block;
unsigned tileWidth;
unsigned tileHeight;
unsigned tileStride;
unsigned tileOffset;
array<unsigned> tile;
unsigned mosaicWidth;
unsigned mosaicHeight;
unsigned mosaicStride;
unsigned mosaicOffset;
array<unsigned> mosaic;
unsigned paddingWidth;
unsigned paddingHeight;
unsigned paddingColor;
array<unsigned> palette;
inline unsigned objectWidth() const { return blockWidth * tileWidth * mosaicWidth + paddingWidth; }
inline unsigned objectHeight() const { return blockHeight * tileHeight * mosaicHeight + paddingHeight; }
inline unsigned objectSize() const {
unsigned size = blockStride * tileWidth * tileHeight * mosaicWidth * mosaicHeight
+ blockOffset * tileHeight * mosaicWidth * mosaicHeight
+ tileStride * mosaicWidth * mosaicHeight
+ tileOffset * mosaicHeight;
return max(1u, size);
}
inline unsigned eval(const string &expression) {
intmax_t result;
if(fixedpoint::eval(expression, result) == false) return 0u;
return result;
}
inline void eval(array<unsigned> &buffer, const string &expression_) {
string expression = expression_;
bool function = false;
for(auto &c : expression) {
if(c == '(') function = true;
if(c == ')') function = false;
if(c == ',' && function == true) c = ';';
}
lstring list = expression.split(",");
for(auto &item : list) {
item.trim();
if(item.wildcard("f(?*) ?*")) {
item.ltrim<1>("f(");
lstring part = item.split<1>(") ");
lstring args = part[0].split<3>(";");
for(auto &item : args) item.trim();
unsigned length = eval(args(0, "0"));
unsigned offset = eval(args(1, "0"));
unsigned stride = eval(args(2, "0"));
if(args.size() < 2) offset = buffer.size();
if(args.size() < 3) stride = 1;
for(unsigned n = 0; n < length; n++) {
string fn = part[1];
fn.replace("n", decimal(n));
fn.replace("o", decimal(offset));
fn.replace("p", decimal(buffer.size()));
buffer.resize(offset + 1);
buffer[offset] = eval(fn);
offset += stride;
}
} else if(item.wildcard("base64*")) {
unsigned offset = 0;
item.ltrim<1>("base64");
if(item.wildcard("(?*) *")) {
item.ltrim<1>("(");
lstring part = item.split<1>(") ");
offset = eval(part[0]);
item = part(1, "");
}
item.trim();
for(auto &c : item) {
if(c >= 'A' && c <= 'Z') buffer.append(offset + c - 'A' + 0);
if(c >= 'a' && c <= 'z') buffer.append(offset + c - 'a' + 26);
if(c >= '0' && c <= '9') buffer.append(offset + c - '0' + 52);
if(c == '-') buffer.append(offset + 62);
if(c == '_') buffer.append(offset + 63);
}
} else if(item.wildcard("file *")) {
item.ltrim<1>("file ");
item.trim();
//...
} else if(item.empty() == false) {
buffer.append(eval(item));
}
}
}
inline void parse(const string &data) {
reset();
lstring lines = data.split("\n");
for(auto &line : lines) {
lstring part = line.split<1>(":");
if(part.size() != 2) continue;
part[0].trim();
part[1].trim();
if(part[0] == "offset") offset = eval(part[1]);
if(part[0] == "width") width = eval(part[1]);
if(part[0] == "height") height = eval(part[1]);
if(part[0] == "count") count = eval(part[1]);
if(part[0] == "endian") endian = eval(part[1]);
if(part[0] == "order") order = eval(part[1]);
if(part[0] == "depth") depth = eval(part[1]);
if(part[0] == "blockWidth") blockWidth = eval(part[1]);
if(part[0] == "blockHeight") blockHeight = eval(part[1]);
if(part[0] == "blockStride") blockStride = eval(part[1]);
if(part[0] == "blockOffset") blockOffset = eval(part[1]);
if(part[0] == "block") eval(block, part[1]);
if(part[0] == "tileWidth") tileWidth = eval(part[1]);
if(part[0] == "tileHeight") tileHeight = eval(part[1]);
if(part[0] == "tileStride") tileStride = eval(part[1]);
if(part[0] == "tileOffset") tileOffset = eval(part[1]);
if(part[0] == "tile") eval(tile, part[1]);
if(part[0] == "mosaicWidth") mosaicWidth = eval(part[1]);
if(part[0] == "mosaicHeight") mosaicHeight = eval(part[1]);
if(part[0] == "mosaicStride") mosaicStride = eval(part[1]);
if(part[0] == "mosaicOffset") mosaicOffset = eval(part[1]);
if(part[0] == "mosaic") eval(mosaic, part[1]);
if(part[0] == "paddingWidth") paddingWidth = eval(part[1]);
if(part[0] == "paddingHeight") paddingHeight = eval(part[1]);
if(part[0] == "paddingColor") paddingColor = eval(part[1]);
if(part[0] == "palette") eval(palette, part[1]);
}
sanitize();
}
inline bool load(const string &filename) {
string filedata;
if(filedata.readfile(filename) == false) return false;
parse(filedata);
return true;
}
inline void sanitize() {
if(depth < 1) depth = 1;
if(depth > 24) depth = 24;
if(blockWidth < 1) blockWidth = 1;
if(blockHeight < 1) blockHeight = 1;
if(tileWidth < 1) tileWidth = 1;
if(tileHeight < 1) tileHeight = 1;
if(mosaicWidth < 1) mosaicWidth = 1;
if(mosaicHeight < 1) mosaicHeight = 1;
}
inline void reset() {
offset = 0;
width = 0;
height = 0;
count = 0;
endian = 1;
order = 0;
depth = 1;
blockWidth = 1;
blockHeight = 1;
blockStride = 0;
blockOffset = 0;
block.reset();
tileWidth = 1;
tileHeight = 1;
tileStride = 0;
tileOffset = 0;
tile.reset();
mosaicWidth = 1;
mosaicHeight = 1;
mosaicStride = 0;
mosaicOffset = 0;
mosaic.reset();
paddingWidth = 0;
paddingHeight = 0;
paddingColor = 0x000000;
palette.reset();
}
inline context() {
reset();
}
};
}
}
#endif

126
bsnes/nall/mosaic/parser.hpp Executable file
View File

@@ -0,0 +1,126 @@
#ifdef NALL_MOSAIC_INTERNAL_HPP
namespace nall {
namespace mosaic {
struct parser {
image canvas;
//export from bitstream to canvas
inline void load(bitstream &stream, uint64_t offset, context &ctx, unsigned width, unsigned height) {
canvas.allocate(width, height);
canvas.clear(ctx.paddingColor);
parse(1, stream, offset, ctx, width, height);
}
//import from canvas to bitstream
inline bool save(bitstream &stream, uint64_t offset, context &ctx) {
if(stream.readonly) return false;
parse(0, stream, offset, ctx, canvas.width, canvas.height);
return true;
}
inline parser() : canvas(0, 32, 0u, 255u << 16, 255u << 8, 255u << 0) {
}
private:
inline uint32_t read(unsigned x, unsigned y) const {
unsigned addr = y * canvas.width + x;
if(addr >= canvas.width * canvas.height) return 0u;
uint32_t *buffer = (uint32_t*)canvas.data;
return buffer[addr];
}
inline void write(unsigned x, unsigned y, uint32_t data) {
unsigned addr = y * canvas.width + x;
if(addr >= canvas.width * canvas.height) return;
uint32_t *buffer = (uint32_t*)canvas.data;
buffer[addr] = data;
}
inline void parse(bool load, bitstream &stream, uint64_t offset, context &ctx, unsigned width, unsigned height) {
stream.endian = ctx.endian;
unsigned canvasWidth = width / (ctx.mosaicWidth * ctx.tileWidth * ctx.blockWidth + ctx.paddingWidth);
unsigned canvasHeight = height / (ctx.mosaicHeight * ctx.tileHeight * ctx.blockHeight + ctx.paddingHeight);
unsigned bitsPerBlock = ctx.depth * ctx.blockWidth * ctx.blockHeight;
unsigned objectOffset = 0;
for(unsigned objectY = 0; objectY < canvasHeight; objectY++) {
for(unsigned objectX = 0; objectX < canvasWidth; objectX++) {
if(objectOffset >= ctx.count && ctx.count > 0) break;
unsigned objectIX = objectX * ctx.objectWidth();
unsigned objectIY = objectY * ctx.objectHeight();
objectOffset++;
unsigned mosaicOffset = 0;
for(unsigned mosaicY = 0; mosaicY < ctx.mosaicHeight; mosaicY++) {
for(unsigned mosaicX = 0; mosaicX < ctx.mosaicWidth; mosaicX++) {
unsigned mosaicData = ctx.mosaic(mosaicOffset, mosaicOffset);
unsigned mosaicIX = (mosaicData % ctx.mosaicWidth) * (ctx.tileWidth * ctx.blockWidth);
unsigned mosaicIY = (mosaicData / ctx.mosaicWidth) * (ctx.tileHeight * ctx.blockHeight);
mosaicOffset++;
unsigned tileOffset = 0;
for(unsigned tileY = 0; tileY < ctx.tileHeight; tileY++) {
for(unsigned tileX = 0; tileX < ctx.tileWidth; tileX++) {
unsigned tileData = ctx.tile(tileOffset, tileOffset);
unsigned tileIX = (tileData % ctx.tileWidth) * ctx.blockWidth;
unsigned tileIY = (tileData / ctx.tileWidth) * ctx.blockHeight;
tileOffset++;
unsigned blockOffset = 0;
for(unsigned blockY = 0; blockY < ctx.blockHeight; blockY++) {
for(unsigned blockX = 0; blockX < ctx.blockWidth; blockX++) {
if(load) {
unsigned palette = 0;
for(unsigned n = 0; n < ctx.depth; n++) {
unsigned index = blockOffset++;
if(ctx.order == 1) index = (index % ctx.depth) * ctx.blockWidth * ctx.blockHeight + (index / ctx.depth);
palette |= stream.read(offset + ctx.block(index, index)) << n;
}
write(
objectIX + mosaicIX + tileIX + blockX,
objectIY + mosaicIY + tileIY + blockY,
ctx.palette(palette, palette)
);
} else /* save */ {
uint32_t palette = read(
objectIX + mosaicIX + tileIX + blockX,
objectIY + mosaicIY + tileIY + blockY
);
for(unsigned n = 0; n < ctx.depth; n++) {
unsigned index = blockOffset++;
if(ctx.order == 1) index = (index % ctx.depth) * ctx.blockWidth * ctx.blockHeight + (index / ctx.depth);
stream.write(offset + ctx.block(index, index), palette & 1);
palette >>= 1;
}
}
} //blockX
} //blockY
offset += ctx.blockStride;
} //tileX
offset += ctx.blockOffset;
} //tileY
offset += ctx.tileStride;
} //mosaicX
offset += ctx.tileOffset;
} //mosaicY
offset += ctx.mosaicStride;
} //objectX
offset += ctx.mosaicOffset;
} //objectY
}
};
}
}
#endif

View File

@@ -64,8 +64,8 @@
#define mkdir(n, m) _wmkdir(nall::utf16_t(n))
#define putenv _putenv
#define rmdir _rmdir
#define usleep(n) Sleep(n / 1000)
#define vsnprintf _vsnprintf
inline void usleep(unsigned milliseconds) { Sleep(milliseconds / 1000); }
#endif
//================

View File

@@ -10,9 +10,12 @@
namespace nall {
struct png {
uint32_t *data;
unsigned size;
//colorType:
//0 = L
//2 = R,G,B
//3 = P
//4 = L,A
//6 = R,G,B,A
struct Info {
unsigned width;
unsigned height;
@@ -28,13 +31,14 @@ struct png {
uint8_t palette[256][3];
} info;
uint8_t *rawData;
unsigned rawSize;
uint8_t *data;
unsigned size;
inline bool decode(const string &filename);
inline bool decode(const uint8_t *sourceData, unsigned sourceSize);
inline void transform();
inline void alphaTransform(uint32_t rgb = 0xffffff);
inline unsigned readbits(const uint8_t *&data);
unsigned bitpos;
inline png();
inline ~png();
@@ -46,16 +50,11 @@ protected:
IEND = 0x49454e44,
};
unsigned bitpos;
inline unsigned interlace(unsigned pass, unsigned index);
inline unsigned inflateSize();
inline bool deinterlace(const uint8_t *&inputData, unsigned pass);
inline bool filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height);
inline unsigned read(const uint8_t *data, unsigned length);
inline unsigned decode(const uint8_t *&data);
inline unsigned readbits(const uint8_t *&data);
inline unsigned scale(unsigned n);
};
bool png::decode(const string &filename) {
@@ -146,14 +145,14 @@ bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
return false;
}
rawSize = info.width * info.height * info.bytesPerPixel;
rawData = new uint8_t[rawSize];
size = info.width * info.height * info.bytesPerPixel;
data = new uint8_t[size];
if(info.interlaceMethod == 0) {
if(filter(rawData, interlacedData, info.width, info.height) == false) {
if(filter(data, interlacedData, info.width, info.height) == false) {
delete[] interlacedData;
delete[] rawData;
rawData = 0;
delete[] data;
data = 0;
return false;
}
} else {
@@ -161,8 +160,8 @@ bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
for(unsigned pass = 0; pass < 7; pass++) {
if(deinterlace(passData, pass) == false) {
delete[] interlacedData;
delete[] rawData;
rawData = 0;
delete[] data;
data = 0;
return false;
}
}
@@ -216,7 +215,7 @@ bool png::deinterlace(const uint8_t *&inputData, unsigned pass) {
const uint8_t *rd = outputData;
for(unsigned y = yo; y < info.height; y += yd) {
uint8_t *wr = rawData + y * info.pitch;
uint8_t *wr = data + y * info.pitch;
for(unsigned x = xo; x < info.width; x += xd) {
for(unsigned b = 0; b < info.bytesPerPixel; b++) {
wr[x * info.bytesPerPixel + b] = *rd++;
@@ -298,42 +297,6 @@ unsigned png::read(const uint8_t *data, unsigned length) {
return result;
}
unsigned png::decode(const uint8_t *&data) {
unsigned p, r, g, b, a;
switch(info.colorType) {
case 0: //L
r = g = b = scale(readbits(data));
a = 0xff;
break;
case 2: //R,G,B
r = scale(readbits(data));
g = scale(readbits(data));
b = scale(readbits(data));
a = 0xff;
break;
case 3: //P
p = readbits(data);
r = info.palette[p][0];
g = info.palette[p][1];
b = info.palette[p][2];
a = 0xff;
break;
case 4: //L,A
r = g = b = scale(readbits(data));
a = scale(readbits(data));
break;
case 6: //R,G,B,A
r = scale(readbits(data));
g = scale(readbits(data));
b = scale(readbits(data));
a = scale(readbits(data));
break;
}
return (a << 24) | (r << 16) | (g << 8) | (b << 0);
}
unsigned png::readbits(const uint8_t *&data) {
unsigned result = 0;
switch(info.bitDepth) {
@@ -363,62 +326,12 @@ unsigned png::readbits(const uint8_t *&data) {
return result;
}
unsigned png::scale(unsigned n) {
switch(info.bitDepth) {
case 1: return n ? 0xff : 0x00;
case 2: return n * 0x55;
case 4: return n * 0x11;
case 8: return n;
case 16: return n >> 8;
}
return 0;
}
void png::transform() {
if(data) delete[] data;
data = new uint32_t[info.width * info.height];
png::png() : data(nullptr) {
bitpos = 0;
const uint8_t *rd = rawData;
for(unsigned y = 0; y < info.height; y++) {
uint32_t *wr = data + y * info.width;
for(unsigned x = 0; x < info.width; x++) {
wr[x] = decode(rd);
}
}
}
void png::alphaTransform(uint32_t rgb) {
transform();
uint8_t ir = rgb >> 16;
uint8_t ig = rgb >> 8;
uint8_t ib = rgb >> 0;
uint32_t *p = data;
for(unsigned y = 0; y < info.height; y++) {
for(unsigned x = 0; x < info.width; x++) {
uint32_t pixel = *p;
uint8_t a = pixel >> 24;
uint8_t r = pixel >> 16;
uint8_t g = pixel >> 8;
uint8_t b = pixel >> 0;
r = (r * a) + (ir * (255 - a)) >> 8;
g = (g * a) + (ig * (255 - a)) >> 8;
b = (b * a) + (ib * (255 - a)) >> 8;
*p++ = (255 << 24) | (r << 16) | (g << 8) | (b << 0);
}
}
}
png::png() : data(nullptr), rawData(nullptr) {
}
png::~png() {
if(data) delete[] data;
if(rawData) delete[] rawData;
}
}

View File

@@ -7,9 +7,11 @@
namespace nall {
template<typename T> struct reference_array {
struct exception_out_of_bounds{};
protected:
typedef typename std::remove_reference<T>::type *Tptr;
Tptr *pool;
typedef typename std::remove_reference<T>::type type_t;
type_t **pool;
unsigned poolsize, buffersize;
public:
@@ -26,7 +28,7 @@ namespace nall {
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
pool = (Tptr*)realloc(pool, newsize * sizeof(T));
pool = (type_t**)realloc(pool, sizeof(type_t*) * newsize);
poolsize = newsize;
buffersize = min(buffersize, newsize);
}
@@ -36,7 +38,14 @@ namespace nall {
buffersize = newsize;
}
bool append(const T data) {
template<typename... Args>
bool append(type_t& data, Args&&... args) {
bool result = append(data);
append(std::forward<Args>(args)...);
return result;
}
bool append(type_t& data) {
for(unsigned index = 0; index < buffersize; index++) {
if(pool[index] == &data) return false;
}
@@ -47,7 +56,7 @@ namespace nall {
return true;
}
bool remove(const T data) {
bool remove(type_t& data) {
for(unsigned index = 0; index < buffersize; index++) {
if(pool[index] == &data) {
for(unsigned i = index; i < buffersize - 1; i++) pool[i] = pool[i + 1];
@@ -70,8 +79,8 @@ namespace nall {
if(pool) free(pool);
buffersize = source.buffersize;
poolsize = source.poolsize;
pool = (Tptr*)malloc(sizeof(T) * poolsize);
memcpy(pool, source.pool, sizeof(T) * buffersize);
pool = (type_t**)malloc(sizeof(type_t*) * poolsize);
memcpy(pool, source.pool, sizeof(type_t*) * buffersize);
return *this;
}
@@ -85,20 +94,20 @@ namespace nall {
return *this;
}
inline T operator[](unsigned index) {
if(index >= buffersize) throw "reference_array[] out of bounds";
inline type_t& operator[](unsigned index) {
if(index >= buffersize) throw exception_out_of_bounds();
return *pool[index];
}
inline const T operator[](unsigned index) const {
if(index >= buffersize) throw "reference_array[] out of bounds";
inline type_t& operator[](unsigned index) const {
if(index >= buffersize) throw exception_out_of_bounds();
return *pool[index];
}
//iteration
struct iterator {
bool operator!=(const iterator &source) const { return index != source.index; }
T& operator*() { return array.operator[](index); }
type_t& operator*() { return array.operator[](index); }
iterator& operator++() { index++; return *this; }
iterator(const reference_array &array, unsigned index) : array(array), index(index) {}
private:

View File

@@ -1,61 +0,0 @@
#ifndef NALL_RESOURCE_HPP
#define NALL_RESOURCE_HPP
#include <nall/file.hpp>
#include <nall/zip.hpp>
namespace nall {
struct resource {
//create resource with "zip -9 resource.zip resource"
static bool encode(const char *outputFilename, const char *inputFilename) {
file fp;
if(fp.open(inputFilename, file::mode::read) == false) return false;
unsigned size = fp.size();
uint8_t *data = new uint8_t[size];
fp.read(data, size);
fp.close();
fp.open(outputFilename, file::mode::write);
fp.print("static const uint8_t data[", size, "] = {\n");
uint8_t *p = data;
while(size) {
fp.print(" ");
for(unsigned n = 0; n < 32 && size; n++, size--) {
fp.print((unsigned)*p++, ",");
}
fp.print("\n");
}
fp.print("};\n");
fp.close();
delete[] data;
}
uint8_t *data;
unsigned size;
//extract first file from ZIP archive
bool decode(const uint8_t *cdata, unsigned csize) {
if(data) delete[] data;
zip archive;
if(archive.open(cdata, csize) == false) return false;
if(archive.file.size() == 0) return false;
bool result = archive.extract(archive.file[0], data, size);
archive.close();
return result;
}
resource() : data(0), size(0) {
}
~resource() {
if(data) delete[] data;
}
};
}
#endif

View File

@@ -55,10 +55,10 @@ namespace nall {
template<typename T> void integer(T &value) {
enum { size = std::is_same<bool, T>::value ? 1 : sizeof(T) };
if(imode == Save) {
for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3);
for(unsigned n = 0; n < size; n++) idata[isize++] = (uintmax_t)value >> (n << 3);
} else if(imode == Load) {
value = 0;
for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3);
for(unsigned n = 0; n < size; n++) value |= (uintmax_t)idata[isize++] << (n << 3);
} else if(imode == Size) {
isize += size;
}

View File

@@ -105,345 +105,439 @@ public:
bool has_st018;
};
#define T "\t"
SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
read_header(data, size);
string xml;
markup = "";
markup = "<?xml version='1.0' encoding='UTF-8'?>\n";
if(type == TypeBsx) {
markup.append("cartridge");
markup.append("<cartridge/>\n");
return;
}
if(type == TypeSufamiTurbo) {
markup.append("cartridge");
markup.append("<cartridge/>\n");
return;
}
if(type == TypeGameBoy) {
markup.append("cartridge rtc=", gameboy_has_rtc(data, size), "\n");
markup.append("<cartridge rtc='", gameboy_has_rtc(data, size), "'\n");
if(gameboy_ram_size(data, size) > 0) {
markup.append(T "ram size=0x", hex(gameboy_ram_size(data, size)), "\n");
markup.append(" <ram size='0x", hex(gameboy_ram_size(data, size)), "'>\n");
}
markup.append("</cartridge>\n");
return;
}
markup.append("cartridge region=", region == NTSC ? "NTSC\n" : "PAL\n");
const char *range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff";
markup.append("<cartridge region='", region == NTSC ? "NTSC" : "PAL", "'>\n");
if(type == TypeSuperGameBoy1Bios) {
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-7f:8000-ffff\n");
markup.append(T T "map mode=linear address=80-ff:8000-ffff\n");
markup.append(T "icd2 revision=1\n");
markup.append(T T "map address=00-3f:6000-7fff\n");
markup.append(T T "map address=80-bf:6000-7fff\n");
} else if(type == TypeSuperGameBoy2Bios) {
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-7f:8000-ffff\n");
markup.append(T T "map mode=linear address=80-ff:8000-ffff\n");
markup.append(T "icd2 revision=1\n");
markup.append(T T "map address=00-3f:6000-7fff\n");
markup.append(T T "map address=80-bf:6000-7fff\n");
} else if(has_cx4) {
markup.append(T "hitachidsp model=HG51B169 frequency=20000000 firmware=cx4.bin sha256=ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18\n");
markup.append(T T "rom\n");
markup.append(T T T "map mode=linear address=00-7f:8000-ffff\n");
markup.append(T T T "map mode=linear address=80-ff:8000-ffff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:6000-7fff\n");
markup.append(T T T "map address=80-bf:6000-7fff\n");
} else if(has_spc7110) {
markup.append(T "rom\n");
markup.append(T T "map mode=shadow address=00-0f:8000-ffff\n");
markup.append(T T "map mode=shadow address=80-bf:8000-ffff\n");
markup.append(T T "map mode=linear address=c0-cf:0000-ffff\n");
if(type == TypeSuperGameBoy1Bios || type == TypeSuperGameBoy2Bios) markup.append(
" <rom>\n"
" <map mode='linear' address='00-7f:8000-ffff'/>\n"
" <map mode='linear' address='80-ff:8000-ffff'/>\n"
" </rom>\n"
" <icd2 revision='1'>\n"
" <map address='00-3f:6000-7fff'/>\n"
" <map address='80-bf:6000-7fff'/>\n"
" </icd2>\n"
);
markup.append(T "spc7110\n");
markup.append(T T "mcu\n");
markup.append(T T T "map address=d0-ff:0000-ffff offset=0x100000 size=0x", hex(size - 0x100000), "\n");
markup.append(T T "ram size=0x", hex(ram_size), "\n");
markup.append(T T T "map mode=linear address=00:6000-7fff\n");
markup.append(T T T "map mode=linear address=30:6000-7fff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:4800-483f\n");
markup.append(T T T "map address=80-bf:4800-483f\n");
if(has_spc7110rtc) {
markup.append(T T "rtc\n");
markup.append(T T T "map address=00-3f:4840-4842\n");
markup.append(T T T "map address=80-bf:4840-4842\n");
}
markup.append(T T "dcu\n");
markup.append(T T T "map address=50:0000-ffff\n");
} else if(mapper == LoROM) {
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-7f:8000-ffff\n");
markup.append(T T "map mode=linear address=80-ff:8000-ffff\n");
else if(has_cx4) markup.append(
" <hitachidsp model='HG51B169' frequency='20000000' firmware='cx4.rom' sha256='ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18'>\n"
" <rom>\n"
" <map mode='linear' address='00-7f:8000-ffff'/>\n"
" <map mode='linear' address='80-ff:8000-ffff'/>\n"
" </rom>\n"
" <mmio>\n"
" <map address='00-3f:6000-7fff'/>\n"
" <map address='80-bf:6000-7fff'/>\n"
" </mmio>\n"
" </hitachidsp>\n"
);
if(ram_size > 0) {
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
markup.append(T T "map mode=linear address=f0-ff:0000-7fff\n");
} else {
markup.append(T T "map mode=linear address=70-7f:0000-ffff\n");
markup.append(T T "map mode=linear address=f0-ff:0000-ffff\n");
}
}
} else if(mapper == HiROM) {
markup.append(T "rom\n");
markup.append(T T "map mode=shadow address=00-3f:8000-ffff\n");
markup.append(T T "map mode=linear address=40-7f:0000-ffff\n");
markup.append(T T "map mode=shadow address=80-bf:8000-ffff\n");
markup.append(T T "map mode=linear address=c0-ff:0000-ffff\n");
if(ram_size > 0) {
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
} else {
markup.append(T T "map mode=linear address=70-7f:0000-ffff\n");
}
}
} else if(mapper == ExLoROM) {
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-3f:8000-ffff\n");
markup.append(T T "map mode=linear address=40-7f:0000-ffff\n");
markup.append(T T "map mode=linear address=80-bf:8000-ffff\n");
if(ram_size > 0) {
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
}
} else if(mapper == ExHiROM) {
markup.append(T "rom\n");
markup.append(T T "map mode=shadow address=00-3f:8000-ffff offset=0x400000\n");
markup.append(T T "map mode=linear address=40-7f:0000-ffff offset=0x400000\n");
markup.append(T T "map mode=shadow address=80-bf:8000-ffff offset=0x000000\n");
markup.append(T T "map mode=linear address=c0-ff:0000-ffff offset=0x000000\n");
if(ram_size > 0) {
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
} else {
markup.append(T T "map mode=linear address=70-7f:0000-ffff\n");
}
}
} else if(mapper == SuperFXROM) {
markup.append(T "superfx revision=2\n");
markup.append(T T "rom\n");
markup.append(T T T "map mode=linear address=00-3f:8000-ffff\n");
markup.append(T T T "map mode=linear address=40-5f:0000-ffff\n");
markup.append(T T T "map mode=linear address=80-bf:8000-ffff\n");
markup.append(T T T "map mode=linear address=c0-df:0000-ffff\n");
markup.append(T T "ram size=0x", hex(ram_size), "\n");
markup.append(T T T "map mode=linear address=00-3f:6000-7fff size=0x2000\n");
markup.append(T T T "map mode=linear address=60-7f:0000-ffff\n");
markup.append(T T T "map mode=linear address=80-bf:6000-7fff size=0x2000\n");
markup.append(T T T "map mode=linear address=e0-ff:0000-ffff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:3000-32ff\n");
markup.append(T T T "map address=80-bf:3000-32ff\n");
} else if(mapper == SA1ROM) {
markup.append(T "sa1\n");
markup.append(T T "mcu\n");
markup.append(T T T "rom\n");
markup.append(T T T T "map mode=direct address=00-3f:8000-ffff\n");
markup.append(T T T T "map mode=direct address=80-bf:8000-ffff\n");
markup.append(T T T T "map mode=direct address=c0-ff:0000-ffff\n");
markup.append(T T T "ram\n");
markup.append(T T T T "map mode=direct address=00-3f:6000-7fff\n");
markup.append(T T T T "map mode=direct address=80-bf:6000-7fff\n");
markup.append(T T "iram size=0x800\n");
markup.append(T T T "map mode=linear address=00-3f:3000-37ff\n");
markup.append(T T T "map mode=linear address=80-bf:3000-37ff\n");
markup.append(T T "bwram size=0x", hex(ram_size), "\n");
markup.append(T T T "map mode=linear address=40-4f:0000-ffff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:2200-23ff\n");
markup.append(T T T "map address=80-bf:2200-23ff\n");
} else if(mapper == BSCLoROM) {
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-1f:8000-ffff offset=0x000000\n");
markup.append(T T "map mode=linear address=20-3f:8000-ffff offset=0x100000\n");
markup.append(T T "map mode=linear address=80-9f:8000-ffff offset=0x200000\n");
markup.append(T T "map mode=linear address=a0-bf:8000-ffff offset=0x100000\n");
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
markup.append(T T "map mode=linear address=f0-ff:0000-7fff\n");
markup.append(T "bsx\n");
markup.append(T T "slot\n");
markup.append(T T T "map mode=linear address=c0-ef:0000-ffff\n");
} else if(mapper == BSCHiROM) {
markup.append(T "rom\n");
markup.append(T T "map mode=shadow address=00-1f:8000-ffff\n");
markup.append(T T "map mode=linear address=40-5f:0000-ffff\n");
markup.append(T T "map mode=shadow address=80-9f:8000-ffff\n");
markup.append(T T "map mode=linear address=c0-df:0000-ffff\n");
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
markup.append(T "bsx\n");
markup.append(T T "slot\n");
markup.append(T T T "map mode=shadow address=20-3f:8000-ffff\n");
markup.append(T T T "map mode=linear address=60-7f:0000-ffff\n");
markup.append(T T T "map mode=shadow address=a0-bf:8000-ffff\n");
markup.append(T T T "map mode=linear address=e0-ff:0000-ffff\n");
} else if(mapper == BSXROM) {
markup.append(T "bsx\n");
markup.append(T T "mcu\n");
markup.append(T T T "map address=00-3f:8000-ffff\n");
markup.append(T T T "map address=80-bf:8000-ffff\n");
markup.append(T T T "map address=40-7f:0000-ffff\n");
markup.append(T T T "map address=c0-ff:0000-ffff\n");
markup.append(T T T "map address=20-3f:6000-7fff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:5000-5fff\n");
markup.append(T T T "map address=80-bf:5000-5fff\n");
} else if(mapper == STROM) {
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-1f:8000-ffff\n");
markup.append(T T "map mode=linear address=80-9f:8000-ffff\n");
markup.append(T "sufamiturbo\n");
markup.append(T T "slot id=A\n");
markup.append(T T T "rom\n");
markup.append(T T T T "map mode=linear address=20-3f:8000-ffff\n");
markup.append(T T T T "map mode=linear address=a0-bf:8000-ffff\n");
markup.append(T T T "ram size=0x20000\n");
markup.append(T T T T "map mode=linear address=60-63:8000-ffff\n");
markup.append(T T T T "map mode=linear address=e0-e3:8000-ffff\n");
markup.append(T T "slot id=B\n");
markup.append(T T T "rom\n");
markup.append(T T T T "map mode=linear address=40-5f:8000-ffff\n");
markup.append(T T T T "map mode=linear address=c0-df:8000-ffff\n");
markup.append(T T T "ram size=0x20000\n");
markup.append(T T T T "map mode=linear address=70-73:8000-ffff\n");
markup.append(T T T T "map mode=linear address=f0-f3:8000-ffff\n");
else if(has_spc7110) {
markup.append(
" <rom>\n"
" <map mode='shadow' address='00-0f:8000-ffff'/>\n"
" <map mode='shadow' address='80-bf:8000-ffff'/>\n"
" <map mode='linear' address='c0-cf:0000-ffff'/>\n"
" </rom>\n"
" <spc7110>\n"
" <ram size='0x", hex(ram_size), "'>\n"
" <map mode='linear' address='00:6000-7fff'/>\n"
" <map mode='linear' address='30:6000-7fff'/>\n"
" </ram>\n"
" <mmio>\n"
" <map address='00-3f:4800-483f'/>\n"
" <map address='80-bf:4800-483f'/>\n"
" </mmio>\n"
" <mcu>\n"
" <map address='d0-ff:0000-ffff' offset='0x100000' size='0x", hex(size - 0x100000), "'/>\n"
" </mcu>\n"
" <dcu>\n"
" <map address='50:0000-ffff'/>\n"
" </dcu>\n"
);
if(has_spc7110rtc) markup.append(
" <rtc>\n"
" <map address='00-3f:4840-4842'/>\n"
" <map address='80-bf:4840-4842'/>\n"
" </rtc>\n"
);
markup.append(
" </spc7110>\n"
);
}
if(has_srtc) {
markup.append(T "srtc\n");
markup.append(T T "map address=00-3f:2800-2801\n");
markup.append(T T "map address=80-bf:2800-2801\n");
else if(mapper == LoROM) {
markup.append(
" <rom>\n"
" <map mode='linear' address='00-7f:8000-ffff'/>\n"
" <map mode='linear' address='80-ff:8000-ffff'/>\n"
" </rom>\n"
);
if(ram_size > 0) markup.append(
" <ram size='0x", hex(ram_size), "'>\n"
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
" <map mode='linear' address='70-7f:", range, "'/>\n"
" <map mode='linear' address='f0-ff:", range, "'/>\n"
" </ram>\n"
);
}
if(has_sdd1) {
markup.append(T "sdd1\n");
markup.append(T T "mcu\n");
markup.append(T T T "map address=c0-ff:0000-ffff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:4800-4807\n");
markup.append(T T T "map address=80-bf:4800-4807\n");
else if(mapper == HiROM) {
markup.append(
" <rom>\n"
" <map mode='shadow' address='00-3f:8000-ffff'/>\n"
" <map mode='linear' address='40-7f:0000-ffff'/>\n"
" <map mode='shadow' address='80-bf:8000-ffff'/>\n"
" <map mode='linear' address='c0-ff:0000-ffff'/>\n"
" </rom>\n"
);
if(ram_size > 0) markup.append(
" <ram size='0x", hex(ram_size), "'>\n"
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
" <map mode='linear' address='70-7f:", range, "'/>\n"
" </ram>\n"
);
}
else if(mapper == ExLoROM) {
markup.append(
" <rom>\n"
" <map mode='linear' address='00-3f:8000-ffff'/>\n"
" <map mode='linear' address='40-7f:0000-ffff'/>\n"
" <map mode='linear' address='80-bf:8000-ffff'/>\n"
" </rom>\n"
);
if(ram_size > 0) markup.append(
" <ram size='0x", hex(ram_size), "'>\n"
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
" <map mode='linear' address='70-7f:0000-7fff'/>\n"
" </ram>\n"
);
}
else if(mapper == ExHiROM) {
markup.append(
" <rom>\n"
" <map mode='shadow' address='00-3f:8000-ffff' offset='0x400000'/>\n"
" <map mode='linear' address='40-7f:0000-ffff' offset='0x400000'/>\n"
" <map mode='shadow' address='80-bf:8000-ffff' offset='0x000000'/>\n"
" <map mode='linear' address='c0-ff:0000-ffff' offset='0x000000'/>\n"
" </rom>\n"
);
if(ram_size > 0) markup.append(
" <ram size='0x", hex(ram_size), "'>\n"
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
" <map mode='linear' address='70-7f:", range, "'/>\n"
" </ram>\n"
);
}
else if(mapper == SuperFXROM) markup.append(
" <superfx revision='2'>\n"
" <rom>\n"
" <map mode='linear' address='00-3f:8000-ffff'/>\n"
" <map mode='linear' address='40-5f:0000-ffff'/>\n"
" <map mode='linear' address='80-bf:8000-ffff'/>\n"
" <map mode='linear' address='c0-df:0000-ffff'/>\n"
" </rom>\n"
" <ram size='0x", hex(ram_size), "'>\n"
" <map mode='linear' address='00-3f:6000-7fff' size='0x2000'/>\n"
" <map mode='linear' address='60-7f:0000-ffff'/>\n"
" <map mode='linear' address='80-bf:6000-7fff' size='0x2000'/>\n"
" <map mode='linear' address='e0-ff:0000-ffff'/>\n"
" </ram>\n"
" <mmio>\n"
" <map address='00-3f:3000-32ff'/>\n"
" <map address='80-bf:3000-32ff'/>\n"
" </mmio>\n"
" </superfx>\n"
);
else if(mapper == SA1ROM) markup.append(
" <sa1>\n"
" <mcu>\n"
" <rom>\n"
" <map mode='direct' address='00-3f:8000-ffff'/>\n"
" <map mode='direct' address='80-bf:8000-ffff'/>\n"
" <map mode='direct' address='c0-ff:0000-ffff'/>\n"
" </rom>\n"
" <ram>\n"
" <map mode='direct' address='00-3f:6000-7fff'/>\n"
" <map mode='direct' address='80-bf:6000-7fff'/>\n"
" </ram>\n"
" </mcu>\n"
" <iram size='0x800'>\n"
" <map mode='linear' address='00-3f:3000-37ff'/>\n"
" <map mode='linear' address='80-bf:3000-37ff'/>\n"
" </iram>\n"
" <bwram size='0x", hex(ram_size), "'>\n"
" <map mode='linear' address='40-4f:0000-ffff'/>\n"
" </bwram>\n"
" <mmio>\n"
" <map address='00-3f:2200-23ff'/>\n"
" <map address='80-bf:2200-23ff'/>\n"
" </mmio>\n"
" </sa1>\n"
);
else if(mapper == BSCLoROM) markup.append(
" <rom>\n"
" <map mode='linear' address='00-1f:8000-ffff' offset='0x000000'/>\n"
" <map mode='linear' address='20-3f:8000-ffff' offset='0x100000'/>\n"
" <map mode='linear' address='80-9f:8000-ffff' offset='0x200000'/>\n"
" <map mode='linear' address='a0-bf:8000-ffff' offset='0x100000'/>\n"
" </rom>\n"
" <ram size='0x", hex(ram_size), "'>\n"
" <map mode='linear' address='70-7f:0000-7fff'/>\n"
" <map mode='linear' address='f0-ff:0000-7fff'/>\n"
" </ram>\n"
" <bsx>\n"
" <slot>\n"
" <map mode='linear' address='c0-ef:0000-ffff'/>\n"
" </slot>\n"
" </bsx>\n"
);
else if(mapper == BSCHiROM) markup.append(
" <rom>\n"
" <map mode='shadow' address='00-1f:8000-ffff'/>\n"
" <map mode='linear' address='40-5f:0000-ffff'/>\n"
" <map mode='shadow' address='80-9f:8000-ffff'/>\n"
" <map mode='linear' address='c0-df:0000-ffff'/>\n"
" </rom>\n"
" <ram size='0x", hex(ram_size), "'>\n"
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
" </ram>\n"
" <bsx>\n"
" <slot>\n"
" <map mode='shadow' address='20-3f:8000-ffff'/>\n"
" <map mode='linear' address='60-7f:0000-ffff'/>\n"
" <map mode='shadow' address='a0-bf:8000-ffff'/>\n"
" <map mode='linear' address='e0-ff:0000-ffff'/>\n"
" </slot>\n"
" </bsx>\n"
);
else if(mapper == BSXROM) markup.append(
" <bsx>\n"
" <mcu>\n"
" <map address='00-3f:8000-ffff'/>\n"
" <map address='80-bf:8000-ffff'/>\n"
" <map address='40-7f:0000-ffff'/>\n"
" <map address='c0-ff:0000-ffff'/>\n"
" <map address='20-3f:6000-7fff'/>\n"
" </mcu>\n"
" <mmio>\n"
" <map address='00-3f:5000-5fff'/>\n"
" <map address='80-bf:5000-5fff'/>\n"
" </mmio>\n"
" </bsx>\n"
);
else if(mapper == STROM) markup.append(
" <rom>\n"
" <map mode='linear' address='00-1f:8000-ffff'/>\n"
" <map mode='linear' address='80-9f:8000-ffff'/>\n"
" </rom>\n"
" <sufamiturbo>\n"
" <slot id='A'>\n"
" <rom>\n"
" <map mode='linear' address='20-3f:8000-ffff'/>\n"
" <map mode='linear' address='a0-bf:8000-ffff'/>\n"
" </rom>\n"
" <ram size='0x20000'>\n"
" <map mode='linear' address='60-63:8000-ffff'/>\n"
" <map mode='linear' address='e0-e3:8000-ffff'/>\n"
" </ram>\n"
" </slot>\n"
" <slot id='B'>\n"
" <rom>\n"
" <map mode='linear' address='40-5f:8000-ffff'/>\n"
" <map mode='linear' address='c0-df:8000-ffff'/>\n"
" </rom>\n"
" <ram size='0x20000'>\n"
" <map mode='linear' address='70-73:8000-ffff'/>\n"
" <map mode='linear' address='f0-f3:8000-ffff'/>\n"
" </ram>\n"
" </slot>\n"
" </sufamiturbo>\n"
);
if(has_srtc) markup.append(
" <srtc>\n"
" <map address='00-3f:2800-2801'/>\n"
" <map address='80-bf:2800-2801'/>\n"
" </srtc>\n"
);
if(has_sdd1) markup.append(
" <sdd1>\n"
" <mcu>\n"
" <map address='c0-ff:0000-ffff'/>\n"
" </mcu>\n"
" <mmio>\n"
" <map address='00-3f:4800-4807'/>\n"
" <map address='80-bf:4800-4807'/>\n"
" </mmio>\n"
" </sdd1>\n"
);
if(has_obc1) markup.append(
" <obc1>\n"
" <map address='00-3f:6000-7fff'/>\n"
" <map address='80-bf:6000-7fff'/>\n"
" </obc1>\n"
);
if(has_dsp1) {
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp1b.bin sha256=4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c\n");
if(dsp1_mapper == DSP1LoROM1MB) {
markup.append(T T "dr\n");
markup.append(T T T "map address=20-3f:8000-bfff\n");
markup.append(T T T "map address=a0-bf:8000-bfff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=20-3f:c000-ffff\n");
markup.append(T T T "map address=a0-bf:c000-ffff\n");
} else if(dsp1_mapper == DSP1LoROM2MB) {
markup.append(T T "dr\n");
markup.append(T T T "map address=60-6f:0000-3fff\n");
markup.append(T T T "map address=e0-ef:0000-3fff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=60-6f:4000-7fff\n");
markup.append(T T T "map address=e0-ef:4000-7fff\n");
} else if(dsp1_mapper == DSP1HiROM) {
markup.append(T T "dr\n");
markup.append(T T T "map address=00-1f:6000-6fff\n");
markup.append(T T T "map address=80-9f:6000-6fff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=00-1f:7000-7fff\n");
markup.append(T T T "map address=80-9f:7000-7fff\n");
}
//91e87d11e1c30d172556bed2211cce2efa94ba595f58c5d264809ef4d363a97b dsp1.rom
markup.append(" <necdsp model='uPD7725' frequency='8000000' firmware='dsp1b.rom' sha256='d789cb3c36b05c0b23b6c6f23be7aa37c6e78b6ee9ceac8d2d2aa9d8c4d35fa9'>\n");
if(dsp1_mapper == DSP1LoROM1MB) markup.append(
" <dr>\n"
" <map address='20-3f:8000-bfff'/>\n"
" <map address='a0-bf:8000-bfff'/>\n"
" </dr>\n"
" <sr>\n"
" <map address='20-3f:c000-ffff'/>\n"
" <map address='a0-bf:c000-ffff'/>\n"
" </sr>\n"
);
if(dsp1_mapper == DSP1LoROM2MB) markup.append(
" <dr>\n"
" <map address='60-6f:0000-3fff'/>\n"
" <map address='e0-ef:0000-3fff'/>\n"
" </dr>\n"
" <sr>\n"
" <map address='60-6f:4000-7fff'/>\n"
" <map address='e0-ef:4000-7fff'/>\n"
" </sr>\n"
);
if(dsp1_mapper == DSP1HiROM) markup.append(
" <dr>\n"
" <map address='00-1f:6000-6fff'/>\n"
" <map address='80-9f:6000-6fff'/>\n"
" </dr>\n"
" <sr>\n"
" <map address='00-1f:7000-7fff'/>\n"
" <map address='80-9f:7000-7fff'/>\n"
" </sr>\n"
);
markup.append(" </necdsp>\n");
}
if(has_dsp2) {
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp2.bin sha256=5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1\n");
markup.append(T T "dr\n");
markup.append(T T T "map address=20-3f:8000-bfff\n");
markup.append(T T T "map address=a0-bf:8000-bfff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=20-3f:c000-ffff\n");
markup.append(T T T "map address=a0-bf:c000-ffff\n");
}
if(has_dsp2) markup.append(
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp2.rom' sha256='03ef4ef26c9f701346708cb5d07847b5203cf1b0818bf2930acd34510ffdd717'>\n"
" <dr>\n"
" <map address='20-3f:8000-bfff'/>\n"
" <map address='a0-bf:8000-bfff'/>\n"
" </dr>\n"
" <sr>\n"
" <map address='20-3f:c000-ffff'/>\n"
" <map address='a0-bf:c000-ffff'/>\n"
" </sr>\n"
" </necdsp>\n"
);
if(has_dsp3) {
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp3.bin sha256=2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90\n");
markup.append(T T "dr\n");
markup.append(T T T "map address=20-3f:8000-bfff\n");
markup.append(T T T "map address=a0-bf:8000-bfff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=20-3f:c000-ffff\n");
markup.append(T T T "map address=a0-bf:c000-ffff\n");
}
if(has_dsp3) markup.append(
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp3.rom' sha256='0971b08f396c32e61989d1067dddf8e4b14649d548b2188f7c541b03d7c69e4e'>\n"
" <dr>\n"
" <map address='20-3f:8000-bfff'/>\n"
" <map address='a0-bf:8000-bfff'/>\n"
" </dr>\n"
" <sr>\n"
" <map address='20-3f:c000-ffff'/>\n"
" <map address='a0-bf:c000-ffff'/>\n"
" </sr>\n"
" </necdsp>\n"
);
if(has_dsp4) {
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp4.bin sha256=63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a\n");
markup.append(T T "dr\n");
markup.append(T T T "map address=30-3f:8000-bfff\n");
markup.append(T T T "map address=b0-bf:8000-bfff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=30-3f:c000-ffff\n");
markup.append(T T T "map address=b0-bf:c000-ffff\n");
}
if(has_dsp4) markup.append(
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp4.rom' sha256='752d03b2d74441e430b7f713001fa241f8bbcfc1a0d890ed4143f174dbe031da'>\n"
" <dr>\n"
" <map address='30-3f:8000-bfff'/>\n"
" <map address='b0-bf:8000-bfff'/>\n"
" </dr>\n"
" <sr>\n"
" <map address='30-3f:c000-ffff'/>\n"
" <map address='b0-bf:c000-ffff'/>\n"
" </sr>\n"
" </necdsp>\n"
);
if(has_obc1) {
markup.append(T "obc1\n");
markup.append(T T "map address=00-3f:6000-7fff\n");
markup.append(T T "map address=80-bf:6000-7fff\n");
}
if(has_st010) markup.append(
" <necdsp model='uPD96050' frequency='10000000' firmware='st010.rom' sha256='fa9bced838fedea11c6f6ace33d1878024bdd0d02cc9485899d0bdd4015ec24c'>\n"
" <dr>\n"
" <map address='60:0000'/>\n"
" <map address='e0:0000'/>\n"
" </dr>\n"
" <sr>\n"
" <map address='60:0001'/>\n"
" <map address='e0:0001'/>\n"
" </sr>\n"
" <dp>\n"
" <map address='68-6f:0000-0fff'/>\n"
" <map address='e8-ef:0000-0fff'/>\n"
" </dp>\n"
" </necdsp>\n"
);
if(has_st010) {
markup.append(T "necdsp model=uPD96050 frequency=10000000 firmware=st0010.bin sha256=55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e\n");
markup.append(T T "dr\n");
markup.append(T T T "map address=60:0000\n");
markup.append(T T T "map address=e0:0000\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=60:0001\n");
markup.append(T T T "map address=e0:0001\n");
markup.append(T T "dp\n");
markup.append(T T T "map address=68-6f:0000-0fff\n");
markup.append(T T T "map address=e8-ef:0000-0fff\n");
}
if(has_st011) markup.append(
" <necdsp model='uPD96050' frequency='15000000' firmware='st011.rom' sha256='8b2b3f3f3e6e29f4d21d8bc736b400bc988b7d2214ebee15643f01c1fee2f364'>\n"
" <dr>\n"
" <map address='60:0000'/>\n"
" <map address='e0:0000'/>\n"
" </dr>\n"
" <sr>\n"
" <map address='60:0001'/>\n"
" <map address='e0:0001'/>\n"
" </sr>\n"
" <dp>\n"
" <map address='68-6f:0000-0fff'/>\n"
" <map address='e8-ef:0000-0fff'/>\n"
" </dp>\n"
" </necdsp>\n"
);
if(has_st011) {
markup.append(T "necdsp model=uPD96050 frequency=15000000 firmware=st0011.bin sha256=651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e\n");
markup.append(T T "dr\n");
markup.append(T T T "map address=60:0000\n");
markup.append(T T T "map address=e0:0000\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=60:0001\n");
markup.append(T T T "map address=e0:0001\n");
markup.append(T T "dp\n");
markup.append(T T T "map address=68-6f:0000-0fff\n");
markup.append(T T T "map address=e8-ef:0000-0fff\n");
}
if(has_st018) markup.append(
" <armdsp firmware='st018.rom' frequency='21477272' sha256='6df209ab5d2524d1839c038be400ae5eb20dafc14a3771a3239cd9e8acd53806'>\n"
" <map address='00-3f:3800-38ff'/>\n"
" <map address='80-bf:3800-38ff'/>\n"
" </armdsp>\n"
);
if(has_st018) {
markup.append(T "setarisc firmware=ST-0018\n");
markup.append(T T "map address=00-3f:3800-38ff\n");
markup.append(T T "map address=80-bf:3800-38ff\n");
}
markup.append("</cartridge>\n");
}
#undef T
void SnesCartridge::read_header(const uint8_t *data, unsigned size) {
type = TypeUnknown;
mapper = LoROM;

70
bsnes/nall/snes/usart.hpp Executable file
View File

@@ -0,0 +1,70 @@
#ifndef NALL_SNES_USART_HPP
#define NALL_SNES_USART_HPP
#include <nall/platform.hpp>
#include <nall/function.hpp>
#include <nall/serial.hpp>
#include <nall/stdint.hpp>
#define usartproc dllexport
static nall::function<void (unsigned milliseconds)> usart_usleep;
static nall::function<uint8_t ()> usart_read;
static nall::function<void (uint8_t data)> usart_write;
extern "C" usartproc void usart_init(
nall::function<void (unsigned milliseconds)> usleep,
nall::function<uint8_t ()> read,
nall::function<void (uint8_t data)> write
) {
usart_usleep = usleep;
usart_read = read;
usart_write = write;
}
extern "C" usartproc void usart_main();
//
static nall::serial usart;
static bool usart_is_virtual = true;
static bool usart_virtual() {
return usart_is_virtual;
}
//
static void usarthw_usleep(unsigned milliseconds) {
usleep(milliseconds);
}
static uint8_t usarthw_read() {
while(true) {
uint8_t buffer[1];
signed length = usart.read((uint8_t*)&buffer, 1);
if(length > 0) return buffer[0];
}
}
static void usarthw_write(uint8_t data) {
uint8_t buffer[1] = { data };
usart.write((uint8_t*)&buffer, 1);
}
int main(int argc, char **argv) {
bool result = false;
if(argc == 1) result = usart.open("/dev/ttyACM0", 57600, true);
if(argc == 2) result = usart.open(argv[1], 57600, true);
if(result == false) {
printf("error: unable to open USART hardware device\n");
return 0;
}
usart_is_virtual = false;
usart_init(usarthw_usleep, usarthw_read, usarthw_write);
usart_main();
usart.close();
return 0;
}
#endif

View File

@@ -1,6 +1,7 @@
#ifndef NALL_SORT_HPP
#define NALL_SORT_HPP
#include <algorithm>
#include <nall/utility.hpp>
//class: merge sort
@@ -10,53 +11,67 @@
//stack: O(log n)
//stable?: yes
//notes:
//there are two primary reasons for choosing merge sort
//over the (usually) faster quick sort*:
//1: it is a stable sort.
//2: it lacks O(n^2) worst-case overhead.
//(* which is also O(n log n) in the average case.)
//note: merge sort was chosen over quick sort, because:
//* it is a stable sort
//* it lacks O(n^2) worst-case overhead
#define NALL_SORT_INSERTION
//#define NALL_SORT_SELECTION
namespace nall {
template<typename T>
void sort(T list[], unsigned length) {
if(length <= 1) return; //nothing to sort
template<typename T, typename Comparator>
void sort(T list[], unsigned size, const Comparator &lessthan) {
if(size <= 1) return; //nothing to sort
//use insertion sort to quickly sort smaller blocks
if(length < 64) {
for(unsigned i = 0; i < length; i++) {
unsigned min = i;
for(unsigned j = i + 1; j < length; j++) {
if(list[j] < list[min]) min = j;
if(size < 64) {
#if defined(NALL_SORT_INSERTION)
for(signed i = 1, j; i < size; i++) {
T copy = std::move(list[i]);
for(j = i - 1; j >= 0; j--) {
if(lessthan(list[j], copy)) break;
list[j + 1] = std::move(list[j]);
}
if(min != i) swap(list[i], list[min]);
list[j + 1] = std::move(copy);
}
#elif defined(NALL_SORT_SELECTION)
for(unsigned i = 0; i < size; i++) {
unsigned min = i;
for(unsigned j = i + 1; j < size; j++) {
if(lessthan(list[j], list[min])) min = j;
}
if(min != i) std::swap(list[i], list[min]);
}
#endif
return;
}
//split list in half and recursively sort both
unsigned middle = length / 2;
sort(list, middle);
sort(list + middle, length - middle);
unsigned middle = size / 2;
sort(list, middle, lessthan);
sort(list + middle, size - middle, lessthan);
//left and right are sorted here; perform merge sort
T *buffer = new T[length];
unsigned offset = 0;
unsigned left = 0;
unsigned right = middle;
while(left < middle && right < length) {
if(list[left] < list[right]) {
buffer[offset++] = list[left++];
T *buffer = new T[size];
unsigned offset = 0, left = 0, right = middle;
while(left < middle && right < size) {
if(lessthan(list[left], list[right])) {
buffer[offset++] = std::move(list[left++]);
} else {
buffer[offset++] = list[right++];
buffer[offset++] = std::move(list[right++]);
}
}
while(left < middle) buffer[offset++] = list[left++];
while(right < length) buffer[offset++] = list[right++];
while(left < middle) buffer[offset++] = std::move(list[left++]);
while(right < size) buffer[offset++] = std::move(list[right++]);
for(unsigned i = 0; i < length; i++) list[i] = buffer[i];
for(unsigned i = 0; i < size; i++) list[i] = std::move(buffer[i]);
delete[] buffer;
}
template<typename T>
void sort(T list[], unsigned size) {
return sort(list, size, [](const T &l, const T &r) { return l < r; });
}
}
#endif

View File

@@ -1,26 +0,0 @@
#ifndef NALL_STACK_HPP
#define NALL_STACK_HPP
#include <nall/vector.hpp>
namespace nall {
template<typename T> struct stack : public linear_vector<T> {
void push(const T &value) {
linear_vector<T>::append(value);
}
T pull() {
if(linear_vector<T>::size() == 0) throw;
T value = linear_vector<T>::operator[](linear_vector<T>::size() - 1);
linear_vector<T>::remove(linear_vector<T>::size() - 1);
return value;
}
T& operator()() {
if(linear_vector<T>::size() == 0) throw;
return linear_vector<T>::operator[](linear_vector<T>::size() - 1);
}
};
}
#endif

View File

@@ -1,20 +0,0 @@
#ifndef NALL_STATIC_HPP
#define NALL_STATIC_HPP
namespace nall {
template<bool C, typename T, typename F> struct static_if { typedef T type; };
template<typename T, typename F> struct static_if<false, T, F> { typedef F type; };
template<typename C, typename T, typename F> struct mp_static_if { typedef typename static_if<C::type, T, F>::type type; };
template<bool A, bool B> struct static_and { enum { value = false }; };
template<> struct static_and<true, true> { enum { value = true }; };
template<typename A, typename B> struct mp_static_and { enum { value = static_and<A::value, B::value>::value }; };
template<bool A, bool B> struct static_or { enum { value = false }; };
template<> struct static_or<false, true> { enum { value = true }; };
template<> struct static_or<true, false> { enum { value = true }; };
template<> struct static_or<true, true> { enum { value = true }; };
template<typename A, typename B> struct mp_static_or { enum { value = static_or<A::value, B::value>::value }; };
}
#endif

View File

@@ -1,8 +1,6 @@
#ifndef NALL_STDINT_HPP
#define NALL_STDINT_HPP
#include <nall/static.hpp>
#if defined(_MSC_VER)
typedef signed char int8_t;
typedef signed short int16_t;

26
bsnes/nall/stream.hpp Executable file
View File

@@ -0,0 +1,26 @@
#ifndef NALL_STREAM_HPP
#define NALL_STREAM_HPP
#include <algorithm>
#include <memory>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/gzip.hpp>
#include <nall/http.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/zip.hpp>
#define NALL_STREAM_INTERNAL_HPP
#include <nall/stream/stream.hpp>
#include <nall/stream/memory.hpp>
#include <nall/stream/mmap.hpp>
#include <nall/stream/file.hpp>
#include <nall/stream/http.hpp>
#include <nall/stream/gzip.hpp>
#include <nall/stream/zip.hpp>
#include <nall/stream/auto.hpp>
#undef NALL_STREAM_INTERNAL_HPP
#endif

24
bsnes/nall/stream/auto.hpp Executable file
View File

@@ -0,0 +1,24 @@
#ifdef NALL_STREAM_INTERNAL_HPP
namespace nall {
#define autostream(...) (*makestream(__VA_ARGS__))
inline std::unique_ptr<stream> makestream(const string &path) {
if(path.ibeginswith("http://")) return std::unique_ptr<stream>(new httpstream(path, 80));
if(path.iendswith(".gz")) return std::unique_ptr<stream>(new gzipstream(filestream{path}));
if(path.iendswith(".zip")) return std::unique_ptr<stream>(new zipstream(filestream{path}));
return std::unique_ptr<stream>(new mmapstream(path));
}
inline std::unique_ptr<stream> makestream(uint8_t *data, unsigned size) {
return std::unique_ptr<stream>(new memorystream(data, size));
}
inline std::unique_ptr<stream> makestream(const uint8_t *data, unsigned size) {
return std::unique_ptr<stream>(new memorystream(data, size));
}
}
#endif

31
bsnes/nall/stream/file.hpp Executable file
View File

@@ -0,0 +1,31 @@
#ifdef NALL_STREAM_INTERNAL_HPP
namespace nall {
struct filestream : stream {
inline bool seekable() const { return true; }
inline bool readable() const { return true; }
inline bool writable() const { return pwritable; }
inline bool randomaccess() const { return false; }
inline unsigned size() const { return pfile.size(); }
inline unsigned offset() const { return pfile.offset(); }
inline void seek(unsigned offset) const { pfile.seek(offset); }
inline uint8_t read() const { return pfile.read(); }
inline void write(uint8_t data) const { pfile.write(data); }
inline filestream(const string &filename) {
pfile.open(filename, file::mode::readwrite);
pwritable = pfile.open();
if(!pwritable) pfile.open(filename, file::mode::read);
}
private:
mutable file pfile;
bool pwritable;
};
}
#endif

28
bsnes/nall/stream/gzip.hpp Executable file
View File

@@ -0,0 +1,28 @@
#ifdef NALL_STREAM_INTERNAL_HPP
namespace nall {
struct gzipstream : memorystream {
inline gzipstream(const stream &stream) {
unsigned size = stream.size();
uint8_t *data = new uint8_t[size];
stream.read(data, size);
gzip archive;
bool result = archive.decompress(data, size);
delete[] data;
if(result == false) return;
psize = archive.size;
pdata = new uint8_t[psize];
memcpy(pdata, archive.data, psize);
}
inline ~gzipstream() {
if(pdata) delete[] pdata;
}
};
}
#endif

43
bsnes/nall/stream/http.hpp Executable file
View File

@@ -0,0 +1,43 @@
#ifdef NALL_STREAM_INTERNAL_HPP
namespace nall {
struct httpstream : stream {
inline bool seekable() const { return true; }
inline bool readable() const { return true; }
inline bool writable() const { return true; }
inline bool randomaccess() const { return true; }
inline unsigned size() const { return psize; }
inline unsigned offset() const { return poffset; }
inline void seek(unsigned offset) const { poffset = offset; }
inline uint8_t read() const { return pdata[poffset++]; }
inline void write(uint8_t data) const { pdata[poffset++] = data; }
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
inline httpstream(const string &url, unsigned port) : pdata(nullptr), psize(0), poffset(0) {
string uri = url;
uri.ltrim<1>("http://");
lstring part = uri.split<1>("/");
part[1] = { "/", part[1] };
http connection;
if(connection.connect(part[0], port) == false) return;
connection.download(part[1], pdata, psize);
}
inline ~httpstream() {
if(pdata) delete[] pdata;
}
private:
mutable uint8_t *pdata;
mutable unsigned psize, poffset;
};
}
#endif

40
bsnes/nall/stream/memory.hpp Executable file
View File

@@ -0,0 +1,40 @@
#ifdef NALL_STREAM_INTERNAL_HPP
namespace nall {
struct memorystream : stream {
inline bool seekable() const { return true; }
inline bool readable() const { return true; }
inline bool writable() const { return pwritable; }
inline bool randomaccess() const { return true; }
inline unsigned size() const { return psize; }
inline unsigned offset() const { return poffset; }
inline void seek(unsigned offset) const { poffset = offset; }
inline uint8_t read() const { return pdata[poffset++]; }
inline void write(uint8_t data) const { pdata[poffset++] = data; }
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
inline memorystream() : pdata(nullptr), psize(0), poffset(0), pwritable(true) {}
inline memorystream(uint8_t *data, unsigned size) {
pdata = data, psize = size, poffset = 0;
pwritable = true;
}
inline memorystream(const uint8_t *data, unsigned size) {
pdata = (uint8_t*)data, psize = size, poffset = 0;
pwritable = false;
}
protected:
mutable uint8_t *pdata;
mutable unsigned psize, poffset, pwritable;
};
}
#endif

36
bsnes/nall/stream/mmap.hpp Executable file
View File

@@ -0,0 +1,36 @@
#ifdef NALL_STREAM_INTERNAL_HPP
namespace nall {
struct mmapstream : stream {
inline bool seekable() const { return true; }
inline bool readable() const { return true; }
inline bool writable() const { return pwritable; }
inline bool randomaccess() const { return false; }
inline unsigned size() const { return pmmap.size(); }
inline unsigned offset() const { return poffset; }
inline void seek(unsigned offset) const { poffset = offset; }
inline uint8_t read() const { return pdata[poffset++]; }
inline void write(uint8_t data) const { pdata[poffset++] = data; }
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
inline mmapstream(const string &filename) {
pmmap.open(filename, filemap::mode::readwrite);
pwritable = pmmap.open();
if(!pwritable) pmmap.open(filename, filemap::mode::read);
pdata = pmmap.data(), poffset = 0;
}
private:
mutable filemap pmmap;
mutable uint8_t *pdata;
mutable unsigned pwritable, poffset;
};
}
#endif

90
bsnes/nall/stream/stream.hpp Executable file
View File

@@ -0,0 +1,90 @@
#ifndef NALL_STREAM_STREAM_HPP
#define NALL_STREAM_STREAM_HPP
namespace nall {
struct stream {
virtual bool seekable() const = 0;
virtual bool readable() const = 0;
virtual bool writable() const = 0;
virtual bool randomaccess() const = 0;
virtual unsigned size() const = 0;
virtual unsigned offset() const = 0;
virtual void seek(unsigned offset) const = 0;
virtual uint8_t read() const = 0;
virtual void write(uint8_t data) const = 0;
inline virtual uint8_t read(unsigned) const { return 0; }
inline virtual void write(unsigned, uint8_t) const {}
inline bool end() const {
return offset() >= size();
}
inline void copy(uint8_t *&data, unsigned &length) const {
seek(0);
length = size();
data = new uint8_t[length];
for(unsigned n = 0; n < length; n++) data[n] = read();
}
inline uintmax_t readl(unsigned length = 1) const {
uintmax_t data = 0, shift = 0;
while(length--) { data |= read() << shift; shift += 8; }
return data;
}
inline uintmax_t readm(unsigned length = 1) const {
uintmax_t data = 0;
while(length--) data = (data << 8) | read();
return data;
}
inline void read(uint8_t *data, unsigned length) const {
while(length--) *data++ = read();
}
inline void writel(uintmax_t data, unsigned length = 1) const {
while(length--) {
write(data);
data >>= 8;
}
}
inline void writem(uintmax_t data, unsigned length = 1) const {
uintmax_t shift = 8 * length;
while(length--) {
shift -= 8;
write(data >> shift);
}
}
inline void write(const uint8_t *data, unsigned length) const {
while(length--) write(*data++);
}
struct byte {
inline operator uint8_t() const { return s.read(offset); }
inline byte& operator=(uint8_t data) { s.write(offset, data); }
inline byte(const stream &s, unsigned offset) : s(s), offset(offset) {}
private:
const stream &s;
const unsigned offset;
};
inline byte operator[](unsigned offset) const {
return byte(*this, offset);
}
inline stream() {}
inline virtual ~stream() {}
stream(const stream&) = delete;
stream& operator=(const stream&) = delete;
};
}
#endif

30
bsnes/nall/stream/zip.hpp Executable file
View File

@@ -0,0 +1,30 @@
#ifdef NALL_STREAM_INTERNAL_HPP
namespace nall {
struct zipstream : memorystream {
inline zipstream(const stream &stream, const string &filter = "*") {
unsigned size = stream.size();
uint8_t *data = new uint8_t[size];
stream.read(data, size);
zip archive;
if(archive.open(data, size) == false) return;
delete[] data;
for(auto &file : archive.file) {
if(file.name.wildcard(filter)) {
archive.extract(file, pdata, psize);
return;
}
}
}
inline ~zipstream() {
if(pdata) delete[] pdata;
}
};
}
#endif

View File

@@ -16,6 +16,7 @@
#include <nall/sha256.hpp>
#include <nall/stdint.hpp>
#include <nall/utility.hpp>
#include <nall/varint.hpp>
#include <nall/vector.hpp>
#include <nall/windows/utf8.hpp>
@@ -24,21 +25,24 @@
#include <nall/string/base.hpp>
#include <nall/string/bml.hpp>
#include <nall/string/bsv.hpp>
#include <nall/string/core.hpp>
#include <nall/string/cast.hpp>
#include <nall/string/compare.hpp>
#include <nall/string/convert.hpp>
#include <nall/string/core.hpp>
#include <nall/string/cstring.hpp>
#include <nall/string/filename.hpp>
#include <nall/string/math.hpp>
#include <nall/string/math-fixed-point.hpp>
#include <nall/string/math-floating-point.hpp>
#include <nall/string/platform.hpp>
#include <nall/string/strl.hpp>
#include <nall/string/strm.hpp>
#include <nall/string/strpos.hpp>
#include <nall/string/trim.hpp>
#include <nall/string/replace.hpp>
#include <nall/string/split.hpp>
#include <nall/string/utf8.hpp>
#include <nall/string/utility.hpp>
#include <nall/string/variadic.hpp>
#include <nall/string/wildcard.hpp>
#include <nall/string/wrapper.hpp>
#include <nall/string/xml.hpp>
#undef NALL_STRING_INTERNAL_HPP

View File

@@ -23,6 +23,7 @@ namespace nall {
struct string {
inline void reserve(unsigned);
inline bool empty() const;
template<typename... Args> inline string& assign(Args&&... args);
template<typename... Args> inline string& append(Args&&... args);
@@ -35,6 +36,12 @@ namespace nall {
template<unsigned Limit = 0> inline string& iqreplace(const char*, const char*);
inline unsigned length() const;
inline unsigned capacity() const;
template<unsigned Limit = 0> inline lstring split(const char*) const;
template<unsigned Limit = 0> inline lstring isplit(const char*) const;
template<unsigned Limit = 0> inline lstring qsplit(const char*) const;
template<unsigned Limit = 0> inline lstring iqsplit(const char*) const;
inline bool equals(const char*) const;
inline bool iequals(const char*) const;
@@ -102,9 +109,7 @@ namespace nall {
#endif
};
struct lstring : public linear_vector<string> {
template<typename T> inline lstring& operator<<(T value);
struct lstring : vector<string> {
inline optional<unsigned> find(const char*) const;
template<unsigned Limit = 0> inline lstring& split(const char*, const char*);
template<unsigned Limit = 0> inline lstring& isplit(const char*, const char*);
@@ -125,8 +130,6 @@ namespace nall {
inline char chrlower(char c);
inline char chrupper(char c);
inline int istrcmp(const char *str1, const char *str2);
inline bool wildcard(const char *str, const char *pattern);
inline bool iwildcard(const char *str, const char *pattern);
inline bool strbegin(const char *str, const char *key);
inline bool istrbegin(const char *str, const char *key);
inline bool strend(const char *str, const char *key);
@@ -148,9 +151,12 @@ namespace nall {
inline string userpath();
inline string currentpath();
//strl.hpp
inline unsigned strlcpy(char *dest, const char *src, unsigned length);
inline unsigned strlcat(char *dest, const char *src, unsigned length);
//strm.hpp
inline unsigned strmcpy(char *target, const char *source, unsigned length);
inline unsigned strmcat(char *target, const char *source, unsigned length);
inline bool strccpy(char *target, const char *source, unsigned length);
inline bool strccat(char *target, const char *source, unsigned length);
inline void strpcpy(char *&target, const char *source, unsigned &length);
//strpos.hpp
inline optional<unsigned> strpos(const char *str, const char *key);
@@ -168,22 +174,27 @@ namespace nall {
template<bool Insensitive> alwaysinline bool chrequal(char x, char y);
template<bool Quoted, typename T> alwaysinline bool quoteskip(T *&p);
template<bool Quoted, typename T> alwaysinline bool quotecopy(char *&t, T *&p);
inline unsigned strlcpy(string &dest, const char *src, unsigned length);
inline unsigned strlcat(string &dest, const char *src, unsigned length);
inline string substr(const char *src, unsigned start = 0, unsigned length = ~0u);
inline string sha256(const uint8_t *data, unsigned size);
inline char* integer(char *result, intmax_t value);
inline char* decimal(char *result, uintmax_t value);
template<unsigned length = 0, char padding = ' '> inline string integer(intmax_t value);
template<unsigned length = 0, char padding = ' '> inline string linteger(intmax_t value);
template<unsigned length = 0, char padding = ' '> inline string decimal(uintmax_t value);
template<unsigned length = 0, char padding = ' '> inline string ldecimal(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string hex(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string binary(uintmax_t value);
inline unsigned fp(char *str, double value);
inline string fp(double value);
inline unsigned fp(char *str, long double value);
inline string fp(long double value);
//variadic.hpp
template<typename... Args> inline void print(Args&&... args);
//wildcard.hpp
inline bool wildcard(const char *str, const char *pattern);
inline bool iwildcard(const char *str, const char *pattern);
};
#endif

View File

@@ -20,7 +20,7 @@ struct Node {
cstring value;
private:
linear_vector<Node> children;
vector<Node> children;
inline bool valid(char p) const { //A-Za-z0-9-.
return p - 'A' < 26u | p - 'a' < 26u | p - '0' < 10u | p - '-' < 2u;

View File

@@ -2,31 +2,184 @@
namespace nall {
//this is needed, as C++0x does not support explicit template specialization inside classes
template<> inline const char* to_string<bool> (bool v) { return v ? "true" : "false"; }
template<> inline const char* to_string<signed int> (signed int v) { static char temp[256]; snprintf(temp, 255, "%+d", v); return temp; }
template<> inline const char* to_string<unsigned int> (unsigned int v) { static char temp[256]; snprintf(temp, 255, "%u", v); return temp; }
template<> inline const char* to_string<intmax_t> (intmax_t v) { static char temp[256]; snprintf(temp, 255, "%+lld", (long long)v); return temp; }
template<> inline const char* to_string<uintmax_t> (uintmax_t v) { static char temp[256]; snprintf(temp, 255, "%llu", (unsigned long long)v); return temp; }
template<> inline const char* to_string<double> (double v) { static char temp[256]; snprintf(temp, 255, "%f", v); return temp; }
template<> inline const char* to_string<char*> (char *v) { return v; }
template<> inline const char* to_string<const char*> (const char *v) { return v; }
template<> inline const char* to_string<string> (string v) { return v; }
template<> inline const char* to_string<const string&> (const string &v) { return v; }
template<> inline const char* to_string<cstring> (cstring v) { return v; }
template<> inline const char* to_string<const cstring&>(const cstring &v) { return v; }
//convert any (supported) type to a const char* without constructing a new nall::string
//this is used inside istring(...) to build nall::string values
template<typename T> struct stringify;
template<typename T> lstring& lstring::operator<<(T value) {
operator[](size()).assign(to_string<T>(value));
return *this;
}
// base types
template<> struct stringify<bool> {
bool value;
operator const char*() const { return value ? "true" : "false"; }
stringify(bool value) : value(value) {}
};
template<> struct stringify<char> {
char data[256];
operator const char*() const { return data; }
stringify(char value) { integer(data, value); }
};
// signed integers
template<> struct stringify<signed char> {
char data[256];
operator const char*() const { return data; }
stringify(signed char value) { integer(data, value); }
};
template<> struct stringify<signed short> {
char data[256];
operator const char*() const { return data; }
stringify(signed short value) { integer(data, value); }
};
template<> struct stringify<signed int> {
char data[256];
operator const char*() const { return data; }
stringify(signed int value) { integer(data, value); }
};
template<> struct stringify<signed long> {
char data[256];
operator const char*() const { return data; }
stringify(signed long value) { integer(data, value); }
};
template<> struct stringify<signed long long> {
char data[256];
operator const char*() const { return data; }
stringify(signed long long value) { integer(data, value); }
};
template<unsigned bits> struct stringify<int_t<bits>> {
char data[256];
operator const char*() const { return data; }
stringify(int_t<bits> value) { integer(data, value); }
};
// unsigned integers
template<> struct stringify<unsigned char> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned char value) { decimal(data, value); }
};
template<> struct stringify<unsigned short> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned short value) { decimal(data, value); }
};
template<> struct stringify<unsigned int> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned int value) { decimal(data, value); }
};
template<> struct stringify<unsigned long> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned long value) { decimal(data, value); }
};
template<> struct stringify<unsigned long long> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned long long value) { decimal(data, value); }
};
template<unsigned bits> struct stringify<uint_t<bits>> {
char data[256];
operator const char*() const { return data; }
stringify(uint_t<bits> value) { decimal(data, value); }
};
// floating-point
template<> struct stringify<float> {
char data[256];
operator const char*() const { return data; }
stringify(float value) { fp(data, value); }
};
template<> struct stringify<double> {
char data[256];
operator const char*() const { return data; }
stringify(double value) { fp(data, value); }
};
template<> struct stringify<long double> {
char data[256];
operator const char*() const { return data; }
stringify(long double value) { fp(data, value); }
};
// strings
template<> struct stringify<char*> {
const char *value;
operator const char*() const { return value; }
stringify(char *value) : value(value) {}
};
template<> struct stringify<const char*> {
const char *value;
operator const char*() const { return value; }
stringify(const char *value) : value(value) {}
};
template<> struct stringify<string> {
const string &value;
operator const char*() const { return value; }
stringify(const string &value) : value(value) {}
};
template<> struct stringify<const string&> {
const string &value;
operator const char*() const { return value; }
stringify(const string &value) : value(value) {}
};
template<> struct stringify<cstring> {
const char *value;
operator const char*() const { return value; }
stringify(const cstring &value) : value(value) {}
};
template<> struct stringify<const cstring&> {
const char *value;
operator const char*() const { return value; }
stringify(const cstring &value) : value(value) {}
};
#if defined(QSTRING_H)
template<> inline const char* to_string<QString>(QString v) { return v.toUtf8().constData(); }
template<> inline const char* to_string<const QString&>(const QString &v) { return v.toUtf8().constData(); }
string::operator QString() const { return QString::fromUtf8(*this); }
template<> struct stringify<QString> {
const QString &value;
operator const char*() const { return value.toUtf8().constData(); }
stringify(const QString &value) : value(value) {}
};
template<> struct stringify<const QString&> {
const QString &value;
operator const char*() const { return value.toUtf8().constData(); }
stringify(const QString &value) : value(value) {}
};
string::operator QString() const {
return QString::fromUtf8(*this);
}
#endif
//
template<typename T> stringify<T> make_string(T value) {
return stringify<T>(std::forward<T>(value));
}
}
#endif

View File

@@ -18,46 +18,6 @@ int istrcmp(const char *str1, const char *str2) {
return (int)chrlower(*str1) - (int)chrlower(*str2);
}
bool wildcard(const char *s, const char *p) {
const char *cp = 0, *mp = 0;
while(*s && *p != '*') {
if(*p != '?' && *s != *p) return false;
p++, s++;
}
while(*s) {
if(*p == '*') {
if(!*++p) return true;
mp = p, cp = s + 1;
} else if(*p == '?' || *p == *s) {
p++, s++;
} else {
p = mp, s = cp++;
}
}
while(*p == '*') p++;
return !*p;
}
bool iwildcard(const char *s, const char *p) {
const char *cp = 0, *mp = 0;
while(*s && *p != '*') {
if(*p != '?' && chrlower(*s) != chrlower(*p)) return false;
p++, s++;
}
while(*s) {
if(*p == '*') {
if(!*++p) return true;
mp = p, cp = s + 1;
} else if(*p == '?' || chrlower(*p) == chrlower(*s)) {
p++, s++;
} else {
p = mp, s = cp++;
}
}
while(*p == '*') p++;
return !*p;
}
bool strbegin(const char *str, const char *key) {
int i, ssl = strlen(str), ksl = strlen(key);

View File

@@ -7,7 +7,7 @@ static void istring(string &output) {
template<typename T, typename... Args>
static void istring(string &output, const T &value, Args&&... args) {
output.append_(to_string(value));
output.append_(make_string(value));
istring(output, std::forward<Args>(args)...);
}
@@ -19,6 +19,10 @@ void string::reserve(unsigned size_) {
}
}
bool string::empty() const {
return !*data;
}
template<typename... Args> string& string::assign(Args&&... args) {
*data = 0;
istring(*this, std::forward<Args>(args)...);
@@ -151,9 +155,7 @@ inline lstring::lstring() {
}
inline lstring::lstring(std::initializer_list<string> list) {
for(const string *s = list.begin(); s != list.end(); ++s) {
operator<<(*s);
}
for(auto &data : list) append(data);
}
}

View File

@@ -0,0 +1,166 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace fixedpoint {
static nall::function<intmax_t (const char *&)> eval_fallback;
static intmax_t eval_integer(const char *& s) {
if(!*s) throw "unrecognized integer";
intmax_t value = 0, x = *s, y = *(s + 1);
//hexadecimal
if(x == '0' && (y == 'X' || y == 'x')) {
s += 2;
while(true) {
if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; }
if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; }
if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; }
return value;
}
}
//binary
if(x == '0' && (y == 'B' || y == 'b')) {
s += 2;
while(true) {
if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; }
return value;
}
}
//octal (or decimal '0')
if(x == '0') {
s += 1;
while(true) {
if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; }
return value;
}
}
//decimal
if(x >= '0' && x <= '9') {
while(true) {
if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; }
return value;
}
}
//char
if(x == '\'' && y != '\'') {
s += 1;
while(true) {
value = value * 256 + *s++;
if(*s == '\'') { s += 1; return value; }
if(!*s) throw "mismatched char";
}
}
throw "unrecognized integer";
}
static intmax_t eval(const char *&s, int depth = 0) {
while(*s == ' ' || *s == '\t') s++; //trim whitespace
if(!*s) throw "unrecognized token";
intmax_t value = 0, x = *s, y = *(s + 1);
if(*s == '(') {
value = eval(++s, 1);
if(*s++ != ')') throw "mismatched group";
}
else if(x == '!') value = !eval(++s, 13);
else if(x == '~') value = ~eval(++s, 13);
else if(x == '+') value = +eval(++s, 13);
else if(x == '-') value = -eval(++s, 13);
else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s);
else if(eval_fallback) value = eval_fallback(s); //optional user-defined syntax parsing
else throw "unrecognized token";
while(true) {
while(*s == ' ' || *s == '\t') s++; //trim whitespace
if(!*s) break;
x = *s, y = *(s + 1);
if(depth >= 13) break;
if(x == '*') { value *= eval(++s, 13); continue; }
if(x == '/') { intmax_t result = eval(++s, 13); if(result == 0) throw "division by zero"; value /= result; continue; }
if(x == '%') { intmax_t result = eval(++s, 13); if(result == 0) throw "division by zero"; value %= result; continue; }
if(depth >= 12) break;
if(x == '+') { value += eval(++s, 12); continue; }
if(x == '-') { value -= eval(++s, 12); continue; }
if(depth >= 11) break;
if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; }
if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; }
if(depth >= 10) break;
if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; }
if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; }
if(x == '<') { value = value < eval(++s, 10); continue; }
if(x == '>') { value = value > eval(++s, 10); continue; }
if(depth >= 9) break;
if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; }
if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; }
if(depth >= 8) break;
if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; }
if(depth >= 7) break;
if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; }
if(depth >= 6) break;
if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; }
if(depth >= 5) break;
if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; }
if(depth >= 4) break;
if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; }
if(depth >= 3) break;
if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; }
if(x == '?') {
intmax_t lhs = eval(++s, 2);
if(*s != ':') throw "mismatched ternary";
intmax_t rhs = eval(++s, 2);
value = value ? lhs : rhs;
continue;
}
if(depth >= 2) break;
if(depth > 0 && x == ')') break;
throw "unrecognized token";
}
return value;
}
static bool eval(const char *s, intmax_t &result) {
try {
result = eval(s);
return true;
} catch(const char*) {
result = 0;
return false;
}
}
static intmax_t parse(const char *s) {
try {
intmax_t result = eval(s);
return result;
} catch(const char *) {
return 0;
}
}
}
#endif

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