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:
byuu 2010-07-11 18:27:42 +00:00
parent 79f20030a0
commit 20b44ddfd1
3 changed files with 47 additions and 36 deletions

View File

@ -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() {

View File

@ -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;

View File

@ -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>