Merge pull request #56 from UnimatrixX01/network_link

Adding support for linking 2 Game boy emulation sessions over a network.
This commit is contained in:
Twinaphex 2016-08-07 02:51:46 +02:00 committed by GitHub
commit 92df2922ef
12 changed files with 499 additions and 12 deletions

View File

@ -27,6 +27,7 @@ SOURCES_CXX := $(CORE_DIR)/cpu.cpp \
$(CORE_DIR)/video/next_m0_time.cpp \
$(CORE_DIR)/video/ppu.cpp \
$(CORE_DIR)/video/sprite_mapper.cpp \
$(CORE_DIR)/../libretro/net_serial.cpp \
$(CORE_DIR)/../libretro/libretro.cpp
SOURCES_C := $(CORE_DIR)/../libretro/blipper.c

View File

@ -20,6 +20,7 @@
#define GAMBATTE_H
#include "inputgetter.h"
#include "serial_io.h"
#include "gbint.h"
#include <string>
#include <stddef.h>
@ -81,6 +82,9 @@ public:
/** Sets the callback used for getting input state. */
void setInputGetter(InputGetter *getInput);
/** Sets the callback used for transferring serial data. */
void setSerialIO(SerialIO *serial_io);
/** Sets the directory used for storing save data. The default is the same directory as the ROM Image file. */
void setSaveDir(const std::string &sdir);

View File

@ -2,6 +2,7 @@
#include "blipper.h"
#include <gambatte.h>
#include "gbcpalettes.h"
#include "net_serial.h"
#include <assert.h>
#include <stdio.h>
@ -56,13 +57,23 @@ class SNESInput : public gambatte::InputGetter
}
} static gb_input;
enum SerialMode {
SERIAL_NONE,
SERIAL_SERVER,
SERIAL_CLIENT
};
static NetSerial gb_net_serial;
static SerialMode gb_serialMode = SERIAL_NONE;
static int gb_NetworkPort = 12345;
static std::string gb_NetworkClientAddr;
static blipper_t *resampler_l;
static blipper_t *resampler_r;
void retro_get_system_info(struct retro_system_info *info)
{
info->library_name = "Gambatte";
info->library_version = "v0.5.0";
info->library_version = "v0.5.0-netlink";
info->need_fullpath = false;
info->block_extract = false;
info->valid_extensions = "gb|gbc|dmg";
@ -153,6 +164,12 @@ void retro_set_environment(retro_environment_t cb)
{ "gambatte_gb_internal_palette", "Internal Palette; GBC - Blue|GBC - Brown|GBC - Dark Blue|GBC - Dark Brown|GBC - Dark Green|GBC - Grayscale|GBC - Green|GBC - Inverted|GBC - Orange|GBC - Pastel Mix|GBC - Red|GBC - Yellow|Special 1|Special 2|Special 3" },
{ "gambatte_gbc_color_correction", "Color correction; enabled|disabled" },
{ "gambatte_gb_hwmode", "Emulated hardware; Auto|GB|GBA" }, // unfortunately, libgambatte does not have a 'force GBC' flag
{ "gambatte_gb_link_mode", "GameBoy Link Mode; Not Connected|Network Server|Network Client" },
{ "gambatte_gb_link_network_port", "Network Link Port; 56400|56401|56402|56403|56404|56405|56406|56407|56408|56409|56410|56411|56412|56413|56414|56415|56416|56417|56418|56419|56420" },
{ "gambatte_gb_link_network_server_ip_octet1", "Network link server address part 1 (client only); 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255" },
{ "gambatte_gb_link_network_server_ip_octet2", "Network link server address part 2 (client only); 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255" },
{ "gambatte_gb_link_network_server_ip_octet3", "Network link server address part 3 (client only); 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255" },
{ "gambatte_gb_link_network_server_ip_octet4", "Network link server address part 4 (client only); 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255" },
{ NULL, NULL },
};
@ -367,6 +384,55 @@ static void check_variables(void)
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "disabled")) colorCorrection=false;
gb.setColorCorrection(colorCorrection);
gb_serialMode = SERIAL_NONE;
var.key = "gambatte_gb_link_mode";
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
if (!strcmp(var.value, "Network Server")) {
gb_serialMode = SERIAL_SERVER;
} else if (!strcmp(var.value, "Network Client")) {
gb_serialMode = SERIAL_CLIENT;
}
}
var.key = "gambatte_gb_link_network_port";
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
gb_NetworkPort=atoi(var.value);
}
gb_NetworkClientAddr = "";
var.key = "gambatte_gb_link_network_server_ip_octet1";
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
gb_NetworkClientAddr += std::string(var.value);
}
var.key = "gambatte_gb_link_network_server_ip_octet2";
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
gb_NetworkClientAddr += "." + std::string(var.value);
}
var.key = "gambatte_gb_link_network_server_ip_octet3";
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
gb_NetworkClientAddr += "." + std::string(var.value);
}
var.key = "gambatte_gb_link_network_server_ip_octet4";
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
gb_NetworkClientAddr += "." + std::string(var.value);
}
switch(gb_serialMode)
{
case SERIAL_SERVER:
gb_net_serial.start(true, gb_NetworkPort, gb_NetworkClientAddr);
gb.setSerialIO(&gb_net_serial);
break;
case SERIAL_CLIENT:
gb_net_serial.start(false, gb_NetworkPort, gb_NetworkClientAddr);
gb.setSerialIO(&gb_net_serial);
break;
default:
gb_net_serial.stop();
gb.setSerialIO(NULL);
break;
}
var.key = "gambatte_gb_colorization";
if (!environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || !var.value)

