Commit Graph

3 Commits

Author SHA1 Message Date
byuu a266a2b5e2 Update to bsnes v066 release.
Major features in this release are: serial controller emulation, a brand new scheduler that supports multiple simultaneous coprocessors, and accuracy improvements.
The serial controller is something devised by blargg. With the proper voltage adjustments (5v-9v), it is possible to wire an SNES controller to a serial port, which can then be used for bidirectional communication between the SNES, and (usually, but not only) a PC. The support in bsnes was added so that such programs could be debugged and ran from within an emulator, and not just on real hardware.
The scheduler rewrite was meant to allow the combination of coprocessors. It was specifically meant to allow the serial controller thread to run alongside the SuperFX and SA-1 coprocessor threads, but it also allows fun things like MSU1 support in SuperFX and SA-1 games, and even creating dev cartridges that utilize both the SuperFX and SA-1 at the same time. The one thing not yet allowed is running multiple instances of the exact same coprocessor at the same time, as this is due to design constraints favoring code inlining.
There are two important accuracy updates. The first is that when PAL video mode is used without being in overscan mode, black bars are shown. Emulators have always shown this black bar at the bottom of the screen, but this is actually incorrect. resxto took pictures from his PAL TV that shows the image is in fact vertically centered in the screen. bsnes has been updated to reflect this.
Also interesting is that I have backported some code from the dot-based PPU renderer. In the game Uniracers, it writes to OAM during Hblank, and expects the write to go to a specific address. In previous releases, that address was hard-coded to go to the required memory location. But the way the hardware really works is that the write goes to the extended attribute address for the last sprite that the PPU fetched, as the PPU is still asserting the OAM address bus. Now, due to the precision limitations, I was not able to also port timing access during the active display period. However, this is sufficient to at least remove the last global hack from the older, speed-focused scanline renderer.
2010-08-01 05:46:17 +00:00
byuu 79f20030a0 Update to bsnes v065r01 release.
In order to fully decode the 16MB address maps, I am going to need to
use blargg's stop-and-swap, which will require a serial-communications
program. To develop this program, and others in the future, as well as
for testing automation, it would be very beneficial to have a way to
debug these programs through bsnes.

So what I'm working on now is emulating a serial port inside bsnes.
The basic premise is to start with a custom XML map that specifies its
presence:

    <?xml version="1.0" encoding="UTF-8"?>
    <cartridge region="NTSC">
    <rom>
    <map mode="linear" address="00-7f:8000-ffff"/>
    <map mode="linear" address="80-ff:8000-ffff"/>
    </rom>

    <ram size="2000">
    <map mode="linear" address="70-7f:0000-7fff"/>
    </ram>

    <serial baud="57600">
    </serial>
    </cartridge>


I am essentially treating the serial communication as a special chip.
One could argue this belongs as an option under controllers, and one
would be right. But as I can't have two coprocessor threads at the
same time, this is how it is for right now. Eventually it'll probably
be under controller port 2, and only enabled in the debugger builds.

So, this pseudo-coprocessor ... it's basically emulating the PC-side
communications program.
Meaning I still need to externalize this somehow so that any program
can be run.
The below program writes 0x96 six times, and then reads in data six
times. Well, technically forever, but the SNES ROM only writes six
times at the end.

    void Serial::enter() {
    scheduler.clock.cop_freq = cartridge.serial_baud_rate() * 2;

    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");
    }
    }


