Update to v086r06 release.

byuu says:

It is done. bsnes can now emulate sending and receiving data via USART.
As such, the UART code has been removed.
The final UART code can be downloaded here: http://byuu.org/snes/uart/
I won't maintain it going forward, because nobody ever used it, and
USART is superior in every way.

I've also verified both sending and receiving on the real SNES now :D
It's so easy ... a caveman with electrical engineering and computer
programming experience can do it.
This commit is contained in:
Tim Allen 2012-02-25 20:03:11 +11:00
parent 338f13e57b
commit e48671694e
11 changed files with 19 additions and 199 deletions

View File

@ -1,7 +1,7 @@
#ifndef BASE_HPP
#define BASE_HPP
const char Version[] = "086.05";
const char Version[] = "086.06";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>

View File

@ -8,7 +8,6 @@ namespace SNES {
#include "mouse/mouse.cpp"
#include "superscope/superscope.cpp"
#include "justifier/justifier.cpp"
#include "uart/uart.cpp"
#include "usart/usart.cpp"
void Controller::Enter() {

View File

@ -32,5 +32,4 @@ struct Controller : Processor {
#include "mouse/mouse.hpp"
#include "superscope/superscope.hpp"
#include "justifier/justifier.hpp"
#include "uart/uart.hpp"
#include "usart/usart.hpp"

View File

@ -1,148 +0,0 @@
#ifdef CONTROLLER_CPP
//Asynchronous 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 UART::enter() {
if(enable == false) {
//fallback, in case library was not found
interface->message("UART library not found");
while(true) step(1);
}
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 UART::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 UART::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 UART::data() {
return (data2 << 1) | (data1 << 0);
}
void UART::latch(bool data) {
latched = data;
}
UART::UART(bool port) : Controller(port) {
enable = false;
string filename = interface->path(Cartridge::Slot::Base, "uart.so");
if(open_absolute(filename)) {
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;
}
UART::~UART() {
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<UART*>(input.port1)) {
return ((UART*)input.port1)->step(clocks);
}
}
if(co_active() == input.port2->thread) {
if(dynamic_cast<UART*>(input.port2)) {
return ((UART*)input.port2)->step(clocks);
}
}
}
static uint8 snesserial_read() {
if(co_active() == input.port1->thread) {
if(dynamic_cast<UART*>(input.port1)) {
return ((UART*)input.port1)->read();
}
}
if(co_active() == input.port2->thread) {
if(dynamic_cast<UART*>(input.port2)) {
return ((UART*)input.port2)->read();
}
}
}
static void snesserial_write(uint8 data) {
if(co_active() == input.port1->thread) {
if(dynamic_cast<UART*>(input.port1)) {
return ((UART*)input.port1)->write(data);
}
}
if(co_active() == input.port2->thread) {
if(dynamic_cast<UART*>(input.port2)) {
return ((UART*)input.port2)->write(data);
}
}
}
#endif

View File

@ -1,19 +0,0 @@
struct UART : Controller, public library {
void enter();
uint8 read();
void write(uint8 data);
uint2 data();
void latch(bool data);
UART(bool port);
~UART();
private:
bool enable;
function<unsigned ()> baudrate;
function<bool ()> flowcontrol;
function<void (void (*)(unsigned), uint8_t (*)(), void (*)(uint8_t))> main;
bool latched;
bool data1;
bool data2;
};

View File

@ -35,21 +35,22 @@ uint8 USART::read() {
//USART -> SNES
void USART::write(uint8 data) {
rxbuffer.append(data);
rxbuffer.append(data ^ 0xff);
}
//USART -> SNES
uint2 USART::data() {
if(rxlength == 0 && rxbuffer.size()) {
data1 = 0;
data1 = 1;
rxdata = rxbuffer[0];
rxbuffer.remove(0);
rxlength++;
} else if(rxlength <= 8) {
data1 = rxdata & 1;
rxdata >>= 1;
rxlength++;
} else {
data1 = 1;
data1 = 0;
rxlength = 0;
}

View File

@ -17,7 +17,6 @@ void Input::connect(bool port, Input::Device id) {
case Device::SuperScope: controller = new SuperScope(port); break;
case Device::Justifier: controller = new Justifier(port, false); break;
case Device::Justifiers: controller = new Justifier(port, true); break;
case Device::UART: controller = new UART(port); break;
case Device::USART: controller = new USART(port); break;
}

View File

@ -7,7 +7,6 @@ struct Input {
SuperScope,
Justifier,
Justifiers,
UART,
USART,
};

View File

@ -39,7 +39,9 @@ MainWindow::MainWindow() {
snesPort1Device[1].setText("Gamepad");
snesPort1Device[2].setText("Multitap");
snesPort1Device[3].setText("Mouse");
RadioItem::group(snesPort1Device[0], snesPort1Device[1], snesPort1Device[2], snesPort1Device[3]);
snesPort1Device[4].setText("Serial USART");
RadioItem::group(snesPort1Device[0], snesPort1Device[1], snesPort1Device[2], snesPort1Device[3],
snesPort1Device[4]);
snesPort1Device[config->snes.controllerPort1Device].setChecked();
snesPort2.setText("Controller Port &2");
snesPort2Device[0].setText("None");
@ -49,10 +51,9 @@ MainWindow::MainWindow() {
snesPort2Device[4].setText("Super Scope");
snesPort2Device[5].setText("Justifier");
snesPort2Device[6].setText("Dual Justifiers");
snesPort2Device[7].setText("Serial UART");
snesPort2Device[8].setText("Serial USART");
snesPort2Device[7].setText("Serial USART");
RadioItem::group(snesPort2Device[0], snesPort2Device[1], snesPort2Device[2], snesPort2Device[3],
snesPort2Device[4], snesPort2Device[5], snesPort2Device[6], snesPort2Device[7], snesPort2Device[8]);
snesPort2Device[4], snesPort2Device[5], snesPort2Device[6], snesPort2Device[7]);
snesPort2Device[config->snes.controllerPort2Device].setChecked();
snesCartridgeUnload.setText("&Unload Cartridge");
@ -126,20 +127,9 @@ MainWindow::MainWindow() {
snesMenu.append(snesReset);
snesMenu.append(snesSeparator1);
snesMenu.append(snesPort1);
snesPort1.append(snesPort1Device[0]);
snesPort1.append(snesPort1Device[1]);
snesPort1.append(snesPort1Device[2]);
snesPort1.append(snesPort1Device[3]);
for(auto &item : snesPort1Device) snesPort1.append(item);
snesMenu.append(snesPort2);
snesPort2.append(snesPort2Device[0]);
snesPort2.append(snesPort2Device[1]);
snesPort2.append(snesPort2Device[2]);
snesPort2.append(snesPort2Device[3]);
snesPort2.append(snesPort2Device[4]);
snesPort2.append(snesPort2Device[5]);
snesPort2.append(snesPort2Device[6]);
snesPort2.append(snesPort2Device[7]);
snesPort2.append(snesPort2Device[8]);
for(auto &item : snesPort2Device) snesPort2.append(item);
snesMenu.append(snesSeparator2);
snesMenu.append(snesCartridgeUnload);
@ -248,6 +238,7 @@ MainWindow::MainWindow() {
snesPort1Device[1].onActivate = [&] { interface->setController(0, 1); };
snesPort1Device[2].onActivate = [&] { interface->setController(0, 2); };
snesPort1Device[3].onActivate = [&] { interface->setController(0, 3); };
snesPort1Device[4].onActivate = [&] { interface->setController(0, 4); };
snesPort2Device[0].onActivate = [&] { interface->setController(1, 0); };
snesPort2Device[1].onActivate = [&] { interface->setController(1, 1); };
@ -257,7 +248,6 @@ MainWindow::MainWindow() {
snesPort2Device[5].onActivate = [&] { interface->setController(1, 5); };
snesPort2Device[6].onActivate = [&] { interface->setController(1, 6); };
snesPort2Device[7].onActivate = [&] { interface->setController(1, 7); };
snesPort2Device[8].onActivate = [&] { interface->setController(1, 8); };
snesCartridgeUnload.onActivate = { &Interface::unloadCartridge, interface };

View File

@ -29,9 +29,9 @@ struct MainWindow : Window {
Item snesReset;
Separator snesSeparator1;
Menu snesPort1;
RadioItem snesPort1Device[4];
RadioItem snesPort1Device[5];
Menu snesPort2;
RadioItem snesPort2Device[9];
RadioItem snesPort2Device[8];
Separator snesSeparator2;
Item snesCartridgeUnload;

View File

@ -7,14 +7,15 @@ void InterfaceSNES::setController(bool port, unsigned device) {
if(port == 0) config->snes.controllerPort1Device = device;
if(port == 1) config->snes.controllerPort2Device = device;
if(port == 0) switch(device) {
if(port == 0) switch(device) { default:
case 0: return SNES::input.connect(0, SNES::Input::Device::None);
case 1: return SNES::input.connect(0, SNES::Input::Device::Joypad);
case 2: return SNES::input.connect(0, SNES::Input::Device::Multitap);
case 3: return SNES::input.connect(0, SNES::Input::Device::Mouse);
case 4: return SNES::input.connect(0, SNES::Input::Device::USART);
}
if(port == 1) switch(device) {
if(port == 1) switch(device) { default:
case 0: return SNES::input.connect(1, SNES::Input::Device::None);
case 1: return SNES::input.connect(1, SNES::Input::Device::Joypad);
case 2: return SNES::input.connect(1, SNES::Input::Device::Multitap);
@ -22,8 +23,7 @@ void InterfaceSNES::setController(bool port, unsigned device) {
case 4: return SNES::input.connect(1, SNES::Input::Device::SuperScope);
case 5: return SNES::input.connect(1, SNES::Input::Device::Justifier);
case 6: return SNES::input.connect(1, SNES::Input::Device::Justifiers);
case 7: return SNES::input.connect(1, SNES::Input::Device::UART);
case 8: return SNES::input.connect(1, SNES::Input::Device::USART);
case 7: return SNES::input.connect(1, SNES::Input::Device::USART);
}
}