View File

@ -0,0 +1,288 @@
#include "net_serial.h"
#include "libretro.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netdb.h>
extern retro_log_printf_t log_cb;
//FILE* fpout;
//FILE* fpin;
NetSerial::NetSerial()
: is_stopped_(true)
, is_server_(false)
, port_(12345)
, hostname_()
, server_fd_(-1)
, sockfd_(-1)
, lastConnectAttempt_(0)
{
// fpout = fopen("/tmp/serial_out.txt", "w");
// fpin = fopen("/tmp/serial_in.txt", "w");
}
NetSerial::~NetSerial()
{
stop();
}
//int master_txn_cnt = 0;
//int slave_txn_cnt = 0;
bool NetSerial::start(bool is_server, int port, const std::string& hostname)
{
stop();
log_cb(RETRO_LOG_INFO, "Starting GameLink nework %s on %s:%d\n",
is_server ? "server" : "client", hostname.c_str(), port);
is_server_ = is_server;
port_ = port;
hostname_ = hostname;
is_stopped_ = false;
return checkAndRestoreConnection(false);
}
void NetSerial::stop()
{
if (!is_stopped_) {
log_cb(RETRO_LOG_INFO, "Stoping GameLink nework\n");
is_stopped_ = true;
if (sockfd_ >= 0) {
close(sockfd_);
sockfd_ = -1;
}
if (server_fd_ >= 0) {
close(server_fd_);
server_fd_ = -1;
}
}
}
bool NetSerial::checkAndRestoreConnection(bool throttle)
{
if (is_stopped_) {
return false;
}
if (sockfd_ < 0 && throttle) {
clock_t now = clock();
// Only attempt to establish the connection every 5 seconds
if (((now - lastConnectAttempt_) / CLOCKS_PER_SEC) < 5) {
return false;
}
}
lastConnectAttempt_ = clock();
if (is_server_) {
if (!startServerSocket()) {
return false;
}
if (!acceptClient()) {
return false;
}
} else {
if (!startClientSocket()) {
return false;
}
}
return true;
}
bool NetSerial::startServerSocket()
{
int fd;
struct sockaddr_in server_addr;
if (server_fd_ < 0) {
bzero((char *)&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port_);
server_addr.sin_addr.s_addr = INADDR_ANY;
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
log_cb(RETRO_LOG_ERROR, "Error opening socket: %s\n", strerror(errno));
return false;
}
if (bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
log_cb(RETRO_LOG_ERROR, "Error on binding: %s\n", strerror(errno));
close(fd);
return false;
}
if (listen(fd, 1) < 0) {
log_cb(RETRO_LOG_ERROR, "Error listening: %s\n", strerror(errno));
close(fd);
return false;
}
server_fd_ = fd;
log_cb(RETRO_LOG_INFO, "GameLink network server started!\n");
}
return true;
}
bool NetSerial::acceptClient()
{
struct sockaddr_in client_addr;
struct timeval tv;
fd_set rfds;
if (server_fd_ < 0) {
return false;
}
if (sockfd_ < 0) {
int retval;
FD_ZERO(&rfds);
FD_SET(server_fd_, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 0;
if (select(server_fd_ + 1, &rfds, NULL, NULL, &tv) <= 0) {
return false;
}
socklen_t client_len = sizeof(client_addr);
sockfd_ = accept(server_fd_, (struct sockaddr*)&client_addr, &client_len);
if (sockfd_ < 0) {
log_cb(RETRO_LOG_ERROR, "Error on accept: %s\n", strerror(errno));
return false;
}
log_cb(RETRO_LOG_INFO, "GameLink network server connected to client!\n");
}
return true;
}
bool NetSerial::startClientSocket()
{
int fd;
struct sockaddr_in server_addr;
if (sockfd_ < 0) {
bzero((char *)&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port_);
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
log_cb(RETRO_LOG_ERROR, "Error opening socket: %s\n", strerror(errno));
return false;
}
struct hostent* server_hostname = gethostbyname(hostname_.c_str());
if (server_hostname == NULL) {
log_cb(RETRO_LOG_ERROR, "Error, no such host: %s\n", hostname_.c_str());
close(fd);
return false;
}
bcopy((char*)server_hostname->h_addr, (char*)&server_addr.sin_addr.s_addr, server_hostname->h_length);
if (connect(fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
log_cb(RETRO_LOG_ERROR, "Error connecting to server: %s\n", strerror(errno));
close(fd);
return false;
}
sockfd_ = fd;
log_cb(RETRO_LOG_INFO, "GameLink network client connected to server!\n");
}
return true;
}
unsigned char NetSerial::send(unsigned char data, bool fastCgb)
{
unsigned char buffer[2];
if (is_stopped_) {
return 0xFF;
}
if (sockfd_ < 0) {
if (!checkAndRestoreConnection(true)) {
return 0xFF;
}
}
// master_txn_cnt++;
// std::cout << "(" << master_txn_cnt << "," << slave_txn_cnt << ") Master send: " << (int)data << " at " << fastCgb << std::endl;
buffer[0] = data;
buffer[1] = fastCgb;
if (write(sockfd_, buffer, 2) <= 0) {
log_cb(RETRO_LOG_ERROR, "Error writing to socket: %s\n", strerror(errno));
close(sockfd_);
sockfd_ = -1;
return 0xFF;
}
// std::cout << "(" << master_txn_cnt << "," << slave_txn_cnt << ") Master waiting for response... " << std::endl;
if (read(sockfd_, buffer, 2) <= 0) {
log_cb(RETRO_LOG_ERROR, "Error reading from socket: %s\n", strerror(errno));
close(sockfd_);
sockfd_ = -1;
return 0xFF;
}
// std::cout << "(" << master_txn_cnt << "," << slave_txn_cnt << ") Master received: " << (int)buffer[0] << " (speed " << (int)buffer[1] << ")" << std::endl;
// fwrite(&data, 1, 1, fpout);
// fwrite(buffer, 1, 1, fpin);
// fflush(fpout);
// fflush(fpin);
return buffer[0];
}
bool NetSerial::check(unsigned char out, unsigned char& in, bool& fastCgb)
{
unsigned char buffer[2];
int bytes_avail = 0;
if (is_stopped_) {
return false;
}
if (sockfd_ < 0) {
if (!checkAndRestoreConnection(true)) {
return false;
}
}
if (ioctl(sockfd_, FIONREAD, &bytes_avail) < 0) {
log_cb(RETRO_LOG_ERROR, "IOCTL Failed: %s\n", strerror(errno));
return false;
}
// No data available yet
if (bytes_avail < 2) {
return false;
}
if (read(sockfd_, buffer, 2) <= 0) {
log_cb(RETRO_LOG_ERROR, "Error reading from socket: %s\n", strerror(errno));
close(sockfd_);
sockfd_ = -1;
return false;
}
// slave_txn_cnt++;
in = buffer[0];
fastCgb = buffer[1];
// std::cout << "(" << master_txn_cnt << "," << slave_txn_cnt << ") Slave read: " << (int)in << " at " << fastCgb << ", responding with " << (int)out << std::endl;
buffer[0] = out;
buffer[1] = 128;
if (write(sockfd_, buffer, 2) <= 0) {
log_cb(RETRO_LOG_ERROR, "Error writing to socket: %s\n", strerror(errno));
close(sockfd_);
sockfd_ = -1;
return false;
}
// fwrite(&out, 1, 1, fpout);
// fwrite(&in, 1, 1, fpin);
// fflush(fpout);
// fflush(fpin);
return true;
}

View File

@ -0,0 +1,36 @@
#ifndef _NET_SERIAL_H
#define _NET_SERIAL_H
#include <gambatte.h>
#include <time.h>
class NetSerial : public gambatte::SerialIO
{
public:
NetSerial();
~NetSerial();
bool start(bool is_server, int port, const std::string& hostname);
void stop();
virtual bool check(unsigned char out, unsigned char& in, bool& fastCgb);
virtual unsigned char send(unsigned char data, bool fastCgb);
private:
bool startServerSocket();
bool startClientSocket();
bool acceptClient();
bool checkAndRestoreConnection(bool throttle);
bool is_stopped_;
bool is_server_;
int port_;
std::string hostname_;
int server_fd_;
int sockfd_;
clock_t lastConnectAttempt_;
};
#endif

View File

@ -496,6 +496,8 @@ void CPU::process(unsigned long const cycles) {
while (mem_.isActive()) {
unsigned short pc = pc_;
mem_.checkSerial(cycleCounter);
if (mem_.halted()) {
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;

View File

@ -25,6 +25,8 @@
namespace gambatte {
class SerialIO;
class CPU {
public:
CPU();
@ -57,6 +59,9 @@ public:
void setInputGetter(InputGetter *getInput) {
mem_.setInputGetter(getInput);
}
void setSerialIO(SerialIO *serial_io) {
mem_.setSerialIO(serial_io);
}
void setSaveDir(std::string const &sdir) {
mem_.setSaveDir(sdir);

View File

@ -26,7 +26,10 @@
namespace gambatte {
Memory::Memory(Interrupter const &interrupter)
: getInput_(0)
: serialize_value_(0xFF)
, serialize_is_fastcgb_(false)
, getInput_(0)
, serial_io_(0)
, divLastUpdate_(0)
, lastOamDmaUpdate_(disabled_time)
, lcd_(ioamhram_, 0, VideoInterruptRequester(intreq_))
@ -62,6 +65,8 @@ unsigned long Memory::saveState(SaveState &state, unsigned long cc) {
state.mem.dmaSource = dmaSource_;
state.mem.dmaDestination = dmaDestination_;
state.mem.oamDmaPos = oamDmaPos_;
state.mem.serialize_value = serialize_value_;
state.mem.serialize_is_fastcgb = serialize_is_fastcgb_;
intreq_.saveState(state);
cart_.saveState(state);
@ -92,9 +97,11 @@ void Memory::loadState(SaveState const &state) {
dmaSource_ = state.mem.dmaSource;
dmaDestination_ = state.mem.dmaDestination;
oamDmaPos_ = state.mem.oamDmaPos;
serialize_value_ = state.mem.serialize_value;
serialize_is_fastcgb_ = state.mem.serialize_is_fastcgb;
serialCnt_ = intreq_.eventTime(intevent_serial) != disabled_time
? serialCntFrom(intreq_.eventTime(intevent_serial) - state.cpu.cycleCounter,
ioamhram_[0x102] & isCgb() * 2)
serialize_is_fastcgb_)
: 8;
cart_.setVrambank(ioamhram_[0x14F] & isCgb());
@ -127,20 +134,50 @@ void Memory::setEndtime(unsigned long cc, unsigned long inc) {
intreq_.setEventTime<intevent_end>(cc + (inc << isDoubleSpeed()));
}
void Memory::startSerialTransfer(unsigned long cc, unsigned char data, bool fastCgb)
{
// If serial interrupt is enabled
serialCnt_ = 8;
serialize_value_ = data;
serialize_is_fastcgb_ = fastCgb;
intreq_.setEventTime<intevent_serial>(serialize_is_fastcgb_
? (cc & ~0x07ul) + 0x010 * 8
: (cc & ~0xFFul) + 0x200 * 8);
}
void Memory::checkSerial(unsigned long const cc) {
// Periodically checks if serial data is received
if ((serial_io_ != 0) &&
((ioamhram_[0x102] & 0x80) == 0x80) &&
(intreq_.eventTime(intevent_serial) == disabled_time)) {
unsigned char data;
bool fastCgb;
if (serial_io_->check(ioamhram_[0x101], data, fastCgb)) {
startSerialTransfer(cc, data, fastCgb);
}
}
}
void Memory::updateSerial(unsigned long const cc) {
if (intreq_.eventTime(intevent_serial) != disabled_time) {
if (intreq_.eventTime(intevent_serial) <= cc) {
ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << serialCnt_) - 1) & 0xFF;
bool fire = ((ioamhram_[0x102] & 0x80) == 0x80);
ioamhram_[0x101] = ((ioamhram_[0x101] << serialCnt_) |
(serialize_value_ >> (8 - serialCnt_))) & 0xFF;
ioamhram_[0x102] &= 0x7F;
intreq_.setEventTime<intevent_serial>(disabled_time);
intreq_.flagIrq(8);
if (fire) {
intreq_.flagIrq(8);
}
} else {
int const targetCnt = serialCntFrom(intreq_.eventTime(intevent_serial) - cc,
ioamhram_[0x102] & isCgb() * 2);
ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << (serialCnt_ - targetCnt)) - 1) & 0xFF;
serialize_is_fastcgb_);
ioamhram_[0x101] = ((ioamhram_[0x101] << (serialCnt_ - targetCnt)) |
(serialize_value_ >> (8 - (serialCnt_ - targetCnt)))) & 0xFF;
serialCnt_ = targetCnt;
}
}
checkSerial(cc);
}
void Memory::updateTimaIrq(unsigned long cc) {
@ -606,11 +643,12 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
serialCnt_ = 8;
if ((data & 0x81) == 0x81) {
intreq_.setEventTime<intevent_serial>((data & isCgb() * 2)
? (cc & ~0x07ul) + 0x010 * 8
: (cc & ~0xFFul) + 0x200 * 8);
} else
intreq_.setEventTime<intevent_serial>(disabled_time);
unsigned char receivedByte = 0xFF;
if (serial_io_ != 0) {
receivedByte = serial_io_->send(ioamhram_[0x101], (data & isCgb() * 2));
}
startSerialTransfer(cc, receivedByte, (data & isCgb() * 2));
}
data |= 0x7E - isCgb() * 2;
break;

View File

@ -28,6 +28,7 @@
namespace gambatte {
class InputGetter;
class SerialIO;
class Memory {
public:
@ -94,11 +95,13 @@ public:
} else
nontrivial_ff_write(p, data, cc);
}
void startSerialTransfer(unsigned long cycleCounter, unsigned char data, bool fastCgb);
unsigned long event(unsigned long cycleCounter);
unsigned long resetCounters(unsigned long cycleCounter);
void setSaveDir(std::string const &dir) { cart_.setSaveDir(dir); }
void setInputGetter(InputGetter *getInput) { getInput_ = getInput; }
void setSerialIO(SerialIO* serial_io) { serial_io_ = serial_io; }
void setEndtime(unsigned long cc, unsigned long inc);
void setSoundBuffer(uint_least32_t *buf) { psg_.setBuffer(buf); }
std::size_t fillSoundBuffer(unsigned long cc);
@ -113,6 +116,7 @@ public:
void setGameGenie(std::string const &codes) { cart_.setGameGenie(codes); }
void setGameShark(std::string const &codes) { interrupter_.setGameShark(codes); }
void checkSerial(unsigned long cc);
void updateInput();
int loadROM(const void *romdata, unsigned romsize, const bool forceDmg, const bool multicartCompat);
@ -120,7 +124,10 @@ public:
private:
Cartridge cart_;
unsigned char ioamhram_[0x200];
unsigned char serialize_value_;
bool serialize_is_fastcgb_;
InputGetter *getInput_;
SerialIO *serial_io_;
unsigned long divLastUpdate_;
unsigned long lastOamDmaUpdate_;
InterruptRequester intreq_;

View File

@ -61,6 +61,9 @@ void GB::reset() {
void GB::setInputGetter(InputGetter *getInput) {
p_->cpu.setInputGetter(getInput);
}
void GB::setSerialIO(SerialIO *serial_io) {
p_->cpu.setSerialIO(serial_io);
}
void GB::Priv::on_load_succeeded(unsigned flags)
{

View File

@ -75,6 +75,8 @@ struct SaveState {
unsigned short dmaDestination;
unsigned char rambank;
unsigned char oamDmaPos;
unsigned char serialize_value;
unsigned char serialize_is_fastcgb;
bool IME;
bool halted;
bool enableRam;

View File

@ -0,0 +1,35 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// 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 version 2 for more details.
//
// You should have received a copy of the GNU General Public License
// version 2 along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
#ifndef SERIAL_IO_H
#define SERIAL_IO_H
namespace gambatte {
class SerialIO
{
public:
virtual ~SerialIO() {};
virtual bool check(unsigned char out, unsigned char& in, bool& fastCgb) = 0;
virtual unsigned char send(unsigned char data, bool fastCgb) = 0;
};
}
#endif