mirror of https://github.com/bsnes-emu/bsnes.git
Update to bsnes v065r02 release.
Be warned, this is going to get complicated. To start out with, serial.tar.bz2 is a simple SNES program that is right now being used for integrity testing. The important part is here: serial_read: lda #$01 -; bit $4017; bne - -; bit $4017; beq - nop #24 pha; lda $4017; eor #$01; ror; pla; ror; nop #18 pha; lda $4017; eor #$01; ror; pla; ror; nop #18 pha; lda $4017; eor #$01; ror; pla; ror; nop #18 pha; lda $4017; eor #$01; ror; pla; ror; nop #18 pha; lda $4017; eor #$01; ror; pla; ror; nop #18 pha; lda $4017; eor #$01; ror; pla; ror; nop #18 pha; lda $4017; eor #$01; ror; pla; ror; nop #18 pha; lda $4017; eor #$01; ror; pla; ror; rts serial_write: pha #6; pla #6; wdm #$00; stz $4016; sec pha #6; pla #6; wdm #$00; sta $4016; ror pha #6; pla #6; wdm #$00; sta $4016; ror pha #6; pla #6; wdm #$00; sta $4016; ror pha #6; pla #6; wdm #$00; sta $4016; ror pha #6; pla #6; wdm #$00; sta $4016; ror pha #6; pla #6; wdm #$00; sta $4016; ror pha #6; pla #6; wdm #$00; sta $4016; ror pha #6; pla #6; wdm #$00; sta $4016; ror pha #6; pla #6; wdm #$00; sta $4016; rts Fairly ugly, but it works. Next, I needed a way to be able to execute the client in such a way that it would work with both bsnes and real hardware, with no recompilation or changes needed. The nice way would be some form of inter-process communication, but well, I don't really do that. And it's probably extremely platform-dependent. So I used what was available to me already, a cross-platform dlopen wrapper for dynamic library support. The client application is written and compiled into a shared library that exports a single function, snesserial_main, which runs as if it is its own program and never returns. It takes three parameters, the time tick(), read() and write() function pointers. It can call them to do its work. This process is put in a folder called snesserial for now. It's the accompanying program to serial.sfc. Now I have both bsnes (v065.02 above) and snesserver, they both act the same way. They load in snesserial, and give it those three functions and call its main entry point. The difference is that the read/write functions in bsnes simulate a serial strobe to the emulator, and the read/write functions in snesserver actually read and write to the TTY device you specify as the program argument, eg for me I use: ./snesserver /dev/ttyUSB0 Mmm, USB<>SNES for the win. There's a limitation in my dlopen wrapper, it adds the libN.so or N.dll stuff automatically, making it difficult to also specify a path. That means that for now you have to put libsnesserial.so into /usr/local/lib. Obviously you don't want to be limited to just one program. The plan is to have it load the library that matches the game name: zelda.sfc + zelda.so/zelda.dll/zelda.dylib (yeah, no libzelda.so.) Now, the bsnes+serial emulation works on any platform. However, snesserver only works on Linux. You can blame that one on Microsoft. They make you require special kernel drivers to be able to access the serial port. I'm not going through the trouble. OS X can probably use it if it makes the appropriate /dev/tty device, but I'm not going to test it. Activating the module can only be done with a custom XML file, as before. Still need to work on integration with the controller port options, as it's not really a special chip. Lastly, the timing is "pretty good", but not perfect. It accepted a 374 cycle-per-bit loop for serial writes from the SNES to the PC, but snesserver did not. I had to adjust to 364 cycle-per-bit loop for it to work on both. This is really bad, because the goal here is to use the PC as the testbed to make sure it'll work on the real thing. On the plus side, you only have to get it working one time, and stick with the serial_read/serial_write functions like at the top of this post. But I do need to address this. I'm not entirely sure how to more accurately simulate a serial port at this point though. Oh, and I am thinking I need to expand the read/write functions to process more than one byte at a time. That should greatly speed up transfer time on the real thing. I also need to add some slowdown so the emulator more closely matches hardware speeds.
This commit is contained in:
parent
79f20030a0
commit
20b44ddfd1
|
@ -5,40 +5,16 @@ namespace SNES {
|
|||
|
||||
Serial serial;
|
||||
|
||||
static void snesserial_tick(unsigned clocks) { serial.add_clocks(clocks); }
|
||||
static uint8 snesserial_read() { return serial.read(); }
|
||||
static void snesserial_write(uint8 data) { serial.write(data); }
|
||||
|
||||
void Serial::enter() {
|
||||
scheduler.clock.cop_freq = cartridge.serial_baud_rate() * 2;
|
||||
|
||||
scheduler.clock.cop_freq = cartridge.serial_baud_rate() * 4;
|
||||
latch = 0;
|
||||
add_clocks(512);
|
||||
|
||||
for(unsigned i = 0; i < 6; i++) {
|
||||
latch = 1; add_clocks(2);
|
||||
|
||||
latch = 1; add_clocks(2);
|
||||
latch = 0; add_clocks(2);
|
||||
latch = 0; add_clocks(2);
|
||||
latch = 1; add_clocks(2);
|
||||
latch = 0; add_clocks(2);
|
||||
latch = 1; add_clocks(2);
|
||||
latch = 1; add_clocks(2);
|
||||
latch = 0; add_clocks(2);
|
||||
|
||||
latch = 0; add_clocks(2);
|
||||
}
|
||||
|
||||
while(true) {
|
||||
while(cpu.joylatch() == 0) add_clocks(1);
|
||||
while(cpu.joylatch() == 1) add_clocks(1);
|
||||
add_clocks(1);
|
||||
|
||||
uint8 data = 0;
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
add_clocks(2);
|
||||
data = (data << 1) | cpu.joylatch();
|
||||
}
|
||||
|
||||
print("Read ", strhex<2>(data), "\n");
|
||||
}
|
||||
add_clocks(256 * 4);
|
||||
if(snesserial_main) snesserial_main(snesserial_tick, snesserial_read, snesserial_write);
|
||||
while(true) add_clocks(scheduler.clock.cop_freq);
|
||||
}
|
||||
|
||||
void Serial::add_clocks(unsigned clocks) {
|
||||
|
@ -46,10 +22,42 @@ void Serial::add_clocks(unsigned clocks) {
|
|||
scheduler.sync_copcpu();
|
||||
}
|
||||
|
||||
uint8 Serial::read() {
|
||||
while(cpu.joylatch() == 0) add_clocks(1);
|
||||
while(cpu.joylatch() == 1) add_clocks(1);
|
||||
add_clocks(2);
|
||||
|
||||
uint8 data = 0;
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
add_clocks(4);
|
||||
data = (cpu.joylatch() << 7) | (data >> 1);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Serial::write(uint8 data) {
|
||||
latch = 1;
|
||||
add_clocks(4);
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
latch = (data & 0x01) ^ 1;
|
||||
data >>= 1;
|
||||
add_clocks(4);
|
||||
}
|
||||
|
||||
latch = 0;
|
||||
add_clocks(4);
|
||||
}
|
||||
|
||||
void Serial::init() {
|
||||
}
|
||||
|
||||
void Serial::enable() {
|
||||
if(opened()) close();
|
||||
if(open("snesserial")) {
|
||||
snesserial_main = sym("snesserial_main");
|
||||
}
|
||||
}
|
||||
|
||||
void Serial::power() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class Serial : property<Serial> {
|
||||
class Serial : property<Serial>, public library {
|
||||
public:
|
||||
void enter();
|
||||
|
||||
|
@ -9,8 +9,11 @@ public:
|
|||
|
||||
readonly<bool> latch;
|
||||
|
||||
private:
|
||||
void add_clocks(unsigned clocks);
|
||||
uint8 read();
|
||||
void write(uint8 data);
|
||||
|
||||
function<void (void (*)(unsigned), uint8_t (*)(), void (*)(uint8_t))> snesserial_main;
|
||||
};
|
||||
|
||||
extern Serial serial;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
static const char bsnesVersion[] = "065.01";
|
||||
static const char bsnesVersion[] = "065.02";
|
||||
static const char bsnesTitle[] = "bsnes";
|
||||
static const unsigned bsnesSerializerVersion = 10;
|
||||
|
||||
|
@ -16,7 +16,7 @@ static const unsigned bsnesSerializerVersion = 10;
|
|||
#define CHEAT_SYSTEM
|
||||
|
||||
//enable debugging extensions (~15% speed hit)
|
||||
//#define DEBUGGER
|
||||
#define DEBUGGER
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
|
|
Loading…
Reference in New Issue