Merge pull request #1 from SergioMartin86/headerOnly

A lot of optimizations and better testing
This commit is contained in:
Sergio Martin 2024-01-17 18:27:42 +01:00 committed by GitHub
commit ea4c425e47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
203 changed files with 106126 additions and 22980 deletions

2
.gitmodules vendored
View File

@ -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

View File

@ -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.

29
extern/hqn/hqn.cpp vendored
View File

@ -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)
{

17
extern/hqn/hqn.h vendored
View File

@ -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++)
{

View File

@ -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')

View File

@ -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.'''
)

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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()
{
}

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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] );
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
};

View File

@ -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' ]
)
)

View File

@ -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);
}

View File

@ -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);

View File

@ -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 );
}

View File

@ -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 ]
)

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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 );
}

View File

@ -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; }

View File

@ -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

View File

@ -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;

View File

@ -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];

View File

@ -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

View File

@ -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

View File

@ -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