Skip to content

Commit

Permalink
Merge pull request #33 from Kautenja/windows
Browse files Browse the repository at this point in the history
Windows Support
  • Loading branch information
Kautenja authored Aug 18, 2018
2 parents 6fadc9a + 8346093 commit fdd1e2c
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 170 deletions.
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
[python-version]: https://img.shields.io/pypi/pyversions/nes-py.svg
[python-home]: https://python.org

nes-py is an NES emulator and OpenAI Gym interface based on the
[LaiNES](https://github.com/AndreaOrru/LaiNES) emulator.
nes-py is an NES emulator and OpenAI Gym interface for MacOS, Linux, and
Windows based on the [LaiNES](https://github.com/AndreaOrru/LaiNES) emulator.

# Installation

Expand All @@ -33,6 +33,20 @@ The preferred installation of `nes-py` is from `pip`:
pip install nes-py
```

## Debian

Make sure you have the `clang++` compiler installed:

```shell
sudo apt-get install clang
```

## Windows

You'll need to install the Visual-Studio 14.0 tools for Windows installation.
The [Visual Studio Community](https://visualstudio.microsoft.com/downloads/)
package provides these tools for free.

# Usage

To access the NES emulator from the command line use the following command.
Expand Down
2 changes: 1 addition & 1 deletion SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ env = Environment(

# Compile the shared library for the Python interface
source_files = Glob('nes_py/laines/build/*/*.cpp') + Glob('nes_py/laines/build/*/*/*.cpp')
env.SharedLibrary('nes_py/laines/build/_nes_env.so', source_files)
env.SharedLibrary('nes_py/_nes_env.so', source_files)
76 changes: 40 additions & 36 deletions nes_py/laines/cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,43 +48,47 @@ namespace CPU {
void dma_oam(u8 bank);
template<bool wr> inline u8 access(u16 addr, u8 v = 0) {
u8* r;
switch (addr) {
// RAM
case 0x0000 ... 0x1FFF:
r = &ram[addr % 0x800];
if (wr) *r = v;
return *r;
// PPU
case 0x2000 ... 0x3FFF:
return PPU::access<wr>(addr % 8, v);

// APU
case 0x4000 ... 0x4013:
case 0x4015:
// RAM
if (0x0000 <= addr && addr <= 0x1FFF) {
r = &ram[addr % 0x800];
if (wr)
*r = v;
return *r;
}
// PPU
else if (0x2000 <= addr && addr <= 0x3FFF) {
return PPU::access<wr>(addr % 8, v);
}
// APU (not implemented, NOP instead)
else if ((0x4000 <= addr && addr <= 0x4013) || addr == 0x4015) {
return 1;
}
// Joypad 1
else if (addr == 0x4017) {
if (wr)
return 1;
// Joypad 1
case 0x4017:
if (wr)
return 1;
else
return joypad->read_state(1);
// OAM / DMA
case 0x4014:
if (wr) dma_oam(v);
break;
case 0x4016:
// Joypad strobe
if (wr) {
joypad->write_strobe(v & 1);
break;
}
// Joypad 0
else
return joypad->read_state(0);
// Cartridge
case 0x4018 ... 0xFFFF:
return cartridge->access<wr>(addr, v);
else
return joypad->read_state(1);
}
// OAM / DMA
else if (addr == 0x4014) {
if (wr)
dma_oam(v);
}
// Joypad Strobe and Joypad 0
else if (addr == 0x4016) {
// Joypad strobe
if (wr)
joypad->write_strobe(v & 1);
// Joypad 0
else
return joypad->read_state(0);
}
// Cartridge
else if (0x4018 <= addr && addr <= 0xFFFF) {
return cartridge->access<wr>(addr, v);
}

return 0;
}
inline u8 wr(u16 a, u8 v) { T; return access<1>(a, v); }
Expand Down Expand Up @@ -294,7 +298,7 @@ namespace CPU {

while (remainingCycles > 0) {
if (nmi) INT<NMI>();
else if (irq and !P[I]) INT<IRQ>();
else if (irq && !P[I]) INT<IRQ>();

exec();
}
Expand Down
2 changes: 1 addition & 1 deletion nes_py/laines/joypad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ u8 Joypad::read_state(int n) {

void Joypad::write_strobe(bool v) {
// Read the joy-pad data on strobe's transition 1 -> 0.
if (this->strobe and !v)
if (this->strobe && !v)
for (int i = 0; i < NUM_JOYPADS; i++)
this->joypad_bits[i] = this->joypad_buttons[i];

Expand Down
2 changes: 1 addition & 1 deletion nes_py/laines/mappers/mapper4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@ void Mapper4::signal_scanline() {
else
irqCounter--;

if (irqEnabled and irqCounter == 0)
if (irqEnabled && irqCounter == 0)
CPU::set_irq();
}
70 changes: 8 additions & 62 deletions nes_py/laines/nes_env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ NESEnv::NESEnv(wchar_t* path) {
current_state->joypad = new Joypad();
// set the cartridge pointer for the CPU and PPU
current_state->load();

// set the backup state to NULL
backup_state = nullptr;
}

void NESEnv::reset() {
// initialize the CPU
CPU::power();
// initialize the PPU
PPU::reset();
}

Expand All @@ -29,73 +31,17 @@ void NESEnv::step(unsigned char action) {
}

void NESEnv::backup() {
// copy the current state with the backup state
// delete any current backup
delete backup_state;
// copy the current state as the backup state
backup_state = new GameState(current_state, CPU::get_state(), PPU::get_state());
}

void NESEnv::restore() {
// copy the backup state into the current state and load the machine
// delete the current state in progress
delete current_state;
// copy the backup state into the current state
current_state = new GameState(backup_state);
// load the current state into the machine
current_state->load();
}

// definitions of functions for the Python interface to access
extern "C" {
/// The initializer to return a new NESEnv with a given path.
NESEnv* NESEnv_init(wchar_t* path){
return new NESEnv(path);
}

/// The width of the NES screen.
unsigned NESEnv_width() {
return GUI::get_width();
}

/// The height of the NES screen.
unsigned NESEnv_height() {
return GUI::get_height();
}

/// The getter for RAM access
u8 NESEnv_read_mem(NESEnv* env, u16 address) {
return CPU::read_mem(address);
}

/// The setter for RAM access
void NESEnv_write_mem(NESEnv* env, u16 address, u8 value) {
CPU::write_mem(address, value);
}

/// Copy the screen of the emulator to an output buffer (NumPy array)
void NESEnv_screen(NESEnv* env, unsigned char *output_buffer) {
PPU::get_gui()->copy_screen(output_buffer);
}

/// The function to reset the environment.
void NESEnv_reset(NESEnv* env) {
env->reset();
}

/// The function to perform a step on the emulator.
void NESEnv_step(NESEnv* env, unsigned char action) {
env->step(action);
}

/// The function to destroy an NESEnv and clear it from memory.
void NESEnv_close(NESEnv* env) {
delete env;
}

/// The function to backup the game-state
void NESEnv_backup(NESEnv* env) {
env->backup();
}

/// The function to restore the game-state
void NESEnv_restore(NESEnv* env) {
env->restore();
}

}
Loading

0 comments on commit fdd1e2c

Please sign in to comment.