Themaister b927f5f9db Revert "Use snes_interface for global symbol."
This reverts commit 7d07cb17516c3ce1269c0af8d80431da6f3139a0.
2011-11-01 22:43:05 +01:00

149 lines
3.9 KiB
C++

#ifdef CONTROLLER_CPP
//Serial communications cable emulation:
//The SNES controller ports can be used for bi-directional serial communication
//when wired to a specialized controller. This class implements said controller,
//for the primary purpose of testing code outside of real hardware.
//The basic idea is to wire the SNES controller pins to a UART, such as
//the MAX232N; or a serial->USB cable, such as the FTDI TTL-232R-5V.
//Connection Diagram:
//[SNES] [UART] [Purpose]
// Latch RXD Data transfer
// Data1 TXD Data transfer
// Data2 RTS Flow control (optional)
// IOBit CTS Flow control (optional)
// GND GND Circuit completion
//The SNES software program will have to use specially timed code to send and
//receive data at a specific baud-rate; whereas the PC handles timing via the
//UART.
//The emulator implementation is designed so that the same PC-side program can
//be used both under emulation and on real hardware. It does this by linking to
//a dynamic library for timing, read and write operations. This library is
//responsible for setting both the baud-rate and flow control setting. The
//SNES-side software program must know about and respect the library setting.
static void snesserial_tick(unsigned clocks);
static uint8 snesserial_read();
static void snesserial_write(uint8 data);
void Serial::enter() {
if(enable == false) while(true) step(1); //fallback, in case library was not found
step(256 * 8); //simulate warm-up delay
if(flowcontrol()) data2 = 1;
main(snesserial_tick, snesserial_read, snesserial_write); //stubs for Serial::step, Serial::read, Serial::write
while(true) step(1); //fallback, in case snesserial_main() returns (it should never do so)
}
uint8 Serial::read() {
while(latched == 0) step(1);
while(latched == 1) step(1);
step(4);
uint8 data = 0;
for(unsigned i = 0; i < 8; i++) {
step(8);
data = (latched << 7) | (data >> 1);
}
return data;
}
void Serial::write(uint8 data) {
if(flowcontrol()) while(iobit()) step(1);
step(8);
data1 = 1;
step(8);
for(unsigned i = 0; i < 8; i++) {
data1 = (data & 1) ^ 1;
data >>= 1;
step(8);
}
data1 = 0;
step(8);
}
uint2 Serial::data() {
return (data2 << 1) | (data1 << 0);
}
void Serial::latch(bool data) {
latched = data;
}
Serial::Serial(bool port) : Controller(port) {
enable = false;
string basename = interface->path(Cartridge::Slot::Base, "");
string name = notdir(basename);
string path = dir(basename);
if(open(name, path)) {
baudrate = sym("snesserial_baudrate");
flowcontrol = sym("snesserial_flowcontrol");
main = sym("snesserial_main");
if(baudrate && flowcontrol && main) enable = true;
}
create(Controller::Enter, enable ? baudrate() * 8 : 1);
latched = false;
data1 = 0;
data2 = 0;
}
Serial::~Serial() {
if(opened()) close();
}
//stubs needed to call into class objects from global function pointers
static void snesserial_tick(unsigned clocks) {
if(co_active() == input.port1->thread) {
if(dynamic_cast<Serial*>(input.port1)) {
return ((Serial*)input.port1)->step(clocks);
}
}
if(co_active() == input.port2->thread) {
if(dynamic_cast<Serial*>(input.port2)) {
return ((Serial*)input.port2)->step(clocks);
}
}
}
static uint8 snesserial_read() {
if(co_active() == input.port1->thread) {
if(dynamic_cast<Serial*>(input.port1)) {
return ((Serial*)input.port1)->read();
}
}
if(co_active() == input.port2->thread) {
if(dynamic_cast<Serial*>(input.port2)) {
return ((Serial*)input.port2)->read();
}
}
return 0;
}
static void snesserial_write(uint8 data) {
if(co_active() == input.port1->thread) {
if(dynamic_cast<Serial*>(input.port1)) {
return ((Serial*)input.port1)->write(data);
}
}
if(co_active() == input.port2->thread) {
if(dynamic_cast<Serial*>(input.port2)) {
return ((Serial*)input.port2)->write(data);
}
}
}
#endif