mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
The homebrew module offers configurable ports useful to homebrew game developers. By reading and writing to new ports at configurable addresses, a rom may * write to stdout * write to stderr * exit the emulator with a given exit status These can be used to create automated test suites.
307 lines
5.6 KiB
C++
307 lines
5.6 KiB
C++
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Nestopia - NES/Famicom emulator written in C++
|
|
//
|
|
// Copyright (C) 2018-2018 Phil Smith
|
|
//
|
|
// This file is part of Nestopia.
|
|
//
|
|
// Nestopia 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.
|
|
//
|
|
// Nestopia 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 Nestopia; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include "NstCpu.hpp"
|
|
#include "NstHomebrew.hpp"
|
|
|
|
namespace Nes
|
|
{
|
|
namespace Core
|
|
{
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("s", on)
|
|
#endif
|
|
|
|
Homebrew::Homebrew(Cpu& c)
|
|
: cpu(c)
|
|
, exitAddress(0)
|
|
, exitSet(false)
|
|
, exitPort(NULL)
|
|
, stdOutAddress(0)
|
|
, stdOutSet(false)
|
|
, stdOutPort(NULL)
|
|
, stdErrAddress(0)
|
|
, stdErrSet(false)
|
|
, stdErrPort(NULL)
|
|
{}
|
|
|
|
Homebrew::~Homebrew()
|
|
{
|
|
ClearPorts();
|
|
}
|
|
|
|
void Homebrew::Reset()
|
|
{
|
|
ActivateExitPort();
|
|
ActivateStdOutPort();
|
|
ActivateStdErrPort();
|
|
}
|
|
|
|
void Homebrew::ClearPorts()
|
|
{
|
|
ClearExitPort();
|
|
ClearStdOutPort();
|
|
ClearStdErrPort();
|
|
}
|
|
|
|
Result Homebrew::SetExitPort
|
|
(
|
|
const word address,
|
|
const bool activate
|
|
)
|
|
{
|
|
if
|
|
(
|
|
exitSet
|
|
&& exitAddress == address
|
|
&& (!activate || exitPort != NULL)
|
|
)
|
|
return RESULT_NOP;
|
|
|
|
ClearExitPort();
|
|
|
|
exitAddress = address;
|
|
exitSet = true;
|
|
|
|
if (activate)
|
|
return ActivateExitPort();
|
|
else
|
|
return RESULT_OK;
|
|
}
|
|
|
|
Result Homebrew::ClearExitPort()
|
|
{
|
|
exitSet = false;
|
|
|
|
if (exitPort != NULL)
|
|
{
|
|
cpu.Unlink
|
|
(
|
|
exitAddress,
|
|
this,
|
|
&Homebrew::Peek_Exit,
|
|
&Homebrew::Poke_Exit
|
|
);
|
|
exitPort = NULL;
|
|
return RESULT_OK;
|
|
}
|
|
else
|
|
return RESULT_NOP;
|
|
}
|
|
|
|
Result Homebrew::SetStdOutPort
|
|
(
|
|
const word address,
|
|
const bool activate
|
|
)
|
|
{
|
|
if
|
|
(
|
|
stdOutSet
|
|
&& stdOutAddress == address
|
|
&& (!activate || stdOutPort != NULL)
|
|
)
|
|
return RESULT_NOP;
|
|
|
|
ClearStdOutPort();
|
|
|
|
stdOutAddress = address;
|
|
stdOutSet = true;
|
|
|
|
if (activate)
|
|
return ActivateStdOutPort();
|
|
else
|
|
return RESULT_OK;
|
|
}
|
|
|
|
Result Homebrew::ClearStdOutPort()
|
|
{
|
|
stdOutSet = false;
|
|
|
|
if (stdOutPort != NULL)
|
|
{
|
|
cpu.Unlink
|
|
(
|
|
stdOutAddress,
|
|
this,
|
|
&Homebrew::Peek_StdOut,
|
|
&Homebrew::Poke_StdOut
|
|
);
|
|
stdOutPort = NULL;
|
|
return RESULT_OK;
|
|
}
|
|
else
|
|
return RESULT_NOP;
|
|
}
|
|
|
|
Result Homebrew::SetStdErrPort
|
|
(
|
|
const word address,
|
|
const bool activate
|
|
)
|
|
{
|
|
if
|
|
(
|
|
stdErrSet
|
|
&& stdErrAddress == address
|
|
&& (!activate || stdErrPort != NULL)
|
|
)
|
|
return RESULT_NOP;
|
|
|
|
ClearStdErrPort();
|
|
|
|
stdErrAddress = address;
|
|
stdErrSet = true;
|
|
|
|
if (activate)
|
|
return ActivateStdErrPort();
|
|
else
|
|
return RESULT_OK;
|
|
}
|
|
|
|
Result Homebrew::ClearStdErrPort()
|
|
{
|
|
stdErrSet = false;
|
|
|
|
if (stdErrPort != NULL)
|
|
{
|
|
cpu.Unlink
|
|
(
|
|
stdErrAddress,
|
|
this,
|
|
&Homebrew::Peek_StdErr,
|
|
&Homebrew::Poke_StdErr
|
|
);
|
|
stdErrPort = NULL;
|
|
return RESULT_OK;
|
|
}
|
|
else
|
|
return RESULT_NOP;
|
|
}
|
|
|
|
dword Homebrew::NumPorts() const
|
|
{
|
|
return (exitPort != NULL ? 1 : 0)
|
|
+ (stdOutPort != NULL ? 1 : 0)
|
|
+ (stdErrPort != NULL ? 1 : 0);
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
NES_PEEK_A(Homebrew,Exit)
|
|
{
|
|
return exitPort->Peek( address );
|
|
}
|
|
|
|
NES_POKE_D(Homebrew,Exit)
|
|
{
|
|
std::exit( data );
|
|
}
|
|
|
|
NES_PEEK_A(Homebrew,StdOut)
|
|
{
|
|
return stdOutPort->Peek( address );
|
|
}
|
|
|
|
NES_POKE_D(Homebrew,StdOut)
|
|
{
|
|
std::cout << (static_cast<unsigned char>(data & 0xFF));
|
|
|
|
if (data == '\n')
|
|
std::cout << std::flush;
|
|
}
|
|
|
|
NES_PEEK_A(Homebrew,StdErr)
|
|
{
|
|
return stdErrPort->Peek( address );
|
|
}
|
|
|
|
NES_POKE_D(Homebrew,StdErr)
|
|
{
|
|
std::cerr << (static_cast<unsigned char>(data & 0xFF));
|
|
|
|
if (data == '\n')
|
|
std::cerr << std::flush;
|
|
}
|
|
|
|
Result Homebrew::ActivateExitPort()
|
|
{
|
|
if (exitSet && exitPort == NULL)
|
|
{
|
|
exitPort = cpu.Link
|
|
(
|
|
exitAddress,
|
|
Cpu::LEVEL_HIGH,
|
|
this,
|
|
&Homebrew::Peek_Exit,
|
|
&Homebrew::Poke_Exit
|
|
);
|
|
return RESULT_OK;
|
|
}
|
|
else
|
|
return RESULT_NOP;
|
|
}
|
|
|
|
Result Homebrew::ActivateStdOutPort()
|
|
{
|
|
if (stdOutSet && stdOutPort == NULL)
|
|
{
|
|
stdOutPort = cpu.Link
|
|
(
|
|
stdOutAddress,
|
|
Cpu::LEVEL_HIGH,
|
|
this,
|
|
&Homebrew::Peek_StdOut,
|
|
&Homebrew::Poke_StdOut
|
|
);
|
|
return RESULT_OK;
|
|
}
|
|
else
|
|
return RESULT_NOP;
|
|
}
|
|
|
|
Result Homebrew::ActivateStdErrPort()
|
|
{
|
|
if (stdErrSet && stdErrPort == NULL)
|
|
{
|
|
stdErrPort = cpu.Link
|
|
(
|
|
stdErrAddress,
|
|
Cpu::LEVEL_HIGH,
|
|
this,
|
|
&Homebrew::Peek_StdErr,
|
|
&Homebrew::Poke_StdErr
|
|
);
|
|
return RESULT_OK;
|
|
}
|
|
else
|
|
return RESULT_NOP;
|
|
}
|
|
}
|
|
}
|