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