mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
7a96321e78
commit
6cbc312f11
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
|
@ -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
|
|
@ -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;
|
||||||
|
};
|
|
@ -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) {
|
||||||
|
|
|
@ -7,7 +7,8 @@ struct Input {
|
||||||
SuperScope,
|
SuperScope,
|
||||||
Justifier,
|
Justifier,
|
||||||
Justifiers,
|
Justifiers,
|
||||||
Serial,
|
UART,
|
||||||
|
USART,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class JoypadID : unsigned {
|
enum class JoypadID : unsigned {
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
Loading…
Reference in New Issue