Update to v086r04 release.

byuu says:

There will probably be a series of small WIPs as I experiment here.

snes/controller/serial is now snes/controller/uart. Asynchronous serial
communications, typically capped at 57,600 baud.
snes/controller/usart is new. It aims to emulate the SNES connected to
a Teensy++ board, and can easily handle 524,288 baud.
And much more importantly, it's synchronous, so there are no timing
issues anymore. Just bit-bang as fast as you can.

Right now, the USART code is just enough for SNES->PC to transfer data
to ... well, nothing yet.

Unless anyone is actually using the UART stuff, I'll be removing it once
the USART is totally up and running.
No sense maintaining code that is 10x slower, more error prone, and used
by nobody.

Note: this is all thanks to blargg being absolutely amazing.
This commit is contained in:
Tim Allen 2012-02-25 19:49:27 +11:00
parent 7a96321e78
commit 6cbc312f11
12 changed files with 141 additions and 38 deletions

View File

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

View File

@ -8,7 +8,8 @@ namespace SNES {
#include "mouse/mouse.cpp" #include "mouse/mouse.cpp"
#include "superscope/superscope.cpp" #include "superscope/superscope.cpp"
#include "justifier/justifier.cpp" #include "justifier/justifier.cpp"
#include "serial/serial.cpp" #include "uart/uart.cpp"
#include "usart/usart.cpp"
void Controller::Enter() { void Controller::Enter() {
if(co_active() == input.port1->thread) input.port1->enter(); if(co_active() == input.port1->thread) input.port1->enter();

View File

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

View File

@ -1,6 +1,6 @@
#ifdef CONTROLLER_CPP #ifdef CONTROLLER_CPP
//Serial communications cable emulation: //Asynchronous serial communications cable emulation:
//The SNES controller ports can be used for bi-directional serial communication //The SNES controller ports can be used for bi-directional serial communication
//when wired to a specialized controller. This class implements said controller, //when wired to a specialized controller. This class implements said controller,
//for the primary purpose of testing code outside of real hardware. //for the primary purpose of testing code outside of real hardware.
@ -30,15 +30,19 @@ static void snesserial_tick(unsigned clocks);
static uint8 snesserial_read(); static uint8 snesserial_read();
static void snesserial_write(uint8 data); static void snesserial_write(uint8 data);
void Serial::enter() { void UART::enter() {
if(enable == false) while(true) step(1); //fallback, in case library was not found 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 step(256 * 8); //simulate warm-up delay
if(flowcontrol()) data2 = 1; if(flowcontrol()) data2 = 1;
main(snesserial_tick, snesserial_read, snesserial_write); //stubs for Serial::step, Serial::read, Serial::write 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) while(true) step(1); //fallback, in case snesserial_main() returns (it should never do so)
} }
uint8 Serial::read() { uint8 UART::read() {
while(latched == 0) step(1); while(latched == 0) step(1);
while(latched == 1) step(1); while(latched == 1) step(1);
step(4); step(4);
@ -52,7 +56,7 @@ uint8 Serial::read() {
return data; return data;
} }
void Serial::write(uint8 data) { void UART::write(uint8 data) {
if(flowcontrol()) while(iobit()) step(1); if(flowcontrol()) while(iobit()) step(1);
step(8); step(8);
@ -69,20 +73,18 @@ void Serial::write(uint8 data) {
step(8); step(8);
} }
uint2 Serial::data() { uint2 UART::data() {
return (data2 << 1) | (data1 << 0); return (data2 << 1) | (data1 << 0);
} }
void Serial::latch(bool data) { void UART::latch(bool data) {
latched = data; latched = data;
} }
Serial::Serial(bool port) : Controller(port) { UART::UART(bool port) : Controller(port) {
enable = false; enable = false;
string basename = interface->path(Cartridge::Slot::Base, "serial.so"); string filename = interface->path(Cartridge::Slot::Base, "uart.so");
string name = notdir(basename); if(open_absolute(filename)) {
string path = dir(basename);
if(open(name, path)) {
baudrate = sym("snesserial_baudrate"); baudrate = sym("snesserial_baudrate");
flowcontrol = sym("snesserial_flowcontrol"); flowcontrol = sym("snesserial_flowcontrol");
main = sym("snesserial_main"); main = sym("snesserial_main");
@ -95,7 +97,7 @@ Serial::Serial(bool port) : Controller(port) {
data2 = 0; data2 = 0;
} }
Serial::~Serial() { UART::~UART() {
if(opened()) close(); if(opened()) close();
} }
@ -103,42 +105,42 @@ Serial::~Serial() {
static void snesserial_tick(unsigned clocks) { static void snesserial_tick(unsigned clocks) {
if(co_active() == input.port1->thread) { if(co_active() == input.port1->thread) {
if(dynamic_cast<Serial*>(input.port1)) { if(dynamic_cast<UART*>(input.port1)) {
return ((Serial*)input.port1)->step(clocks); return ((UART*)input.port1)->step(clocks);
} }
} }
if(co_active() == input.port2->thread) { if(co_active() == input.port2->thread) {
if(dynamic_cast<Serial*>(input.port2)) { if(dynamic_cast<UART*>(input.port2)) {
return ((Serial*)input.port2)->step(clocks); return ((UART*)input.port2)->step(clocks);
} }
} }
} }
static uint8 snesserial_read() { static uint8 snesserial_read() {
if(co_active() == input.port1->thread) { if(co_active() == input.port1->thread) {
if(dynamic_cast<Serial*>(input.port1)) { if(dynamic_cast<UART*>(input.port1)) {
return ((Serial*)input.port1)->read(); return ((UART*)input.port1)->read();
} }
} }
if(co_active() == input.port2->thread) { if(co_active() == input.port2->thread) {
if(dynamic_cast<Serial*>(input.port2)) { if(dynamic_cast<UART*>(input.port2)) {
return ((Serial*)input.port2)->read(); return ((UART*)input.port2)->read();
} }
} }
} }
static void snesserial_write(uint8 data) { static void snesserial_write(uint8 data) {
if(co_active() == input.port1->thread) { if(co_active() == input.port1->thread) {
if(dynamic_cast<Serial*>(input.port1)) { if(dynamic_cast<UART*>(input.port1)) {
return ((Serial*)input.port1)->write(data); return ((UART*)input.port1)->write(data);
} }
} }
if(co_active() == input.port2->thread) { if(co_active() == input.port2->thread) {
if(dynamic_cast<Serial*>(input.port2)) { if(dynamic_cast<UART*>(input.port2)) {
return ((Serial*)input.port2)->write(data); return ((UART*)input.port2)->write(data);
} }
} }
} }

View File

@ -1,11 +1,11 @@
struct Serial : Controller, public library { struct UART : Controller, public library {
void enter(); void enter();
uint8 read(); uint8 read();
void write(uint8 data); void write(uint8 data);
uint2 data(); uint2 data();
void latch(bool data); void latch(bool data);
Serial(bool port); UART(bool port);
~Serial(); ~UART();
private: private:
bool enable; bool enable;

View File

@ -0,0 +1,75 @@
#ifdef CONTROLLER_CPP
//Synchronous serial communications cable emulation
//Hardware:
//Teensy++ 2.0 USB
//AT90USB1286
//Connection Diagram:
//[SNES] [Teensy]
// +5v ---
// Clock D5
// Latch D2
// Data1 D3
// Data2 ---
// IOBit ---
// GND GND
static uint8 usart_read();
static void usart_write(uint8 data);
//USART -> SNES
uint2 USART::data() {
if(rxlength == 0) {
data1 = 0;
rxdata = usart_read();
} else if(rxlength <= 8) {
data1 = rxdata & 1;
rxdata >>= 1;
} else {
data1 = 1;
rxlength = 0;
}
return (data2 << 1) | (data1 << 0);
}
//SNES -> USART
void USART::latch(bool data) {
if(txlength == 0 && latched == 1 && data == 0) {
txlength++;
} else if(txlength <= 8) {
txdata = (data << 7) | (txdata >> 1);
txlength++;
} else {
if(data == 1) usart_write(txdata);
txlength = 0;
}
latched = data;
}
USART::USART(bool port) : Controller(port) {
latched = 0;
data1 = 0;
data2 = 0;
rxlength = 0;
rxdata = 0;
txlength = 0;
txdata = 0;
}
USART::~USART() {
}
static uint8 usart_read() {
return 0xff;
}
static void usart_write(uint8 data) {
}
#endif

View File

@ -0,0 +1,17 @@
struct USART : Controller, public library {
uint2 data();
void latch(bool data);
USART(bool port);
~USART();
private:
bool latched;
bool data1;
bool data2;
uint8 rxlength;
uint8 rxdata;
uint8 txlength;
uint8 txdata;
};

View File

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

View File

@ -7,7 +7,8 @@ struct Input {
SuperScope, SuperScope,
Justifier, Justifier,
Justifiers, Justifiers,
Serial, UART,
USART,
}; };
enum class JoypadID : unsigned { enum class JoypadID : unsigned {

View File

@ -49,9 +49,10 @@ MainWindow::MainWindow() {
snesPort2Device[4].setText("Super Scope"); snesPort2Device[4].setText("Super Scope");
snesPort2Device[5].setText("Justifier"); snesPort2Device[5].setText("Justifier");
snesPort2Device[6].setText("Dual Justifiers"); snesPort2Device[6].setText("Dual Justifiers");
snesPort2Device[7].setText("Serial Cable"); snesPort2Device[7].setText("Serial UART");
snesPort2Device[8].setText("Serial USART");
RadioItem::group(snesPort2Device[0], snesPort2Device[1], snesPort2Device[2], snesPort2Device[3], RadioItem::group(snesPort2Device[0], snesPort2Device[1], snesPort2Device[2], snesPort2Device[3],
snesPort2Device[4], snesPort2Device[5], snesPort2Device[6], snesPort2Device[7]); snesPort2Device[4], snesPort2Device[5], snesPort2Device[6], snesPort2Device[7], snesPort2Device[8]);
snesPort2Device[config->snes.controllerPort2Device].setChecked(); snesPort2Device[config->snes.controllerPort2Device].setChecked();
snesCartridgeUnload.setText("&Unload Cartridge"); snesCartridgeUnload.setText("&Unload Cartridge");
@ -138,6 +139,7 @@ MainWindow::MainWindow() {
snesPort2.append(snesPort2Device[5]); snesPort2.append(snesPort2Device[5]);
snesPort2.append(snesPort2Device[6]); snesPort2.append(snesPort2Device[6]);
snesPort2.append(snesPort2Device[7]); snesPort2.append(snesPort2Device[7]);
snesPort2.append(snesPort2Device[8]);
snesMenu.append(snesSeparator2); snesMenu.append(snesSeparator2);
snesMenu.append(snesCartridgeUnload); snesMenu.append(snesCartridgeUnload);
@ -255,6 +257,7 @@ MainWindow::MainWindow() {
snesPort2Device[5].onActivate = [&] { interface->setController(1, 5); }; snesPort2Device[5].onActivate = [&] { interface->setController(1, 5); };
snesPort2Device[6].onActivate = [&] { interface->setController(1, 6); }; snesPort2Device[6].onActivate = [&] { interface->setController(1, 6); };
snesPort2Device[7].onActivate = [&] { interface->setController(1, 7); }; snesPort2Device[7].onActivate = [&] { interface->setController(1, 7); };
snesPort2Device[8].onActivate = [&] { interface->setController(1, 8); };
snesCartridgeUnload.onActivate = { &Interface::unloadCartridge, interface }; snesCartridgeUnload.onActivate = { &Interface::unloadCartridge, interface };

View File

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

View File

@ -22,7 +22,8 @@ void InterfaceSNES::setController(bool port, unsigned device) {
case 4: return SNES::input.connect(1, SNES::Input::Device::SuperScope); case 4: return SNES::input.connect(1, SNES::Input::Device::SuperScope);
case 5: return SNES::input.connect(1, SNES::Input::Device::Justifier); case 5: return SNES::input.connect(1, SNES::Input::Device::Justifier);
case 6: return SNES::input.connect(1, SNES::Input::Device::Justifiers); case 6: return SNES::input.connect(1, SNES::Input::Device::Justifiers);
case 7: return SNES::input.connect(1, SNES::Input::Device::Serial); case 7: return SNES::input.connect(1, SNES::Input::Device::UART);
case 8: return SNES::input.connect(1, SNES::Input::Device::USART);
} }
} }
@ -412,7 +413,8 @@ string InterfaceSNES::path(SNES::Cartridge::Slot slot, const string &hint) {
track.trim<1>("track-", ".pcm"); track.trim<1>("track-", ".pcm");
return interface->base.filename(hint, { "-", decimal(track), ".pcm" }); return interface->base.filename(hint, { "-", decimal(track), ".pcm" });
} }
if(hint == "serial.so") return { dir(interface->base.name), "libserial.so" }; if(hint == "uart.so") return { dir(interface->base.name), "uart.so" };
if(hint == "usart.so") return { dir(interface->base.name), "usart.so" };
if(hint.endswith(".rom")) return { dir(interface->base.name), hint }; if(hint.endswith(".rom")) return { dir(interface->base.name), hint };
} }
return { dir(interface->base.name), hint }; return { dir(interface->base.name), hint };