The SNES code looks like this:

    //21,477,272hz cpu / 57,600hz serial = ~372.87 clocks/tick

    org $8000
    xce; rep #$10
    ldx #$01ff; txs

    jsr serial_read; sta $700000
    jsr serial_read; sta $700001
    jsr serial_read; sta $700002
    jsr serial_read; sta $700003
    jsr serial_read; sta $700004
    jsr serial_read; sta $700005

    nop #46

    jsr serial_write
    jsr serial_write
    jsr serial_write
    jsr serial_write
    jsr serial_write
    jsr serial_write

    //hang
    -; bra -

    serial_read:
    -; lda $4017; and #$01; bne -
    -; lda $4017; and #$01; beq -
    nop #23; lda $00; nop #10

    lda $4017; and #$01; asl $00; ora $00; sta $00; lda $00; nop #13
    lda $4017; and #$01; asl $00; ora $00; sta $00; lda $00; nop #13
    lda $4017; and #$01; asl $00; ora $00; sta $00; lda $00; nop #13
    lda $4017; and #$01; asl $00; ora $00; sta $00; lda $00; nop #13
    lda $4017; and #$01; asl $00; ora $00; sta $00; lda $00; nop #13
    lda $4017; and #$01; asl $00; ora $00; sta $00; lda $00; nop #13
    lda $4017; and #$01; asl $00; ora $00; sta $00; lda $00; nop #13
    lda $4017; and #$01; asl $00; ora $00; sta $00; lda $00; nop #13

    rts

    serial_write:
    lda #$01; sta $4016; nop #23

    lda #$00; sta $4016; nop #23

    lda #$01; sta $4016; nop #23
    lda #$00; sta $4016; nop #23
    lda #$00; sta $4016; nop #23
    lda #$01; sta $4016; nop #23
    lda #$00; sta $4016; nop #23
    lda #$01; sta $4016; nop #23
    lda #$01; sta $4016; nop #23
    lda #$00; sta $4016; nop #23

    lda #$01; sta $4016; nop #23

    rts


That reads six times, and then writes six times.

I haven't made the test do an infinite PC->SNES transfer, but I have
for SNES->PC. No errors after millions of bytes, even if I stride the
CPU timing.

As this is just an example, it's pretty lousy with actual timing, but
I guess that's a good thing as it still works quite well. More
tolerance = less potential for errors.

**Now, this part is important.**

While debugging this, I noticed that my serial coprocessor was only
running when Vcounter=n,Hcounter=0. In other words, nothing was making
the CPU synchronize back to the coprocessor, except the once-per-
scanline synchronization that was there in case of CPU stalls.

That's ... really bad. MSU1 worked only because it buffers a few
hundred samples for audio mixing. I don't really know why this didn't
cause an issue for SuperFX, SA-1 or Super Game Boy games; but in
theory they were at most 682x less precise than they should have been
in terms of CPU->coprocessor synchronization.

Making it sync after every add_clocks() call results in a 10% speed
hit to SuperFX, SA-1 and Super Game Boy emulation, unfortunately.

I don't notice any quality improvements in emulation, but in theory
the lack of this syncing before effectively eliminated the benefits of
the SuperFX and SA-1 being cycle based.

I'm going to have to look into this more to understand if I was
intentionally not doing this sync before, or if I really did forget
it. I'm thinking it was an oversight right now. The SuperFX and SA-1
don't really talk back and forth a whole hell of a lot, so once a
scanline probably wouldn't be that noticeable.

But holy hell ... now that I'm thinking about it, I wonder if this was
the cause of all that craziness in trying to sync up the Super Game
Boy's VRAM transfers. Yeah, definitely need to look into this more ...
2010-07-09 15:02:23 +00:00
byuu 79b939e1c7 Update to bsnes v065 release.
It's been a while, so here is a new release of bsnes.
Unfortunately I don't have a full changelog this time. Most of the work went into stabilizing libsnes (which is used by the experimental .NET, Cocoa and Python UIs; as well as by Richard Bannister's OS X port).
The Windows binary package now comes with all three variants included: bsnes.exe, the standard version that casual users should run; bsnes-debugger.exe, for SNES programmers and ROM hackers only; and bsnes-accurate.exe, which should not be used by anybody, ever.
In all seriousness, bsnes-accurate.exe is bsnes with the dot-based S-PPU renderer. It's twice as slow as the normal build, and you won't really notice any differences except in Air Strike Patrol. It's there for the curious and for any SNES programmers who want to try making some really awesome video demos.
Changelog:
* OS X port builds once again; now requires gcc44 from Macports
* libsnes API finalized
* fixed a bug with S-CPU debugger breakpoints
* various source cleanup
2010-06-27 12:29:18 +00:00