Merge pull request #1 from SergioMartin86/headerOnly
A lot of optimizations and better testing
This commit is contained in:
commit
ea4c425e47
|
@ -1,3 +1,3 @@
|
|||
[submodule "extern/QuickNES_Core"]
|
||||
path = extern/QuickNES_Core
|
||||
path = source/quickNES/QuickNES_Core
|
||||
url = https://github.com/libretro/QuickNES_Core.git
|
||||
|
|
|
@ -17,6 +17,7 @@ Changes
|
|||
+ Forced alignment at the start of a page to prevent crossing cache line boundaries
|
||||
+ Simplifying instruction decode
|
||||
- Minimize compiled code size to reduce pressure on L1i cache
|
||||
- Reduce heap allocations
|
||||
|
||||
Credits
|
||||
=========
|
||||
|
@ -27,7 +28,7 @@ Credits
|
|||
- The latest version of the code is maintained by Libretro's community [here](https://github.com/libretro/QuickNES_Core)
|
||||
- For the interactive player, this project uses a modified version of [HeadlessQuickNES (HQN)](https://github.com/Bindernews/HeadlessQuickNes) by Drew (Binder News)
|
||||
- We use some of the [NES test rom set](https://github.com/christopherpow/nes-test-roms) made by multiple authors and gathered by Christopher Pow et al.
|
||||
- We also use some movies from the (TASVideos)[tasvideos.org] website for testing. These movies are copied into this repository with authorization under the Creative Commons Attribution 2.0 license.
|
||||
- We also use some movies from the [TASVideos](tasvideos.org) website for testing. These movies are copied into this repository with authorization under the Creative Commons Attribution 2.0 license.
|
||||
|
||||
All base code for this project was found under open source licenses, which I preserved in their corresponding files/folders. Any non-credited work is unintentional and shall be immediately rectfied.
|
||||
|
||||
|
|
|
@ -68,25 +68,6 @@ error_t HQNState::setSampleRate(int rate)
|
|||
return ret;
|
||||
}
|
||||
|
||||
// Load a ROM image
|
||||
error_t HQNState::loadROM(const char *filename)
|
||||
{
|
||||
// unload any existing rom data
|
||||
unloadRom();
|
||||
|
||||
if (!load_file(filename, (char**)(&m_romData), &m_romSize))
|
||||
{
|
||||
return "Failed to open file";
|
||||
}
|
||||
|
||||
// Now finally load the rom. Ugh
|
||||
Mem_File_Reader r(m_romData, (int)m_romSize);
|
||||
Auto_File_Reader a(r);
|
||||
error_t result = m_emu->load_ines(a);
|
||||
if (m_listener)
|
||||
m_listener->onLoadROM(this, filename);
|
||||
return result;
|
||||
}
|
||||
|
||||
error_t HQNState::saveState(void *dest, size_t size, size_t *size_out)
|
||||
{
|
||||
|
@ -120,16 +101,6 @@ error_t HQNState::loadState(const char *data, size_t size)
|
|||
return result;
|
||||
}
|
||||
|
||||
void HQNState::unloadRom()
|
||||
{
|
||||
if (m_romData)
|
||||
{
|
||||
delete[] m_romData;
|
||||
m_romData = nullptr;
|
||||
m_romSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance the emulator
|
||||
error_t HQNState::advanceFrame(bool sleep)
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef __HQN_H__
|
||||
#define __HQN_H__
|
||||
|
||||
#include <core/Nes_Emu.h>
|
||||
#include <Nes_Emu.h>
|
||||
#include <cstdint>
|
||||
#include <stdio.h>
|
||||
|
||||
|
@ -46,6 +46,8 @@ public:
|
|||
HQNState();
|
||||
~HQNState();
|
||||
|
||||
void setEmulatorPointer(void* const emuPtr) { m_emu = (Nes_Emu*)emuPtr; }
|
||||
|
||||
/*
|
||||
The joypad data for the two joypads available to an NES.
|
||||
This is directly available because I'm lazy.
|
||||
|
@ -56,11 +58,6 @@ public:
|
|||
inline Nes_Emu *emu() const
|
||||
{ return m_emu; }
|
||||
|
||||
/*
|
||||
Load a NES rom from the named file.
|
||||
Returns NULL or error string.
|
||||
*/
|
||||
error_t loadROM(const char *filename);
|
||||
|
||||
/*
|
||||
Advance the emulator by one frame. If sleep is true and there is a frame
|
||||
|
@ -118,9 +115,6 @@ public:
|
|||
{ return m_keyboard; }
|
||||
|
||||
private:
|
||||
// Safely unload the currently loaded rom
|
||||
void unloadRom();
|
||||
|
||||
/* ROM file stored in memory because reasons */
|
||||
uint8_t *m_romData;
|
||||
size_t m_romSize;
|
||||
|
@ -148,7 +142,7 @@ void printUsage(const char *filename);
|
|||
} // end namespace hqn
|
||||
|
||||
// Copied from bizinterface.cpp in BizHawk/quicknes
|
||||
inline void saveBlit(const Nes_Emu *e, int32_t *dest, const int32_t *colors, int cropleft, int croptop, int cropright, int cropbottom)
|
||||
inline void saveBlit(const void *ePtr, int32_t *dest, const int32_t *colors, int cropleft, int croptop, int cropright, int cropbottom)
|
||||
{
|
||||
// what is the point of the 256 color bitmap and the dynamic color allocation to it?
|
||||
// why not just render directly to a 512 color bitmap with static palette positions?
|
||||
|
@ -172,10 +166,11 @@ inline void saveBlit(const Nes_Emu *e, int32_t *dest, const int32_t *colors, int
|
|||
// }
|
||||
// }
|
||||
|
||||
const Nes_Emu *e = (Nes_Emu*) ePtr;
|
||||
const unsigned char *in_pixels = e->frame().pixels;
|
||||
if (in_pixels == NULL) return;
|
||||
int32_t *out_pixels = dest;
|
||||
|
||||
|
||||
for (unsigned h = 0; h < Nes_Emu::image_height; h++, in_pixels += e->frame().pitch, out_pixels += Nes_Emu::image_width)
|
||||
for (unsigned w = 0; w < Nes_Emu::image_width; w++)
|
||||
{
|
||||
|
|
27
meson.build
27
meson.build
|
@ -9,7 +9,6 @@ pageSize = get_option('CPUFunctionAlignment')
|
|||
|
||||
# Loading dependencies
|
||||
subdir('source')
|
||||
subdir('extern')
|
||||
|
||||
# Common application flags
|
||||
commonCompileArgs = [ '-Wfatal-errors', '-Wall', '-Wno-multichar' ]
|
||||
|
@ -24,21 +23,23 @@ quickerNESPlayerSrc = [
|
|||
'extern/hqn/options.cpp',
|
||||
]
|
||||
|
||||
executable('player',
|
||||
'source/player.cpp',
|
||||
cpp_args : [ commonCompileArgs, '-DNCURSES' ],
|
||||
dependencies : [ quickerNESCoreDependency, quickerNESApplicationDependency, dependency('sdl2'), dependency('SDL2_image') ],
|
||||
include_directories : include_directories(['source']),
|
||||
link_args : [ '-lncurses' ],
|
||||
sources : quickerNESPlayerSrc
|
||||
)
|
||||
if get_option('buildPlayer') == true
|
||||
executable('player',
|
||||
'source/player.cpp',
|
||||
cpp_args : [ commonCompileArgs, '-DNCURSES' ],
|
||||
dependencies : [ quickerNESDependency, toolDependency, dependency('sdl2'), dependency('SDL2_image') ],
|
||||
include_directories : include_directories(['source']),
|
||||
link_args : [ '-lncurses' ],
|
||||
sources : quickerNESPlayerSrc
|
||||
)
|
||||
endif
|
||||
|
||||
# Building tester tool for QuickerNES
|
||||
|
||||
quickerNESTester = executable('quickerNESTester',
|
||||
'source/tester.cpp',
|
||||
cpp_args : [ commonCompileArgs, '-Werror' ],
|
||||
dependencies : [ quickerNESCoreDependency, quickerNESApplicationDependency ],
|
||||
dependencies : [ quickerNESDependency, toolDependency ],
|
||||
include_directories : include_directories(['../extern/json'])
|
||||
)
|
||||
|
||||
|
@ -47,11 +48,9 @@ quickerNESTester = executable('quickerNESTester',
|
|||
quickNESTester = executable('quickNESTester',
|
||||
'source/tester.cpp',
|
||||
cpp_args : [ commonCompileArgs ],
|
||||
dependencies : [ quickNESCoreDependency, quickerNESApplicationDependency ],
|
||||
dependencies : [ quickNESDependency, toolDependency ],
|
||||
include_directories : include_directories(['../extern/json'])
|
||||
)
|
||||
|
||||
# Building tests
|
||||
if get_option('buildTests') == true
|
||||
subdir('tests')
|
||||
endif
|
||||
subdir('tests')
|
|
@ -1,16 +1,15 @@
|
|||
option('buildTests',
|
||||
option('buildPlayer',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
description : 'Build test suite',
|
||||
description : 'Build playback tool',
|
||||
yield: true
|
||||
)
|
||||
|
||||
option('CPUFunctionAlignment',
|
||||
type : 'integer',
|
||||
min : 1,
|
||||
value : 4096,
|
||||
value : 1024,
|
||||
description : '''Indicates the alignment for the main emulator CPU function.
|
||||
This is a large function that may cross page boundaries, which impacts performance.
|
||||
We recommend setting this value for the architecture own page size.
|
||||
This requires the GNU compiler to work.'''
|
||||
)
|
|
@ -1,120 +0,0 @@
|
|||
|
||||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include "Nes_Cart.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details. You should have received a copy of the GNU Lesser General
|
||||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
char const Nes_Cart::not_ines_file [] = "Not an iNES file";
|
||||
|
||||
Nes_Cart::Nes_Cart()
|
||||
{
|
||||
prg_ = NULL;
|
||||
chr_ = NULL;
|
||||
clear();
|
||||
}
|
||||
|
||||
Nes_Cart::~Nes_Cart()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void Nes_Cart::clear()
|
||||
{
|
||||
if ( prg_ )
|
||||
free( prg_ );
|
||||
prg_ = NULL;
|
||||
|
||||
if ( chr_ )
|
||||
free( chr_ );
|
||||
chr_ = NULL;
|
||||
|
||||
prg_size_ = 0;
|
||||
chr_size_ = 0;
|
||||
mapper = 0;
|
||||
}
|
||||
|
||||
long Nes_Cart::round_to_bank_size( long n )
|
||||
{
|
||||
n += bank_size - 1;
|
||||
return n - n % bank_size;
|
||||
}
|
||||
|
||||
const char * Nes_Cart::resize_prg( long size )
|
||||
{
|
||||
if ( size != prg_size_ )
|
||||
{
|
||||
// padding allows CPU to always read operands of instruction, which
|
||||
// might go past end of data
|
||||
void* p = realloc( prg_, round_to_bank_size( size ) + 2 );
|
||||
CHECK_ALLOC( p || !size );
|
||||
prg_ = (uint8_t*) p;
|
||||
prg_size_ = size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char * Nes_Cart::resize_chr( long size )
|
||||
{
|
||||
if ( size != chr_size_ )
|
||||
{
|
||||
void* p = realloc( chr_, round_to_bank_size( size ) );
|
||||
CHECK_ALLOC( p || !size );
|
||||
chr_ = (uint8_t*) p;
|
||||
chr_size_ = size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// iNES reading
|
||||
|
||||
struct ines_header_t {
|
||||
uint8_t signature [4];
|
||||
uint8_t prg_count; // number of 16K PRG banks
|
||||
uint8_t chr_count; // number of 8K CHR banks
|
||||
uint8_t flags; // MMMM FTBV Mapper low, Four-screen, Trainer, Battery, V mirror
|
||||
uint8_t flags2; // MMMM --XX Mapper high 4 bits
|
||||
uint8_t zero [8]; // if zero [7] is non-zero, treat flags2 as zero
|
||||
};
|
||||
BOOST_STATIC_ASSERT( sizeof (ines_header_t) == 16 );
|
||||
|
||||
const char * Nes_Cart::load_ines( Auto_File_Reader in )
|
||||
{
|
||||
RETURN_ERR( in.open() );
|
||||
|
||||
ines_header_t h;
|
||||
RETURN_ERR( in->read( &h, sizeof h ) );
|
||||
|
||||
if ( 0 != memcmp( h.signature, "NES\x1A", 4 ) )
|
||||
return not_ines_file;
|
||||
|
||||
if ( h.zero [7] ) // handle header defaced by a fucking idiot's handle
|
||||
h.flags2 = 0;
|
||||
|
||||
set_mapper( h.flags, h.flags2 );
|
||||
|
||||
if ( h.flags & 0x04 ) // skip trainer
|
||||
RETURN_ERR( in->skip( 512 ) );
|
||||
|
||||
RETURN_ERR( resize_prg( h.prg_count * 16 * 1024L ) );
|
||||
RETURN_ERR( resize_chr( h.chr_count * 8 * 1024L ) );
|
||||
|
||||
RETURN_ERR( in->read( prg(), prg_size() ) );
|
||||
RETURN_ERR( in->read( chr(), chr_size() ) );
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
|
||||
// NES cartridge data (PRG, CHR, mapper)
|
||||
|
||||
// Nes_Emu 0.7.0
|
||||
|
||||
#ifndef NES_CART_H
|
||||
#define NES_CART_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "blargg_common.h"
|
||||
#include "abstract_file.h"
|
||||
|
||||
class Nes_Cart {
|
||||
public:
|
||||
Nes_Cart();
|
||||
~Nes_Cart();
|
||||
|
||||
// Load iNES file
|
||||
const char * load_ines( Auto_File_Reader );
|
||||
static const char not_ines_file [];
|
||||
|
||||
// to do: support UNIF?
|
||||
|
||||
// True if data is currently loaded
|
||||
bool loaded() const { return prg_ != NULL; }
|
||||
|
||||
// Free data
|
||||
void clear();
|
||||
|
||||
// True if cartridge claims to have battery-backed memory
|
||||
bool has_battery_ram() const;
|
||||
|
||||
// Size of PRG data
|
||||
long prg_size() const { return prg_size_; }
|
||||
|
||||
// Size of CHR data
|
||||
long chr_size() const { return chr_size_; }
|
||||
|
||||
// Change size of PRG (code) data
|
||||
const char * resize_prg( long );
|
||||
|
||||
// Change size of CHR (graphics) data
|
||||
const char * resize_chr( long );
|
||||
|
||||
// Set mapper and information bytes. LSB and MSB are the standard iNES header
|
||||
// bytes at offsets 6 and 7.
|
||||
void set_mapper( int mapper_lsb, int mapper_msb );
|
||||
|
||||
unsigned mapper_data() const { return mapper; }
|
||||
|
||||
// Initial mirroring setup
|
||||
int mirroring() const { return mapper & 0x09; }
|
||||
|
||||
// iNES mapper code
|
||||
int mapper_code() const;
|
||||
|
||||
// Pointer to beginning of PRG data
|
||||
uint8_t * prg() { return prg_; }
|
||||
uint8_t const* prg() const { return prg_; }
|
||||
|
||||
// Pointer to beginning of CHR data
|
||||
uint8_t * chr() { return chr_; }
|
||||
uint8_t const* chr() const { return chr_; }
|
||||
|
||||
// End of public interface
|
||||
private:
|
||||
enum { bank_size = 8 * 1024L }; // bank sizes must be a multiple of this
|
||||
uint8_t *prg_;
|
||||
uint8_t *chr_;
|
||||
long prg_size_;
|
||||
long chr_size_;
|
||||
unsigned mapper;
|
||||
long round_to_bank_size( long n );
|
||||
};
|
||||
|
||||
inline bool Nes_Cart::has_battery_ram() const { return mapper & 0x02; }
|
||||
|
||||
inline void Nes_Cart::set_mapper( int mapper_lsb, int mapper_msb )
|
||||
{
|
||||
mapper = mapper_msb * 0x100 + mapper_lsb;
|
||||
}
|
||||
|
||||
inline int Nes_Cart::mapper_code() const { return ((mapper >> 8) & 0xf0) | ((mapper >> 4) & 0x0f); }
|
||||
|
||||
#endif
|
|
@ -1,68 +0,0 @@
|
|||
while ( true )
|
||||
{
|
||||
while ( count-- )
|
||||
{
|
||||
int attrib = attr_table [addr >> 2 & 0x07];
|
||||
attrib >>= (addr >> 4 & 4) | (addr & 2);
|
||||
unsigned long offset = (attrib & 3) * attrib_factor + this->palette_offset;
|
||||
|
||||
// draw one tile
|
||||
cache_t const* lines = this->get_bg_tile( nametable [addr] + bg_bank );
|
||||
uint8_t* p = pixels;
|
||||
addr++;
|
||||
pixels += 8; // next tile
|
||||
|
||||
if ( !clipped )
|
||||
{
|
||||
// optimal case: no clipping
|
||||
for ( int n = 4; n--; )
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
((unaligned_uint32_t*) p) [0].val = (line >> 4 & mask) + offset;
|
||||
((unaligned_uint32_t*) p) [1].val = (line & mask) + offset;
|
||||
p += row_bytes;
|
||||
((unaligned_uint32_t*) p) [0].val = (line >> 6 & mask) + offset;
|
||||
((unaligned_uint32_t*) p) [1].val = (line >> 2 & mask) + offset;
|
||||
p += row_bytes;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lines += fine_y >> 1;
|
||||
|
||||
if ( fine_y & 1 )
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
((unaligned_uint32_t*) p) [0].val = (line >> 6 & mask) + offset;
|
||||
((unaligned_uint32_t*) p) [1].val = (line >> 2 & mask) + offset;
|
||||
p += row_bytes;
|
||||
}
|
||||
|
||||
for ( int n = height >> 1; n--; )
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
((unaligned_uint32_t*) p) [0].val = (line >> 4 & mask) + offset;
|
||||
((unaligned_uint32_t*) p) [1].val = (line & mask) + offset;
|
||||
p += row_bytes;
|
||||
((unaligned_uint32_t*) p) [0].val = (line >> 6 & mask) + offset;
|
||||
((unaligned_uint32_t*) p) [1].val = (line >> 2 & mask) + offset;
|
||||
p += row_bytes;
|
||||
}
|
||||
|
||||
if ( height & 1 )
|
||||
{
|
||||
unsigned long line = *lines;
|
||||
((unaligned_uint32_t*) p) [0].val = (line >> 4 & mask) + offset;
|
||||
((unaligned_uint32_t*) p) [1].val = (line & mask) + offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count = count2;
|
||||
count2 = 0;
|
||||
addr -= 32;
|
||||
attr_table = attr_table - nametable + nametable2;
|
||||
nametable = nametable2;
|
||||
if ( !count )
|
||||
break;
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
|
||||
#include "abstract_file.h"
|
||||
|
||||
#include "blargg_config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Copyright (C) 2005-2006 Shay Green. Permission is hereby granted, free of
|
||||
charge, to any person obtaining a copy of this software module and associated
|
||||
documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell copies of the Software, and
|
||||
to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions: The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions of the Software. THE
|
||||
SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
const char *Data_Writer::write( const void*, long ) { return 0; }
|
||||
|
||||
// Mem_Writer
|
||||
|
||||
Mem_Writer::Mem_Writer( void* p, long s, int b )
|
||||
{
|
||||
data_ = (char*) p;
|
||||
size_ = 0;
|
||||
allocated = s;
|
||||
mode = b ? ignore_excess : fixed;
|
||||
}
|
||||
|
||||
Mem_Writer::Mem_Writer()
|
||||
{
|
||||
data_ = 0;
|
||||
size_ = 0;
|
||||
allocated = 0;
|
||||
mode = expanding;
|
||||
}
|
||||
|
||||
Mem_Writer::~Mem_Writer()
|
||||
{
|
||||
if ( ( mode == expanding ) && data_ )
|
||||
free( data_ );
|
||||
}
|
||||
|
||||
const char *Mem_Writer::write( const void* p, long s )
|
||||
{
|
||||
long remain = allocated - size_;
|
||||
if ( s > remain )
|
||||
{
|
||||
if ( mode == fixed )
|
||||
return "Tried to write more data than expected";
|
||||
|
||||
if ( mode == ignore_excess )
|
||||
{
|
||||
s = remain;
|
||||
}
|
||||
else // expanding
|
||||
{
|
||||
long new_allocated = size_ + s;
|
||||
new_allocated += (new_allocated >> 1) + 2048;
|
||||
void* p = realloc( data_, new_allocated );
|
||||
if ( !p )
|
||||
return "Out of memory";
|
||||
data_ = (char*) p;
|
||||
allocated = new_allocated;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy( data_ + size_, p, s );
|
||||
size_ += s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Dry_Writer for determining size
|
||||
|
||||
Dry_Writer::Dry_Writer()
|
||||
{
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
Dry_Writer::~Dry_Writer()
|
||||
{
|
||||
}
|
||||
|
||||
const char *Dry_Writer::write( const void* p, long s )
|
||||
{
|
||||
size_ += s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Auto_File_Reader
|
||||
|
||||
const char* Auto_File_Reader::open()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Auto_File_Reader::~Auto_File_Reader()
|
||||
{
|
||||
if ( path )
|
||||
delete data;
|
||||
}
|
||||
|
||||
// Auto_File_Writer
|
||||
|
||||
const char* Auto_File_Writer::open()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* Auto_File_Writer::open_comp( int level )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Auto_File_Writer::~Auto_File_Writer()
|
||||
{
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
|
||||
// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/
|
||||
|
||||
#include "apu_state.h"
|
||||
#include "Nes_Apu.h"
|
||||
|
||||
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details. You should have received a copy of the GNU Lesser General
|
||||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
template<int mode>
|
||||
struct apu_reflection
|
||||
{
|
||||
#define REFLECT( apu, state ) (mode ? void (apu = state) : void (state = apu))
|
||||
|
||||
static void reflect_env( apu_state_t::env_t* state, Nes_Envelope& osc )
|
||||
{
|
||||
REFLECT( (*state) [0], osc.env_delay );
|
||||
REFLECT( (*state) [1], osc.envelope );
|
||||
REFLECT( (*state) [2], osc.reg_written [3] );
|
||||
}
|
||||
|
||||
static void reflect_square( apu_state_t::square_t& state, Nes_Square& osc )
|
||||
{
|
||||
reflect_env( &state.env, osc );
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.length_counter, osc.length_counter );
|
||||
REFLECT( state.phase, osc.phase );
|
||||
REFLECT( state.swp_delay, osc.sweep_delay );
|
||||
REFLECT( state.swp_reset, osc.reg_written [1] );
|
||||
}
|
||||
|
||||
static void reflect_triangle( apu_state_t::triangle_t& state, Nes_Triangle& osc )
|
||||
{
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.length_counter, osc.length_counter );
|
||||
REFLECT( state.linear_counter, osc.linear_counter );
|
||||
REFLECT( state.phase, osc.phase );
|
||||
REFLECT( state.linear_mode, osc.reg_written [3] );
|
||||
}
|
||||
|
||||
static void reflect_noise( apu_state_t::noise_t& state, Nes_Noise& osc )
|
||||
{
|
||||
reflect_env( &state.env, osc );
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.length_counter, osc.length_counter );
|
||||
REFLECT( state.shift_reg, osc.noise );
|
||||
}
|
||||
|
||||
static void reflect_dmc( apu_state_t::dmc_t& state, Nes_Dmc& osc )
|
||||
{
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.remain, osc.length_counter );
|
||||
REFLECT( state.buf, osc.buf );
|
||||
REFLECT( state.bits_remain, osc.bits_remain );
|
||||
REFLECT( state.bits, osc.bits );
|
||||
REFLECT( state.buf_full, osc.buf_full );
|
||||
REFLECT( state.silence, osc.silence );
|
||||
REFLECT( state.irq_flag, osc.irq_flag );
|
||||
if ( mode )
|
||||
state.addr = osc.address | 0x8000;
|
||||
else
|
||||
osc.address = state.addr & 0x7fff;
|
||||
}
|
||||
};
|
||||
|
||||
void Nes_Apu::save_state( apu_state_t* state ) const
|
||||
{
|
||||
for ( int i = 0; i < osc_count * 4; i++ )
|
||||
{
|
||||
int index = i >> 2;
|
||||
state->apu.w40xx [i] = oscs [index]->regs [i & 3];
|
||||
//if ( index < 4 )
|
||||
// state->length_counters [index] = oscs [index]->length_counter;
|
||||
}
|
||||
state->apu.w40xx [0x11] = dmc.dac;
|
||||
|
||||
state->apu.w4015 = osc_enables;
|
||||
state->apu.w4017 = frame_mode;
|
||||
state->apu.frame_delay = frame_delay;
|
||||
state->apu.frame_step = frame;
|
||||
state->apu.irq_flag = irq_flag;
|
||||
|
||||
typedef apu_reflection<1> refl;
|
||||
Nes_Apu& apu = *(Nes_Apu*) this; // const_cast
|
||||
refl::reflect_square ( state->square1, apu.square1 );
|
||||
refl::reflect_square ( state->square2, apu.square2 );
|
||||
refl::reflect_triangle( state->triangle, apu.triangle );
|
||||
refl::reflect_noise ( state->noise, apu.noise );
|
||||
refl::reflect_dmc ( state->dmc, apu.dmc );
|
||||
}
|
||||
|
||||
void Nes_Apu::load_state( apu_state_t const& state )
|
||||
{
|
||||
reset();
|
||||
|
||||
write_register( 0, 0x4017, state.apu.w4017 );
|
||||
write_register( 0, 0x4015, state.apu.w4015 );
|
||||
osc_enables = state.apu.w4015; // DMC clears bit 4
|
||||
|
||||
for ( int i = 0; i < osc_count * 4; i++ )
|
||||
{
|
||||
int n = state.apu.w40xx [i];
|
||||
int index = i >> 2;
|
||||
oscs [index]->regs [i & 3] = n;
|
||||
write_register( 0, 0x4000 + i, n );
|
||||
//if ( index < 4 )
|
||||
// oscs [index]->length_counter = state.length_counters [index];
|
||||
}
|
||||
|
||||
frame_delay = state.apu.frame_delay;
|
||||
frame = state.apu.frame_step;
|
||||
irq_flag = state.apu.irq_flag;
|
||||
|
||||
typedef apu_reflection<0> refl;
|
||||
apu_state_t& st = (apu_state_t&) state; // const_cast
|
||||
refl::reflect_square ( st.square1, square1 );
|
||||
refl::reflect_square ( st.square2, square2 );
|
||||
refl::reflect_triangle( st.triangle, triangle );
|
||||
refl::reflect_noise ( st.noise, noise );
|
||||
refl::reflect_dmc ( st.dmc, dmc );
|
||||
dmc.recalc_irq();
|
||||
|
||||
//force channels to have correct last_amp levels after load state
|
||||
square1.run(last_time, last_time);
|
||||
square2.run(last_time, last_time);
|
||||
triangle.run(last_time, last_time);
|
||||
noise.run(last_time, last_time);
|
||||
dmc.run(last_time, last_time);
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
|
||||
// NES APU state snapshot support
|
||||
|
||||
// Nes_Snd_Emu 0.1.7
|
||||
|
||||
#ifndef APU_STATE_H
|
||||
#define APU_STATE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "blargg_common.h"
|
||||
|
||||
struct apu_state_t
|
||||
{
|
||||
typedef uint8_t env_t [3];
|
||||
/*struct env_t {
|
||||
uint8_t delay;
|
||||
uint8_t env;
|
||||
uint8_t written;
|
||||
};*/
|
||||
|
||||
struct apu_t {
|
||||
uint8_t w40xx [0x14]; // $4000-$4013
|
||||
uint8_t w4015; // enables
|
||||
uint8_t w4017; // mode
|
||||
uint16_t frame_delay;
|
||||
uint8_t frame_step;
|
||||
uint8_t irq_flag;
|
||||
} apu;
|
||||
|
||||
struct square_t {
|
||||
uint16_t delay;
|
||||
env_t env;
|
||||
uint8_t length_counter;
|
||||
uint8_t phase;
|
||||
uint8_t swp_delay;
|
||||
uint8_t swp_reset;
|
||||
uint8_t unused2 [1];
|
||||
};
|
||||
|
||||
square_t square1;
|
||||
square_t square2;
|
||||
|
||||
struct triangle_t {
|
||||
uint16_t delay;
|
||||
uint8_t length_counter;
|
||||
uint8_t phase;
|
||||
uint8_t linear_counter;
|
||||
uint8_t linear_mode;
|
||||
} triangle;
|
||||
|
||||
struct noise_t {
|
||||
uint16_t delay;
|
||||
env_t env;
|
||||
uint8_t length_counter;
|
||||
uint16_t shift_reg;
|
||||
} noise;
|
||||
|
||||
struct dmc_t {
|
||||
uint16_t delay;
|
||||
uint16_t remain;
|
||||
uint16_t addr;
|
||||
uint8_t buf;
|
||||
uint8_t bits_remain;
|
||||
uint8_t bits;
|
||||
uint8_t buf_full;
|
||||
uint8_t silence;
|
||||
uint8_t irq_flag;
|
||||
} dmc;
|
||||
|
||||
//uint8_t length_counters [4];
|
||||
|
||||
enum { tag = 0x41505552 }; // 'APUR'
|
||||
void swap();
|
||||
};
|
||||
BOOST_STATIC_ASSERT( sizeof (apu_state_t) == 72 );
|
||||
|
||||
#endif
|
|
@ -1,17 +0,0 @@
|
|||
// Library configuration. Modify this file as necessary.
|
||||
|
||||
// File_Extractor 1.0.0
|
||||
#ifndef BLARGG_CONFIG_H
|
||||
#define BLARGG_CONFIG_H
|
||||
|
||||
#ifndef HAVE_STDINT_H
|
||||
#define HAVE_STDINT_H
|
||||
#endif
|
||||
|
||||
// Use standard config.h if present
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
|
@ -1,66 +0,0 @@
|
|||
|
||||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include "nes_data.h"
|
||||
|
||||
#include "blargg_endian.h"
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details. You should have received a copy of the GNU Lesser General
|
||||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
#define SWAP_BE( n ) (void) (set_be( &(n), (n) ))
|
||||
#define SWAP_LE( n ) (void) (set_le( &(n), (n) ))
|
||||
|
||||
void nes_block_t::swap()
|
||||
{
|
||||
SWAP_BE( tag );
|
||||
SWAP_LE( size );
|
||||
}
|
||||
|
||||
void nes_state_t::swap()
|
||||
{
|
||||
SWAP_LE( timestamp );
|
||||
SWAP_LE( frame_count );
|
||||
}
|
||||
|
||||
void cpu_state_t::swap()
|
||||
{
|
||||
SWAP_LE( pc );
|
||||
}
|
||||
|
||||
void ppu_state_t::swap()
|
||||
{
|
||||
SWAP_LE( vram_addr );
|
||||
SWAP_LE( vram_temp );
|
||||
SWAP_LE( decay_low );
|
||||
SWAP_LE( decay_high );
|
||||
}
|
||||
|
||||
void apu_state_t::swap()
|
||||
{
|
||||
SWAP_LE( apu.frame_delay );
|
||||
SWAP_LE( square1.delay );
|
||||
SWAP_LE( square2.delay );
|
||||
SWAP_LE( triangle.delay );
|
||||
SWAP_LE( noise.delay );
|
||||
SWAP_LE( noise.shift_reg );
|
||||
SWAP_LE( dmc.delay );
|
||||
SWAP_LE( dmc.remain );
|
||||
SWAP_LE( dmc.addr );
|
||||
}
|
||||
|
||||
void joypad_state_t::swap()
|
||||
{
|
||||
SWAP_LE( joypad_latches [0] );
|
||||
SWAP_LE( joypad_latches [1] );
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
|
||||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include "nes_util.h"
|
||||
|
||||
#include "Nes_Cart.h"
|
||||
#include "Nes_Emu.h"
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details. You should have received a copy of the GNU Lesser General
|
||||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
// game_genie_patch_t
|
||||
|
||||
const char *game_genie_patch_t::decode( const char* in )
|
||||
{
|
||||
int const code_len = 8;
|
||||
unsigned char result [code_len] = { 0 };
|
||||
int in_len = strlen( in );
|
||||
if ( in_len != 6 && in_len != 8 )
|
||||
return "Game Genie code is wrong length";
|
||||
for ( int i = 0; i < code_len; i++ )
|
||||
{
|
||||
char c = 'A';
|
||||
if ( i < in_len )
|
||||
c = toupper( in [i] );
|
||||
|
||||
static char const letters [17] = "AEPOZXLUGKISTVYN";
|
||||
char const* p = strchr( (char*) letters, c );
|
||||
if ( !p )
|
||||
return "Game Genie code had invalid character";
|
||||
int n = p - letters;
|
||||
|
||||
result [i] |= n >> 1;
|
||||
result [(i + 1) % code_len] |= (n << 3) & 0x0f;
|
||||
}
|
||||
|
||||
addr = result [3]<<12 | result [5]<<8 | result [2]<<4 | result [4];
|
||||
change_to = result [1]<<4 | result [0];
|
||||
compare_with = -1;
|
||||
if ( addr & 0x8000 )
|
||||
compare_with = result [7]<<4 | result [6];
|
||||
addr |= 0x8000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int game_genie_patch_t::apply( Nes_Cart& cart ) const
|
||||
{
|
||||
// determine bank size
|
||||
long bank_size = 32 * 1024L; // mappers 0, 2, 3, 7, 11, 34, 71, 87
|
||||
switch ( cart.mapper_code() )
|
||||
{
|
||||
case 1: // MMC1
|
||||
case 71: // Camerica
|
||||
case 232: // Quattro
|
||||
bank_size = 16 * 1024L;
|
||||
break;
|
||||
|
||||
case 4: // MMC3
|
||||
case 5: // MMC5
|
||||
case 24: // VRC6
|
||||
case 26: // VRC6
|
||||
case 69: // FME7
|
||||
bank_size = 8 * 1024L;
|
||||
break;
|
||||
}
|
||||
|
||||
// patch each bank (not very good, since it might patch banks that never occupy
|
||||
// that address)
|
||||
int mask = (compare_with >= 0 ? ~0 : 0);
|
||||
uint8_t* p = cart.prg() + addr % bank_size;
|
||||
int count = 0;
|
||||
for ( int n = cart.prg_size() / bank_size; n--; p += bank_size )
|
||||
{
|
||||
if ( !((*p ^ compare_with) & mask) )
|
||||
{
|
||||
*p = change_to;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Cheat_Value_Finder
|
||||
|
||||
Cheat_Value_Finder::Cheat_Value_Finder()
|
||||
{
|
||||
emu = NULL;
|
||||
}
|
||||
|
||||
void Cheat_Value_Finder::start( Nes_Emu* new_emu )
|
||||
{
|
||||
emu = new_emu;
|
||||
pos = 0;
|
||||
memcpy( original, emu->low_mem(), low_mem_size );
|
||||
memset( changed, 0, low_mem_size );
|
||||
}
|
||||
|
||||
void Cheat_Value_Finder::rescan()
|
||||
{
|
||||
uint8_t const* low_mem = emu->low_mem();
|
||||
for ( int i = 0; i < low_mem_size; i++ )
|
||||
changed [i] |= original [i] ^ low_mem [i];
|
||||
memcpy( original, emu->low_mem(), low_mem_size );
|
||||
}
|
||||
|
||||
void Cheat_Value_Finder::search( int new_original, int new_changed )
|
||||
{
|
||||
original_value = new_original;
|
||||
changed_value = new_changed;
|
||||
pos = -1;
|
||||
}
|
||||
|
||||
int Cheat_Value_Finder::next_match( int* addr )
|
||||
{
|
||||
uint8_t const* low_mem = emu->low_mem();
|
||||
while ( ++pos < low_mem_size )
|
||||
{
|
||||
if ( !changed [pos] )
|
||||
{
|
||||
int old = (original [pos] - original_value) & 0xff;
|
||||
int cur = (low_mem [pos] - changed_value) & 0xff;
|
||||
|
||||
if ( old == cur )
|
||||
{
|
||||
if ( addr )
|
||||
*addr = pos;
|
||||
return (char) old; // sign-extend
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return no_match;
|
||||
}
|
||||
|
||||
int Cheat_Value_Finder::change_value( int new_value )
|
||||
{
|
||||
int result = emu->low_mem() [pos];
|
||||
emu->low_mem() [pos] = new_value;
|
||||
return result;
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
|
||||
// Experimental utilities for NES emulator
|
||||
|
||||
// Nes_Emu 0.7.0
|
||||
|
||||
#ifndef NES_UTIL_H
|
||||
#define NES_UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "blargg_common.h"
|
||||
|
||||
class Nes_Emu;
|
||||
class Nes_Cart;
|
||||
|
||||
struct game_genie_patch_t
|
||||
{
|
||||
unsigned addr; // always 0x8000 or greater
|
||||
int change_to;
|
||||
int compare_with; // if -1, always change byte
|
||||
|
||||
// Decode Game Genie code
|
||||
const char *decode( const char* in );
|
||||
|
||||
// Apply patch to cartridge data. Might not work for some codes, since this really
|
||||
// requires emulator support. Returns number of bytes changed, where 0
|
||||
// means patch wasn't for that cartridge.
|
||||
int apply( Nes_Cart& ) const;
|
||||
};
|
||||
|
||||
class Cheat_Value_Finder {
|
||||
public:
|
||||
Cheat_Value_Finder();
|
||||
|
||||
// Start scanning emulator's memory for values that are constantly changing.
|
||||
void start( Nes_Emu* );
|
||||
|
||||
// Rescan memory and eliminate any changed bytes from later matching.
|
||||
// Should be called many times after begin_scan() and before begin_matching().
|
||||
void rescan();
|
||||
|
||||
// Start search for any bytes which changed by difference between original and
|
||||
// changed values.
|
||||
void search( int original, int changed );
|
||||
|
||||
// Get next match and return its delta from changed value (closer to 0
|
||||
// is more likely to be a match), or no_match if there are no more matches.
|
||||
// Optionally returns address of matched byte.
|
||||
enum { no_match = 0x100 };
|
||||
int next_match( int* addr = NULL );
|
||||
|
||||
// Change current match to new value. Returns previous value.
|
||||
int change_value( int new_value );
|
||||
|
||||
private:
|
||||
Nes_Emu* emu;
|
||||
int original_value;
|
||||
int changed_value;
|
||||
int pos;
|
||||
enum { low_mem_size = 0x800 };
|
||||
uint8_t original [low_mem_size];
|
||||
uint8_t changed [low_mem_size];
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,20 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <Nes_Emu.h>
|
||||
#include <Nes_State.h>
|
||||
#include <string>
|
||||
#include <utils.hpp>
|
||||
#include "sha1/sha1.hpp"
|
||||
|
||||
#ifdef USE_ORIGINAL_QUICKNES
|
||||
extern void register_misc_mappers();
|
||||
extern void register_extra_mappers();
|
||||
#endif
|
||||
|
||||
#define _LOW_MEM_SIZE 0x800
|
||||
#define _HIGH_MEM_SIZE 0x2000
|
||||
#define _NAMETABLES_MEM_SIZE 0x1000
|
||||
|
||||
// Size of image generated in graphics buffer
|
||||
static const uint16_t image_width = 256;
|
||||
static const uint16_t image_height = 240;
|
||||
|
||||
class EmuInstance
|
||||
{
|
||||
public:
|
||||
|
@ -22,110 +18,7 @@ class EmuInstance
|
|||
typedef uint8_t inputType;
|
||||
|
||||
// Deleting default constructors
|
||||
EmuInstance() = delete;
|
||||
EmuInstance(EmuInstance& e) = delete;
|
||||
~EmuInstance() = default;
|
||||
|
||||
EmuInstance(const std::string& romFilePath, const std::string& stateFilePath)
|
||||
{
|
||||
// Creating new emulator
|
||||
_nes = new Nes_Emu;
|
||||
|
||||
// If running the original QuickNES, register extra mappers now
|
||||
#ifdef USE_ORIGINAL_QUICKNES
|
||||
register_misc_mappers();
|
||||
register_extra_mappers();
|
||||
#endif
|
||||
|
||||
// Loading ROM
|
||||
std::string romData;
|
||||
bool status = loadStringFromFile(romData, romFilePath.c_str());
|
||||
if (status == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", romFilePath.c_str());
|
||||
|
||||
// Calculating ROM hash value
|
||||
_romSHA1String = SHA1::GetHash((uint8_t*)romData.data(), romData.size());
|
||||
|
||||
// Loading the rom into the emulator
|
||||
Mem_File_Reader romReader(romData.data(), (int)romData.size());
|
||||
Auto_File_Reader romFile(romReader);
|
||||
auto result = _nes->load_ines(romFile);
|
||||
if (result != 0) EXIT_WITH_ERROR("Could not initialize emulator with rom file: %s\n", romFilePath.c_str());
|
||||
|
||||
// Getting state size to use
|
||||
_stateSize = getStateSizeImpl();
|
||||
|
||||
// Loading state file, if specified
|
||||
if (stateFilePath != "") loadStateFile(stateFilePath);
|
||||
}
|
||||
|
||||
uint8_t* getLowMem() { return _nes->low_mem(); };
|
||||
uint8_t* getNametableMem() { return _nes->nametable_mem(); };
|
||||
uint8_t* getHighMem() { return _nes->high_mem();};
|
||||
const uint8_t* getChrMem() { return _nes->chr_mem();};
|
||||
size_t getChrMemSize() { return _nes->chr_size();};
|
||||
// uint8_t* getSpriteRAM() { return _nes->spr_mem(); }
|
||||
// uint16_t getSpriteRAMSize() { return _nes->spr_mem_size(); }
|
||||
|
||||
const std::string getRomSHA1() const { return _romSHA1String; };
|
||||
|
||||
void loadStateFile(const std::string& stateFilePath)
|
||||
{
|
||||
// Loading state data
|
||||
std::string stateData;
|
||||
bool status = loadStringFromFile(stateData, stateFilePath.c_str());
|
||||
|
||||
if (status == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", stateFilePath.c_str());
|
||||
Mem_File_Reader stateReader(stateData.data(), (int)stateData.size());
|
||||
Auto_File_Reader stateFile(stateReader);
|
||||
|
||||
// Loading state data into state object
|
||||
Nes_State state;
|
||||
state.read(stateFile);
|
||||
|
||||
// Loading state object into the emulator
|
||||
_nes->load_state(state);
|
||||
}
|
||||
|
||||
inline size_t getStateSize() const { return _stateSize; }
|
||||
|
||||
inline hash_t getStateHash()
|
||||
{
|
||||
MetroHash128 hash;
|
||||
|
||||
uint8_t stateData[_stateSize];
|
||||
serializeState(stateData);
|
||||
|
||||
hash.Update(getLowMem(), _LOW_MEM_SIZE);
|
||||
hash.Update(getHighMem(), _HIGH_MEM_SIZE);
|
||||
hash.Update(getNametableMem(), _NAMETABLES_MEM_SIZE);
|
||||
hash.Update(getChrMem(), getChrMemSize());
|
||||
|
||||
hash_t result;
|
||||
hash.Finalize(reinterpret_cast<uint8_t *>(&result));
|
||||
return result;
|
||||
}
|
||||
|
||||
void saveStateFile(const std::string& stateFilePath) const
|
||||
{
|
||||
std::string stateData;
|
||||
stateData.resize(_stateSize);
|
||||
serializeState((uint8_t*)stateData.data());
|
||||
saveStringToFile(stateData, stateFilePath.c_str());
|
||||
}
|
||||
|
||||
void serializeState(uint8_t* state) const
|
||||
{
|
||||
Mem_Writer w(state, _stateSize, 0);
|
||||
Auto_File_Writer a(w);
|
||||
_nes->save_state(a);
|
||||
}
|
||||
|
||||
void deserializeState(const uint8_t* state)
|
||||
{
|
||||
Mem_File_Reader r(state, _stateSize);
|
||||
Auto_File_Reader a(r);
|
||||
_nes->load_state(a);
|
||||
}
|
||||
virtual ~EmuInstance() = default;
|
||||
|
||||
// Controller input bits
|
||||
// 0 - A / 1
|
||||
|
@ -184,55 +77,102 @@ class EmuInstance
|
|||
moveString += "|";
|
||||
return moveString;
|
||||
}
|
||||
|
||||
void enableRendering() { _doRendering = true; }
|
||||
void disableRendering() { _doRendering = false; }
|
||||
|
||||
void advanceState(const std::string& move)
|
||||
|
||||
inline void advanceState(const std::string& move)
|
||||
{
|
||||
if (move.find("r") != std::string::npos) _nes->reset(false);
|
||||
if (move.find("r") != std::string::npos) doSoftReset();
|
||||
|
||||
advanceState(moveStringToCode(move), 0);
|
||||
advanceStateImpl(moveStringToCode(move), 0);
|
||||
}
|
||||
|
||||
void advanceState(const inputType controller1, const inputType controller2)
|
||||
inline size_t getStateSize() const { return _stateSize; }
|
||||
inline std::string getRomSHA1() const { return _romSHA1String; }
|
||||
|
||||
inline hash_t getStateHash() const
|
||||
{
|
||||
if (_doRendering == true) _nes->emulate_frame(controller1, controller2);
|
||||
if (_doRendering == false) _nes->emulate_skip_frame(controller1, controller2);
|
||||
MetroHash128 hash;
|
||||
|
||||
uint8_t stateData[_stateSize];
|
||||
serializeState(stateData);
|
||||
|
||||
hash.Update(getLowMem(), _LOW_MEM_SIZE);
|
||||
hash.Update(getHighMem(), _HIGH_MEM_SIZE);
|
||||
hash.Update(getNametableMem(), _NAMETABLES_MEM_SIZE);
|
||||
hash.Update(getChrMem(), getChrMemSize());
|
||||
|
||||
hash_t result;
|
||||
hash.Finalize(reinterpret_cast<uint8_t *>(&result));
|
||||
return result;
|
||||
}
|
||||
|
||||
Nes_Emu* getInternalEmulator() const { return _nes; }
|
||||
inline void enableRendering() { _doRendering = true; };
|
||||
inline void disableRendering() { _doRendering = false; };
|
||||
|
||||
private:
|
||||
inline void loadStateFile(const std::string& stateFilePath)
|
||||
{
|
||||
std::string stateData;
|
||||
bool status = loadStringFromFile(stateData, stateFilePath);
|
||||
if (status == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", stateFilePath.c_str());
|
||||
deserializeState((uint8_t*)stateData.data());
|
||||
}
|
||||
|
||||
inline void saveStateFile(const std::string& stateFilePath) const
|
||||
{
|
||||
std::string stateData;
|
||||
stateData.resize(_stateSize);
|
||||
serializeState((uint8_t*)stateData.data());
|
||||
saveStringToFile(stateData, stateFilePath.c_str());
|
||||
}
|
||||
|
||||
inline void loadROMFile(const std::string& romFilePath)
|
||||
{
|
||||
// Loading ROM data
|
||||
bool status = loadStringFromFile(_romData, romFilePath);
|
||||
if (status == false) EXIT_WITH_ERROR("Could not find/read ROM file: %s\n", romFilePath.c_str());
|
||||
|
||||
// Calculating ROM hash value
|
||||
_romSHA1String = SHA1::GetHash((uint8_t*)_romData.data(), _romData.size());
|
||||
|
||||
// Actually loading rom file
|
||||
status = loadROMFileImpl(_romData);
|
||||
if (status == false) EXIT_WITH_ERROR("Could not process ROM file: %s\n", romFilePath.c_str());
|
||||
|
||||
// Detecting state size
|
||||
_stateSize = getStateSizeImpl();
|
||||
}
|
||||
|
||||
// Virtual functions
|
||||
|
||||
virtual bool loadROMFileImpl(const std::string& romFilePath) = 0;
|
||||
virtual void advanceStateImpl(const inputType controller1, const inputType controller2) = 0;
|
||||
virtual uint8_t* getLowMem() const = 0;
|
||||
virtual uint8_t* getNametableMem() const = 0;
|
||||
virtual uint8_t* getHighMem() const = 0;
|
||||
virtual const uint8_t* getChrMem() const = 0;
|
||||
virtual size_t getChrMemSize() const = 0;
|
||||
virtual void serializeState(uint8_t* state) const = 0;
|
||||
virtual void deserializeState(const uint8_t* state) = 0;
|
||||
virtual size_t getStateSizeImpl() const = 0;
|
||||
virtual void doSoftReset() = 0;
|
||||
virtual void doHardReset() = 0;
|
||||
virtual std::string getCoreName() const = 0;
|
||||
virtual void* getInternalEmulatorPointer() const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
EmuInstance() = default;
|
||||
|
||||
// Storage for the state
|
||||
size_t _stateSize;
|
||||
|
||||
// Flag to determine whether to enable/disable rendering
|
||||
bool _doRendering = true;
|
||||
|
||||
private:
|
||||
|
||||
inline size_t getStateSizeImpl() const
|
||||
{
|
||||
#ifdef USE_ORIGINAL_QUICKNES
|
||||
#define _DUMMY_SIZE 65536
|
||||
uint8_t data[_DUMMY_SIZE];
|
||||
Mem_Writer w(data, _DUMMY_SIZE);
|
||||
Auto_File_Writer a(w);
|
||||
_nes->save_state(a);
|
||||
return w.size();
|
||||
#else
|
||||
// Using dry writer to just obtain the state size
|
||||
Dry_Writer w;
|
||||
Auto_File_Writer a(w);
|
||||
_nes->save_state(a);
|
||||
return w.size();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Emulator instance
|
||||
Nes_Emu* _nes;
|
||||
|
||||
// State size for the given rom
|
||||
size_t _stateSize;
|
||||
// Storage for the ROM data
|
||||
std::string _romData;
|
||||
|
||||
// SHA1 rom hash
|
||||
std::string _romSHA1String;
|
||||
|
||||
};
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
# Getting core configuration
|
||||
subdir('core')
|
||||
# Getting core configurations
|
||||
subdir('quickNES')
|
||||
subdir('quickerNES')
|
||||
|
||||
# quickerNES Application Configuration
|
||||
# Tool Configuration
|
||||
|
||||
quickerNESApplicationDependency = declare_dependency(
|
||||
toolDependency = declare_dependency(
|
||||
include_directories : include_directories(['.', '../extern']),
|
||||
sources : [ '../extern/metrohash128/metrohash128.cpp' ]
|
||||
)
|
||||
)
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#define _INVERSE_FRAME_RATE 16667
|
||||
|
||||
class Nes_Emu;
|
||||
|
||||
struct stepData_t
|
||||
{
|
||||
std::string input;
|
||||
|
@ -37,8 +39,11 @@ class PlaybackInstance
|
|||
// Initializes the playback module instance
|
||||
PlaybackInstance(EmuInstance* emu, const std::vector<std::string>& sequence, const std::string& overlayPath = "") : _emu(emu)
|
||||
{
|
||||
// Enabling emulation rendering
|
||||
_emu->enableRendering();
|
||||
|
||||
// Loading Emulator instance HQN
|
||||
_hqnState.m_emu = _emu->getInternalEmulator();
|
||||
_hqnState.setEmulatorPointer(_emu->getInternalEmulatorPointer());
|
||||
static uint8_t video_buffer[Nes_Emu::image_width * Nes_Emu::image_height];
|
||||
_hqnState.m_emu->set_pixels(video_buffer, Nes_Emu::image_width+8);
|
||||
|
||||
|
@ -149,7 +154,7 @@ class PlaybackInstance
|
|||
// Since we do not store the blit information (too much memory), we need to load the previous frame and re-run the input
|
||||
|
||||
// If its the first step, then simply reset
|
||||
if (stepId == 0) _emu->getInternalEmulator()->reset();
|
||||
if (stepId == 0) _emu->doHardReset();
|
||||
|
||||
// Else we load the previous frame
|
||||
if (stepId > 0)
|
||||
|
@ -161,7 +166,7 @@ class PlaybackInstance
|
|||
|
||||
// Updating image
|
||||
int32_t curBlit[BLIT_SIZE];
|
||||
saveBlit(_emu->getInternalEmulator(), curBlit, hqn::HQNState::NES_VIDEO_PALETTE, 0, 0, 0, 0);
|
||||
saveBlit(_emu->getInternalEmulatorPointer(), curBlit, hqn::HQNState::NES_VIDEO_PALETTE, 0, 0, 0, 0);
|
||||
_hqnGUI->update_blit(curBlit, _overlayBaseSurface, overlayButtonASurface, overlayButtonBSurface, overlayButtonSelectSurface, overlayButtonStartSurface, overlayButtonLeftSurface, overlayButtonRightSurface, overlayButtonUpSurface, overlayButtonDownSurface);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,14 @@
|
|||
#include "emuInstance.hpp"
|
||||
#include "playbackInstance.hpp"
|
||||
|
||||
#ifdef _USE_QUICKNES
|
||||
#include "quickNESInstance.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef _USE_QUICKERNES
|
||||
#include "quickerNESInstance.hpp"
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Parsing command line arguments
|
||||
|
@ -71,7 +79,19 @@ int main(int argc, char *argv[])
|
|||
refreshTerminal();
|
||||
|
||||
// Creating emulator instance
|
||||
auto e = EmuInstance(romFilePath, stateFilePath);
|
||||
#ifdef _USE_QUICKNES
|
||||
auto e = QuickNESInstance();
|
||||
#endif
|
||||
|
||||
#ifdef _USE_QUICKERNES
|
||||
auto e = QuickerNESInstance();
|
||||
#endif
|
||||
|
||||
// Loading ROM File
|
||||
e.loadROMFile(romFilePath);
|
||||
|
||||
// If an initial state is provided, load it now
|
||||
if (stateFilePath != "") e.loadStateFile(stateFilePath);
|
||||
|
||||
// Creating playback instance
|
||||
auto p = PlaybackInstance(&e, sequence);
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/* Copyright notice for this file:
|
||||
* Copyright (C) 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* This mapper was added by retrowertz for Libretro port of QuickNES.
|
||||
*
|
||||
* Mapper 180 Crazy Climber
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Nes_Mapper.h"
|
||||
|
||||
template < bool _is152 >
|
||||
class Mapper_74x161x162x32 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper_74x161x162x32()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
if ( _is152 == 0 )
|
||||
bank = ~0;
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
if ( _is152 )
|
||||
write( 0, 0, bank );
|
||||
else
|
||||
{
|
||||
intercept_writes( 0x6000, 1 );
|
||||
write_intercepted( 0, 0x6000, bank );
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool write_intercepted( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( ( addr != 0x6000 ) || _is152 )
|
||||
return false;
|
||||
|
||||
bank = data;
|
||||
set_prg_bank( 0x8000, bank_32k, ( bank >> 4 ) & 0x03 );
|
||||
set_chr_bank( 0x0000, bank_8k, ( ( bank >> 4 ) & 0x04 ) | ( bank & 0x03 ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( _is152 == 0) return;
|
||||
|
||||
bank = handle_bus_conflict (addr, data );
|
||||
set_prg_bank( 0x8000, bank_16k, ( bank >> 4 ) & 0x07 );
|
||||
set_chr_bank( 0x0000, bank_8k, bank & 0x0F );
|
||||
mirror_single( ( bank >> 7) & 0x01 );
|
||||
}
|
||||
|
||||
uint8_t bank;
|
||||
};
|
||||
|
||||
void register_mapper_70();
|
||||
void register_mapper_70()
|
||||
{
|
||||
register_mapper< Mapper_74x161x162x32 <true> > ( 70 );
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
# quickNES Core sources
|
||||
|
||||
quickNESCoreSrc = [
|
||||
quickNESSrc = [
|
||||
'QuickNES_Core/nes_emu/abstract_file.cpp',
|
||||
'QuickNES_Core/nes_emu/apu_state.cpp',
|
||||
'QuickNES_Core/nes_emu/Blip_Buffer.cpp',
|
||||
|
@ -64,13 +64,15 @@ quickNESCoreSrc = [
|
|||
'QuickNES_Core/nes_emu/Mapper_TaitoX1005.cpp',
|
||||
'QuickNES_Core/nes_emu/Mapper_TaitoTC0190.cpp',
|
||||
'QuickNES_Core/nes_emu/Mapper_Un1rom.cpp',
|
||||
'QuickNES_Core/nes_emu/nes_ntsc.cpp'
|
||||
'QuickNES_Core/nes_emu/nes_ntsc.cpp',
|
||||
'Mapper_70.cpp',
|
||||
'quickNESInstance.hpp'
|
||||
]
|
||||
|
||||
# quickNES Core Configuration
|
||||
|
||||
quickNESCoreDependency = declare_dependency(
|
||||
compile_args : [ '-DUSE_ORIGINAL_QUICKNES'],
|
||||
include_directories : include_directories(['QuickNES_Core/nes_emu']),
|
||||
sources : [ quickNESCoreSrc ]
|
||||
quickNESDependency = declare_dependency(
|
||||
compile_args : [ '-D_USE_QUICKNES' ],
|
||||
include_directories : include_directories(['.', 'QuickNES_Core/nes_emu']),
|
||||
sources : [ quickNESSrc ]
|
||||
)
|
|
@ -0,0 +1,92 @@
|
|||
#pragma once
|
||||
|
||||
#include <Nes_Emu.h>
|
||||
#include <Nes_State.h>
|
||||
#include <emuInstance.hpp>
|
||||
|
||||
#define _DUMMY_SIZE 65536
|
||||
|
||||
extern void register_misc_mappers();
|
||||
extern void register_extra_mappers();
|
||||
extern void register_mapper_70();
|
||||
|
||||
class QuickNESInstance : public EmuInstance
|
||||
{
|
||||
public:
|
||||
|
||||
QuickNESInstance() : EmuInstance()
|
||||
{
|
||||
// Creating new emulator
|
||||
_nes = new Nes_Emu;
|
||||
|
||||
// Allocating video buffer
|
||||
video_buffer = (uint8_t*) malloc(image_width * image_height);
|
||||
|
||||
// Setting video buffer
|
||||
_nes->set_pixels(video_buffer, image_width+8);
|
||||
|
||||
// If running the original QuickNES, register extra mappers now
|
||||
register_misc_mappers();
|
||||
register_extra_mappers();
|
||||
register_mapper_70();
|
||||
}
|
||||
|
||||
virtual bool loadROMFileImpl(const std::string& romData) override
|
||||
{
|
||||
// Loading rom data
|
||||
Mem_File_Reader romReader(romData.data(), (int)romData.size());
|
||||
Auto_File_Reader romFile(romReader);
|
||||
auto result = _nes->load_ines(romFile);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
uint8_t* getLowMem() const override { return _nes->low_mem(); };
|
||||
uint8_t* getNametableMem() const override { return _nes->nametable_mem(); };
|
||||
uint8_t* getHighMem() const override { return _nes->high_mem();};
|
||||
const uint8_t* getChrMem() const override { return _nes->chr_mem();};
|
||||
size_t getChrMemSize() const override { return _nes->chr_size();};
|
||||
|
||||
void serializeState(uint8_t* state) const override
|
||||
{
|
||||
Mem_Writer w(state, _stateSize, 0);
|
||||
Auto_File_Writer a(w);
|
||||
_nes->save_state(a);
|
||||
}
|
||||
|
||||
void deserializeState(const uint8_t* state) override
|
||||
{
|
||||
Mem_File_Reader r(state, _stateSize);
|
||||
Auto_File_Reader a(r);
|
||||
_nes->load_state(a);
|
||||
}
|
||||
|
||||
void advanceStateImpl(const inputType controller1, const inputType controller2) override
|
||||
{
|
||||
if (_doRendering == true) _nes->emulate_frame(controller1, controller2);
|
||||
if (_doRendering == false) _nes->emulate_skip_frame(controller1, controller2);
|
||||
}
|
||||
|
||||
std::string getCoreName() const override { return "QuickNES"; }
|
||||
void doSoftReset() override { _nes->reset(false); }
|
||||
void doHardReset() override { _nes->reset(true); }
|
||||
|
||||
void* getInternalEmulatorPointer() const override { return _nes; }
|
||||
|
||||
private:
|
||||
|
||||
inline size_t getStateSizeImpl() const override
|
||||
{
|
||||
uint8_t* data = (uint8_t*) malloc (_DUMMY_SIZE);
|
||||
Mem_Writer w(data, _DUMMY_SIZE);
|
||||
Auto_File_Writer a(w);
|
||||
_nes->save_state(a);
|
||||
free(data);
|
||||
return w.size();
|
||||
}
|
||||
|
||||
// Video buffer
|
||||
uint8_t* video_buffer;
|
||||
|
||||
// Emulator instance
|
||||
Nes_Emu* _nes;
|
||||
};
|
|
@ -5,17 +5,95 @@
|
|||
// Nes_Snd_Emu 0.1.7
|
||||
|
||||
#include <cstdint>
|
||||
#include "blargg_source.h"
|
||||
#include "blargg_endian.h"
|
||||
|
||||
typedef long nes_time_t; // CPU clock cycle count
|
||||
typedef unsigned nes_addr_t; // 16-bit memory address
|
||||
|
||||
#include "Nes_Oscs.h"
|
||||
|
||||
struct apu_state_t;
|
||||
class Nes_Buffer;
|
||||
|
||||
class Nes_Apu {
|
||||
public:
|
||||
|
||||
typedef uint8_t env_t [3];
|
||||
/*struct env_t {
|
||||
uint8_t delay;
|
||||
uint8_t env;
|
||||
uint8_t written;
|
||||
};*/
|
||||
|
||||
struct apu_t {
|
||||
uint8_t w40xx [0x14]; // $4000-$4013
|
||||
uint8_t w4015; // enables
|
||||
uint8_t w4017; // mode
|
||||
uint16_t frame_delay;
|
||||
uint8_t frame_step;
|
||||
uint8_t irq_flag;
|
||||
};
|
||||
|
||||
struct square_t {
|
||||
uint16_t delay;
|
||||
env_t env;
|
||||
uint8_t length_counter;
|
||||
uint8_t phase;
|
||||
uint8_t swp_delay;
|
||||
uint8_t swp_reset;
|
||||
uint8_t unused2 [1];
|
||||
};
|
||||
|
||||
struct triangle_t {
|
||||
uint16_t delay;
|
||||
uint8_t length_counter;
|
||||
uint8_t phase;
|
||||
uint8_t linear_counter;
|
||||
uint8_t linear_mode;
|
||||
};
|
||||
|
||||
struct noise_t {
|
||||
uint16_t delay;
|
||||
env_t env;
|
||||
uint8_t length_counter;
|
||||
uint16_t shift_reg;
|
||||
};
|
||||
|
||||
struct dmc_t {
|
||||
uint16_t delay;
|
||||
uint16_t remain;
|
||||
uint16_t addr;
|
||||
uint8_t buf;
|
||||
uint8_t bits_remain;
|
||||
uint8_t bits;
|
||||
uint8_t buf_full;
|
||||
uint8_t silence;
|
||||
uint8_t irq_flag;
|
||||
};
|
||||
|
||||
struct apu_state_t
|
||||
{
|
||||
apu_t apu;
|
||||
square_t square1;
|
||||
square_t square2;
|
||||
triangle_t triangle;
|
||||
noise_t noise;
|
||||
dmc_t dmc;
|
||||
|
||||
enum { tag = 0x41505552 }; // 'APUR'
|
||||
void swap()
|
||||
{
|
||||
SWAP_LE( apu.frame_delay );
|
||||
SWAP_LE( square1.delay );
|
||||
SWAP_LE( square2.delay );
|
||||
SWAP_LE( triangle.delay );
|
||||
SWAP_LE( noise.delay );
|
||||
SWAP_LE( noise.shift_reg );
|
||||
SWAP_LE( dmc.delay );
|
||||
SWAP_LE( dmc.remain );
|
||||
SWAP_LE( dmc.addr );
|
||||
}
|
||||
};
|
||||
BOOST_STATIC_ASSERT( sizeof (apu_state_t) == 72 );
|
||||
|
||||
Nes_Apu();
|
||||
~Nes_Apu();
|
||||
|
||||
|
@ -172,3 +250,125 @@ inline nes_time_t Nes_Dmc::next_read_time() const
|
|||
|
||||
inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); }
|
||||
|
||||
|
||||
template<int mode>
|
||||
struct apu_reflection
|
||||
{
|
||||
#define REFLECT( apu, state ) (mode ? void (apu = state) : void (state = apu))
|
||||
|
||||
static void reflect_env( Nes_Apu::env_t* state, Nes_Envelope& osc )
|
||||
{
|
||||
REFLECT( (*state) [0], osc.env_delay );
|
||||
REFLECT( (*state) [1], osc.envelope );
|
||||
REFLECT( (*state) [2], osc.reg_written [3] );
|
||||
}
|
||||
|
||||
static void reflect_square( Nes_Apu::square_t& state, Nes_Square& osc )
|
||||
{
|
||||
reflect_env( &state.env, osc );
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.length_counter, osc.length_counter );
|
||||
REFLECT( state.phase, osc.phase );
|
||||
REFLECT( state.swp_delay, osc.sweep_delay );
|
||||
REFLECT( state.swp_reset, osc.reg_written [1] );
|
||||
}
|
||||
|
||||
static void reflect_triangle( Nes_Apu::triangle_t& state, Nes_Triangle& osc )
|
||||
{
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.length_counter, osc.length_counter );
|
||||
REFLECT( state.linear_counter, osc.linear_counter );
|
||||
REFLECT( state.phase, osc.phase );
|
||||
REFLECT( state.linear_mode, osc.reg_written [3] );
|
||||
}
|
||||
|
||||
static void reflect_noise( Nes_Apu::noise_t& state, Nes_Noise& osc )
|
||||
{
|
||||
reflect_env( &state.env, osc );
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.length_counter, osc.length_counter );
|
||||
REFLECT( state.shift_reg, osc.noise );
|
||||
}
|
||||
|
||||
static void reflect_dmc( Nes_Apu::dmc_t& state, Nes_Dmc& osc )
|
||||
{
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.remain, osc.length_counter );
|
||||
REFLECT( state.buf, osc.buf );
|
||||
REFLECT( state.bits_remain, osc.bits_remain );
|
||||
REFLECT( state.bits, osc.bits );
|
||||
REFLECT( state.buf_full, osc.buf_full );
|
||||
REFLECT( state.silence, osc.silence );
|
||||
REFLECT( state.irq_flag, osc.irq_flag );
|
||||
if ( mode )
|
||||
state.addr = osc.address | 0x8000;
|
||||
else
|
||||
osc.address = state.addr & 0x7fff;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline void Nes_Apu::save_state( apu_state_t* state ) const
|
||||
{
|
||||
for ( int i = 0; i < osc_count * 4; i++ )
|
||||
{
|
||||
int index = i >> 2;
|
||||
state->apu.w40xx [i] = oscs [index]->regs [i & 3];
|
||||
//if ( index < 4 )
|
||||
// state->length_counters [index] = oscs [index]->length_counter;
|
||||
}
|
||||
state->apu.w40xx [0x11] = dmc.dac;
|
||||
|
||||
state->apu.w4015 = osc_enables;
|
||||
state->apu.w4017 = frame_mode;
|
||||
state->apu.frame_delay = frame_delay;
|
||||
state->apu.frame_step = frame;
|
||||
state->apu.irq_flag = irq_flag;
|
||||
|
||||
typedef apu_reflection<1> refl;
|
||||
Nes_Apu& apu = *(Nes_Apu*) this; // const_cast
|
||||
refl::reflect_square ( state->square1, apu.square1 );
|
||||
refl::reflect_square ( state->square2, apu.square2 );
|
||||
refl::reflect_triangle( state->triangle, apu.triangle );
|
||||
refl::reflect_noise ( state->noise, apu.noise );
|
||||
refl::reflect_dmc ( state->dmc, apu.dmc );
|
||||
}
|
||||
|
||||
inline void Nes_Apu::load_state( apu_state_t const& state )
|
||||
{
|
||||
reset();
|
||||
|
||||
write_register( 0, 0x4017, state.apu.w4017 );
|
||||
write_register( 0, 0x4015, state.apu.w4015 );
|
||||
osc_enables = state.apu.w4015; // DMC clears bit 4
|
||||
|
||||
for ( int i = 0; i < osc_count * 4; i++ )
|
||||
{
|
||||
int n = state.apu.w40xx [i];
|
||||
int index = i >> 2;
|
||||
oscs [index]->regs [i & 3] = n;
|
||||
write_register( 0, 0x4000 + i, n );
|
||||
//if ( index < 4 )
|
||||
// oscs [index]->length_counter = state.length_counters [index];
|
||||
}
|
||||
|
||||
frame_delay = state.apu.frame_delay;
|
||||
frame = state.apu.frame_step;
|
||||
irq_flag = state.apu.irq_flag;
|
||||
|
||||
typedef apu_reflection<0> refl;
|
||||
apu_state_t& st = (apu_state_t&) state; // const_cast
|
||||
refl::reflect_square ( st.square1, square1 );
|
||||
refl::reflect_square ( st.square2, square2 );
|
||||
refl::reflect_triangle( st.triangle, triangle );
|
||||
refl::reflect_noise ( st.noise, noise );
|
||||
refl::reflect_dmc ( st.dmc, dmc );
|
||||
dmc.recalc_irq();
|
||||
|
||||
//force channels to have correct last_amp levels after load state
|
||||
square1.run(last_time, last_time);
|
||||
square2.run(last_time, last_time);
|
||||
triangle.run(last_time, last_time);
|
||||
noise.run(last_time, last_time);
|
||||
dmc.run(last_time, last_time);
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
|
||||
// NES cartridge data (PRG, CHR, mapper)
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details. You should have received a copy of the GNU Lesser General
|
||||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
|
||||
#ifndef NES_CART_H
|
||||
#define NES_CART_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "blargg_common.h"
|
||||
#include "abstract_file.h"
|
||||
|
||||
|
||||
class Nes_Cart {
|
||||
public:
|
||||
|
||||
Nes_Cart() = default;
|
||||
|
||||
struct ines_header_t {
|
||||
uint8_t signature [4];
|
||||
uint8_t prg_count; // number of 16K PRG banks
|
||||
uint8_t chr_count; // number of 8K CHR banks
|
||||
uint8_t flags; // MMMM FTBV Mapper low, Four-screen, Trainer, Battery, V mirror
|
||||
uint8_t flags2; // MMMM --XX Mapper high 4 bits
|
||||
uint8_t zero [8]; // if zero [7] is non-zero, treat flags2 as zero
|
||||
};
|
||||
BOOST_STATIC_ASSERT( sizeof (ines_header_t) == 16 );
|
||||
|
||||
// Load iNES file
|
||||
const char * load_ines( const uint8_t* buffer )
|
||||
{
|
||||
ines_header_t h;
|
||||
|
||||
size_t bufferPos = 0;
|
||||
{ size_t copySize = sizeof(ines_header_t); memcpy(&h, &buffer[bufferPos], copySize); bufferPos += copySize; }
|
||||
if ( h.zero [7] ) h.flags2 = 0;
|
||||
set_mapper( h.flags, h.flags2 );
|
||||
|
||||
// skip trainer
|
||||
if ( h.flags & 0x04 ) bufferPos += 512;
|
||||
|
||||
// Allocating memory for prg and chr
|
||||
prg_size_ = h.prg_count * 16 * 1024L;
|
||||
chr_size_ = h.chr_count * 8 * 1024L;
|
||||
|
||||
auto p = malloc(prg_size_ + chr_size_);
|
||||
prg_ = (uint8_t*)p;
|
||||
chr_ = &prg_[prg_size_];
|
||||
|
||||
{ size_t copySize = prg_size(); memcpy(prg(), &buffer[bufferPos], copySize); bufferPos += copySize; }
|
||||
{ size_t copySize = chr_size(); memcpy(chr(), &buffer[bufferPos], copySize); bufferPos += copySize; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline bool has_battery_ram() const { return mapper & 0x02; }
|
||||
|
||||
// Set mapper and information bytes. LSB and MSB are the standard iNES header
|
||||
// bytes at offsets 6 and 7.
|
||||
inline void set_mapper( int mapper_lsb, int mapper_msb )
|
||||
{
|
||||
mapper = mapper_msb * 0x100 + mapper_lsb;
|
||||
}
|
||||
|
||||
inline int mapper_code() const { return ((mapper >> 8) & 0xf0) | ((mapper >> 4) & 0x0f); }
|
||||
|
||||
// Size of PRG data
|
||||
long prg_size() const { return prg_size_; }
|
||||
|
||||
// Size of CHR data
|
||||
long chr_size() const { return chr_size_; }
|
||||
|
||||
unsigned mapper_data() const { return mapper; }
|
||||
|
||||
// Initial mirroring setup
|
||||
int mirroring() const { return mapper & 0x09; }
|
||||
|
||||
// Pointer to beginning of PRG data
|
||||
inline uint8_t * prg() { return prg_; }
|
||||
inline uint8_t const* prg() const { return prg_; }
|
||||
|
||||
// Pointer to beginning of CHR data
|
||||
inline uint8_t * chr() { return chr_; }
|
||||
inline uint8_t const* chr() const { return chr_; }
|
||||
|
||||
// End of public interface
|
||||
private:
|
||||
uint8_t *prg_;
|
||||
uint8_t *chr_;
|
||||
long prg_size_;
|
||||
long chr_size_;
|
||||
unsigned mapper;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -369,7 +369,6 @@ void Nes_Core::write_io( nes_addr_t addr, int data )
|
|||
// if strobe goes low, latch data
|
||||
if ( joypad.w4016 & 1 & ~data )
|
||||
{
|
||||
joypad_read_count++;
|
||||
joypad.joypad_latches [0] = current_joypad [0];
|
||||
joypad.joypad_latches [1] = current_joypad [1];
|
||||
}
|
||||
|
@ -614,8 +613,6 @@ nes_time_t Nes_Core::emulate_frame(int joypad1, int joypad2)
|
|||
current_joypad [0] = (joypad1 |= ~0xFF);
|
||||
current_joypad [1] = (joypad2 |= ~0xFF);
|
||||
|
||||
joypad_read_count = 0;
|
||||
|
||||
cpu_time_offset = ppu.begin_frame( nes.timestamp ) - 1;
|
||||
ppu_2002_time = 0;
|
||||
clock_ = cpu_time_offset;
|
|
@ -50,7 +50,6 @@ public: private: friend class Nes_Emu;
|
|||
|
||||
public:
|
||||
unsigned long current_joypad [2];
|
||||
int joypad_read_count;
|
||||
Nes_Cart const* cart;
|
||||
Nes_Mapper* mapper;
|
||||
nes_state_t nes;
|
|
@ -8,6 +8,11 @@
|
|||
#include <stdio.h>
|
||||
#include <Nes_Core.h>
|
||||
|
||||
/**
|
||||
* Optimizations by Sergio Martin (eien86) 2023-2024
|
||||
* The license below (LGPLv2) applies.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
|
@ -19,6 +24,15 @@ more details. You should have received a copy of the GNU Lesser General
|
|||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#define st_n 0x80
|
||||
#define st_v 0x40
|
||||
#define st_r 0x20
|
||||
#define st_b 0x10
|
||||
#define st_d 0x08
|
||||
#define st_i 0x04
|
||||
#define st_z 0x02
|
||||
#define st_c 0x01
|
||||
|
||||
// Macros
|
||||
|
||||
#define GET_OPERAND( addr ) page [addr]
|
||||
|
@ -114,10 +128,6 @@ imm##op: \
|
|||
goto loop; \
|
||||
}
|
||||
|
||||
inline void Nes_Cpu::set_code_page( int i, uint8_t const* p )
|
||||
{
|
||||
code_map [i] = p - (unsigned) i * page_size;
|
||||
}
|
||||
|
||||
void Nes_Cpu::reset( void const* unmapped_page )
|
||||
{
|
||||
|
@ -134,12 +144,12 @@ void Nes_Cpu::reset( void const* unmapped_page )
|
|||
irq_time_ = LONG_MAX / 2 + 1;
|
||||
end_time_ = LONG_MAX / 2 + 1;
|
||||
|
||||
set_code_page( 0, low_mem );
|
||||
set_code_page( 1, low_mem );
|
||||
set_code_page( 2, low_mem );
|
||||
set_code_page( 3, low_mem );
|
||||
code_map [0] = low_mem;
|
||||
code_map [1] = low_mem - 1 * page_size;
|
||||
code_map [2] = low_mem - 2 * page_size;
|
||||
code_map [3] = low_mem - 3 * page_size;
|
||||
for ( int i = 4; i < page_count + 1; i++ )
|
||||
set_code_page( i, (uint8_t*) unmapped_page );
|
||||
code_map [i] = (uint8_t*) unmapped_page;
|
||||
|
||||
isCorrectExecution = true;
|
||||
}
|
||||
|
@ -147,8 +157,8 @@ void Nes_Cpu::reset( void const* unmapped_page )
|
|||
void Nes_Cpu::map_code( nes_addr_t start, unsigned size, const void* data )
|
||||
{
|
||||
unsigned first_page = start / page_size;
|
||||
for ( unsigned i = size / page_size; i--; )
|
||||
set_code_page( first_page + i, (uint8_t*) data + i * page_size );
|
||||
const uint8_t* newPtr = (uint8_t*) data - start;
|
||||
for ( unsigned i = size / page_size; i--; ) code_map [first_page + i] = newPtr;
|
||||
}
|
||||
|
||||
// Note: 'addr' is evaulated more than once in the following macros, so it
|
||||
|
@ -198,16 +208,6 @@ void Nes_Cpu::write( nes_addr_t addr, int value )
|
|||
|
||||
// status flags
|
||||
|
||||
enum {
|
||||
st_n = 0x80,
|
||||
st_v = 0x40,
|
||||
st_r = 0x20,
|
||||
st_b = 0x10,
|
||||
st_d = 0x08,
|
||||
st_i = 0x04,
|
||||
st_z = 0x02,
|
||||
st_c = 0x01,
|
||||
};
|
||||
|
||||
constexpr uint8_t clock_table [256] = {
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
@ -73,7 +73,6 @@ public:
|
|||
unsigned long error_count_;
|
||||
|
||||
enum { irq_inhibit = 0x04 };
|
||||
void set_code_page( int, uint8_t const* );
|
||||
void update_clock_limit();
|
||||
|
||||
registers_t r;
|
|
@ -84,20 +84,8 @@ inline void Nes_Emu::clear_sound_buf()
|
|||
sound_buf->clear();
|
||||
}
|
||||
|
||||
// Emulation
|
||||
|
||||
void Nes_Emu::close()
|
||||
{
|
||||
if ( cart() )
|
||||
{
|
||||
emu.close();
|
||||
private_cart.clear();
|
||||
}
|
||||
}
|
||||
|
||||
const char * Nes_Emu::set_cart( Nes_Cart const* new_cart )
|
||||
{
|
||||
close();
|
||||
RETURN_ERR( auto_init() );
|
||||
RETURN_ERR( emu.open( new_cart ) );
|
||||
|
||||
|
@ -173,7 +161,6 @@ const char * Nes_Emu::emulate_frame( int joypad1, int joypad2 )
|
|||
f->chan_count = sound_buf->samples_per_frame();
|
||||
f->palette_begin = emu.ppu.palette_begin;
|
||||
f->palette_size = emu.ppu.palette_size;
|
||||
f->joypad_read_count = emu.joypad_read_count;
|
||||
f->burst_phase = emu.ppu.burst_phase;
|
||||
f->pitch = emu.ppu.host_row_bytes;
|
||||
f->pixels = emu.ppu.host_pixels + f->left;
|
||||
|
@ -189,10 +176,9 @@ const char * Nes_Emu::emulate_frame( int joypad1, int joypad2 )
|
|||
|
||||
// Extras
|
||||
|
||||
const char * Nes_Emu::load_ines( Auto_File_Reader in )
|
||||
const char * Nes_Emu::load_ines( const uint8_t* buffer )
|
||||
{
|
||||
close();
|
||||
RETURN_ERR( private_cart.load_ines( in ) );
|
||||
private_cart.load_ines( buffer );
|
||||
return set_cart( &private_cart );
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ public:
|
|||
// Basic setup
|
||||
|
||||
// Load iNES file into emulator and clear recording
|
||||
const char * load_ines( Auto_File_Reader );
|
||||
const char * load_ines( const uint8_t* buffer );
|
||||
|
||||
// Set sample rate for sound generation
|
||||
const char * set_sample_rate( long );
|
||||
|
@ -90,10 +90,6 @@ public:
|
|||
// Pointer to current cartridge, or NULL if none is loaded
|
||||
Nes_Cart const* cart() const { return emu.cart; }
|
||||
|
||||
// Free any memory and close cartridge, if one was currently open. A new cartridge
|
||||
// must be opened before further emulation can take place.
|
||||
void close();
|
||||
|
||||
// Emulate powering NES off and then back on. If full_reset is false, emulates
|
||||
// pressing the reset button only, which doesn't affect memory, otherwise
|
||||
// emulates powering system off then on.
|
||||
|
@ -222,7 +218,6 @@ private:
|
|||
virtual void loading_state( Nes_State const& ) { }
|
||||
void load_state( Nes_State_ const& );
|
||||
void save_state( Nes_State_* s ) const { emu.save_state( s ); }
|
||||
int joypad_read_count() const { return emu.joypad_read_count; }
|
||||
long timestamp() const { return emu.nes.frame_count; }
|
||||
void set_timestamp( long t ) { emu.nes.frame_count = t; }
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include "Nes_File.h"
|
||||
|
||||
#include "blargg_endian.h"
|
||||
#include "blargg_source.h"
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -16,7 +16,6 @@ more details. You should have received a copy of the GNU Lesser General
|
|||
Public License along with this module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
// Nes_File_Writer
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "blargg_endian.h"
|
||||
#include "Nes_Apu.h"
|
||||
#include "Nes_Emu.h"
|
||||
#include "Nes_Mapper.h"
|
||||
|
||||
|
@ -121,7 +122,7 @@ const char * Nes_State_::write_blocks( Nes_File_Writer& out ) const
|
|||
|
||||
if ( apu_valid )
|
||||
{
|
||||
apu_state_t s = *apu;
|
||||
Nes_Apu::apu_state_t s = *apu;
|
||||
RETURN_ERR( write_nes_state( out, s ) );
|
||||
}
|
||||
|
||||
|
@ -232,7 +233,7 @@ const char * Nes_State_::read_blocks( Nes_File_Reader& in )
|
|||
ppu_valid = true;
|
||||
break;
|
||||
|
||||
case apu_state_t::tag:
|
||||
case FOUR_CHAR('APUR'):
|
||||
memset( apu, 0, sizeof *apu );
|
||||
RETURN_ERR( read_nes_state( in, apu ) );
|
||||
apu_valid = true;
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "Nes_File.h"
|
||||
#include "Nes_Cpu.h"
|
||||
#include "Nes_Apu.h"
|
||||
class Nes_Emu;
|
||||
class Nes_State;
|
||||
|
||||
|
@ -63,7 +64,7 @@ public:
|
|||
nes_state_t nes;
|
||||
Nes_Cpu::registers_t* cpu;
|
||||
joypad_state_t* joypad;
|
||||
apu_state_t* apu;
|
||||
Nes_Apu::apu_state_t* apu;
|
||||
ppu_state_t* ppu;
|
||||
mapper_state_t* mapper;
|
||||
|
||||
|
@ -96,7 +97,7 @@ public:
|
|||
private:
|
||||
Nes_Cpu::registers_t cpu;
|
||||
joypad_state_t joypad;
|
||||
apu_state_t apu;
|
||||
Nes_Apu::apu_state_t apu;
|
||||
ppu_state_t ppu;
|
||||
mapper_state_t mapper;
|
||||
uint8_t ram [ram_size];
|
|
@ -1,12 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
/* Copyright (C) 2005-2006 Shay Green. Permission is hereby granted, free of
|
||||
charge, to any person obtaining a copy of this software module and associated
|
||||
documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell copies of the Software, and
|
||||
to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions: The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions of the Software. THE
|
||||
SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
// Abstract file access interfaces
|
||||
|
||||
#ifndef ABSTRACT_FILE_H
|
||||
#define ABSTRACT_FILE_H
|
||||
|
||||
#undef BLARGG_CONFIG_H
|
||||
|
||||
#include "Data_Reader.h"
|
||||
#include <cstring>
|
||||
|
||||
// Supports writing
|
||||
class Data_Writer {
|
||||
|
@ -15,8 +27,8 @@ public:
|
|||
virtual ~Data_Writer() { }
|
||||
|
||||
// Write 'n' bytes. NULL on success, otherwise error string.
|
||||
virtual const char *write( const void*, long n ) = 0;
|
||||
|
||||
virtual const char *write( const void*, long ) { return 0; }
|
||||
|
||||
private:
|
||||
// noncopyable
|
||||
Data_Writer( const Data_Writer& );
|
||||
|
@ -31,39 +43,93 @@ class Mem_Writer : public Data_Writer {
|
|||
enum { expanding, fixed, ignore_excess } mode;
|
||||
public:
|
||||
// Keep all written data in expanding block of memory
|
||||
Mem_Writer();
|
||||
|
||||
Mem_Writer()
|
||||
{
|
||||
data_ = 0;
|
||||
size_ = 0;
|
||||
allocated = 0;
|
||||
mode = expanding;
|
||||
}
|
||||
|
||||
// Write to fixed-size block of memory. If ignore_excess is false, returns
|
||||
// error if more than 'size' data is written, otherwise ignores any excess.
|
||||
Mem_Writer( void*, long size, int ignore_excess = 0 );
|
||||
|
||||
const char *write( const void*, long );
|
||||
|
||||
Mem_Writer( void* p, long s, int b )
|
||||
{
|
||||
data_ = (char*) p;
|
||||
size_ = 0;
|
||||
allocated = s;
|
||||
mode = b ? ignore_excess : fixed;
|
||||
}
|
||||
|
||||
const char * write( const void* p, long s )
|
||||
{
|
||||
long remain = allocated - size_;
|
||||
if ( s > remain )
|
||||
{
|
||||
if ( mode == fixed )
|
||||
return "Tried to write more data than expected";
|
||||
|
||||
if ( mode == ignore_excess )
|
||||
{
|
||||
s = remain;
|
||||
}
|
||||
else // expanding
|
||||
{
|
||||
long new_allocated = size_ + s;
|
||||
new_allocated += (new_allocated >> 1) + 2048;
|
||||
void* p = realloc( data_, new_allocated );
|
||||
if ( !p )
|
||||
return "Out of memory";
|
||||
data_ = (char*) p;
|
||||
allocated = new_allocated;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy( data_ + size_, p, s );
|
||||
size_ += s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pointer to beginning of written data
|
||||
char* data() { return data_; }
|
||||
|
||||
// Number of bytes written
|
||||
size_t size() const { return size_; }
|
||||
|
||||
~Mem_Writer();
|
||||
~Mem_Writer()
|
||||
{
|
||||
if ( ( mode == expanding ) && data_ ) free( data_ );
|
||||
}
|
||||
};
|
||||
|
||||
// Dry writer to get the state size
|
||||
class Dry_Writer : public Data_Writer {
|
||||
long size_;
|
||||
|
||||
public:
|
||||
// Keep all written data in expanding block of memory
|
||||
Dry_Writer();
|
||||
|
||||
const char *write( const void*, long );
|
||||
|
||||
Dry_Writer()
|
||||
{
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
~Dry_Writer()
|
||||
{
|
||||
}
|
||||
|
||||
const char *write( const void* p, long s )
|
||||
{
|
||||
size_ += s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Pointer to beginning of written data
|
||||
char* data() { return NULL; }
|
||||
|
||||
// Number of bytes written
|
||||
long size() const { return size_; }
|
||||
|
||||
~Dry_Writer();
|
||||
};
|
||||
|
||||
|
||||
|
@ -76,9 +142,18 @@ public:
|
|||
Auto_File_Reader( Data_Reader& r ) : data( &r ), path( 0 ) { }
|
||||
Auto_File_Reader( Auto_File_Reader const& );
|
||||
Auto_File_Reader& operator = ( Auto_File_Reader const& );
|
||||
~Auto_File_Reader();
|
||||
const char* open();
|
||||
|
||||
|
||||
const char* open()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
~Auto_File_Reader()
|
||||
{
|
||||
if ( path )
|
||||
delete data;
|
||||
}
|
||||
|
||||
int operator ! () const { return !data; }
|
||||
Data_Reader* operator -> () const { return data; }
|
||||
Data_Reader& operator * () const { return *data; }
|
||||
|
@ -93,9 +168,20 @@ public:
|
|||
Auto_File_Writer( Data_Writer& r ) : data( &r ), path( 0 ) { }
|
||||
Auto_File_Writer( Auto_File_Writer const& );
|
||||
Auto_File_Writer& operator = ( Auto_File_Writer const& );
|
||||
~Auto_File_Writer();
|
||||
const char* open();
|
||||
const char* open_comp( int level = -1 ); // compress output if possible
|
||||
|
||||
~Auto_File_Writer()
|
||||
{
|
||||
}
|
||||
|
||||
const char* open()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* open_comp( int level = -1 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int operator ! () const { return !data; }
|
||||
Data_Writer* operator -> () const { return data; }
|
||||
|
@ -122,5 +208,3 @@ inline Auto_File_Writer& Auto_File_Writer::operator = ( Auto_File_Writer const&
|
|||
return *this;
|
||||
}
|
||||
inline Auto_File_Writer::Auto_File_Writer( Auto_File_Writer const& r ) { *this = r; }
|
||||
|
||||
#endif
|
|
@ -34,9 +34,6 @@ arithmetic on smaller types. */
|
|||
// In case compiler doesn't support these properly. Used rarely.
|
||||
#define STATIC_CAST(T,expr) static_cast<T> (expr)
|
||||
|
||||
// User configuration can override the above macros if necessary
|
||||
#include "blargg_config.h"
|
||||
|
||||
// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0.
|
||||
#ifndef BOOST_STATIC_ASSERT
|
||||
#ifdef _MSC_VER
|
|
@ -1,11 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
// CPU Byte Order Utilities
|
||||
|
||||
// Nes_Emu 0.7.0
|
||||
|
||||
#ifndef BLARGG_ENDIAN
|
||||
#define BLARGG_ENDIAN
|
||||
|
||||
#include "blargg_common.h"
|
||||
|
||||
// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16)
|
||||
|
@ -105,4 +103,5 @@ inline void set_be( uint32_t* p, unsigned long n ) { SET_BE32( p, n ); }
|
|||
inline unsigned get_be( uint16_t* p ) { return GET_BE16( p ); }
|
||||
inline unsigned long get_be( uint32_t* p ) { return GET_BE32( p ); }
|
||||
|
||||
#endif
|
||||
#define SWAP_BE( n ) (void) (set_be( &(n), (n) ))
|
||||
#define SWAP_LE( n ) (void) (set_le( &(n), (n) ))
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue