Update to v080r01 release.

byuu says:

There was one unfortunate aspect of the S-DD1 module: you had to give it
the DMA length and a target buffer, and it would do the entire
decompression at once. Real hardware would work by streaming the data
byte by byte. So with that, I went ahead and rewrote the code to handle
byte-based streaming.

This WIP is an important milestone for me personally. Up until now,
bsnes has always had code that was directly copy-pasted from other
authors. With all of the DSP and Cx4 chips rewritten in LLE, and the
SPC7110 algorithm already ported over from C, and archive decompression
code removed for a long time, the S-DD1 was the only module left like
this. It's obviously not that big of a deal. The code is basically still
a copy of the original. S-DD1 decomp from Andreas Naive, SPC7110 decomp
from neviksti, and S-DSP from blargg. And the rest of the emulator is of
course only possible because of code and research before it, although
everything else has no resemblance at all to code before it. The main
advantage, really, is absolute code consistency. I always use the same
variant of K&R, for instance. I dunno, I guess I just never really liked
the "Build-a-Bear Workshop" style of emulators, like is so prominent in
the Genesis scene: "My new Genesis emu (uses Starscream/Musashi 68K
core, Marat Fayzullin's Z80 core, YM2612 core from Game_Music_Emu, VDP
core from Gens, SVP core from picodrive)", sorry, but you wrote
a front-end, not an emulator :/

I also updated the SPC7110 decompression module: I merged the class
inside the SPC7110 class (not sure why it was separate before), and
replaced the morton lookup tables with for-loops. The morton tables were
added to be a tiny bit faster when I was more interested in speed than
code clarity. It may be a tiny bit slower (or faster due to less L2
cache usage), but you won't even notice an FPS drop, and it cuts out
a good chunk of code and some tables. Lastly, I added pinput_poll() to
video_refresh(). Forgot to remove Interface::input_poll() from the C++
side, will have to do that later.
This commit is contained in:
Tim Allen 2011-06-28 21:36:00 +10:00
parent 5fc86eae6d
commit 8ae6444af7
15 changed files with 439 additions and 730 deletions

View File

@ -1,7 +1,7 @@
include nall/Makefile
snes := snes
gameboy := gameboy
profile := compatibility
profile := accuracy
ui := ui
# debugger

View File

@ -34,7 +34,7 @@ void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) {
nvram.reset();
parse_xml(xml_list);
print(xml_list[0], "\n\n");
//print(xml_list[0], "\n\n");
if(ram_size > 0) {
ram.map(allocate<uint8>(ram_size, 0xff), ram_size);

287
bsnes/snes/chip/sdd1/decomp.cpp Executable file
View File

@ -0,0 +1,287 @@
//S-DD1 decompression algorithm implementation
//original code written by Andreas Naive (public domain license)
//bsnes port written by byuu
//note: decompression module does not need to be serialized with bsnes
//this is because decompression only runs during DMA, and bsnes will complete
//any pending DMA transfers prior to serialization.
//input manager
void SDD1::Decomp::IM::init(unsigned offset_) {
offset = offset_;
bit_count = 4;
}
uint8 SDD1::Decomp::IM::get_codeword(uint8 code_length) {
uint8 codeword;
uint8 comp_count;
codeword = self.rom_read(offset) << bit_count;
bit_count++;
if(codeword & 0x80) {
codeword |= self.rom_read(offset + 1) >> (9 - bit_count);
bit_count += code_length;
}
if(bit_count & 0x08) {
offset++;
bit_count &= 0x07;
}
return codeword;
}
//golomb-code decoder
const uint8 SDD1::Decomp::GCD::run_count[] = {
0x00, 0x00, 0x01, 0x00, 0x03, 0x01, 0x02, 0x00,
0x07, 0x03, 0x05, 0x01, 0x06, 0x02, 0x04, 0x00,
0x0f, 0x07, 0x0b, 0x03, 0x0d, 0x05, 0x09, 0x01,
0x0e, 0x06, 0x0a, 0x02, 0x0c, 0x04, 0x08, 0x00,
0x1f, 0x0f, 0x17, 0x07, 0x1b, 0x0b, 0x13, 0x03,
0x1d, 0x0d, 0x15, 0x05, 0x19, 0x09, 0x11, 0x01,
0x1e, 0x0e, 0x16, 0x06, 0x1a, 0x0a, 0x12, 0x02,
0x1c, 0x0c, 0x14, 0x04, 0x18, 0x08, 0x10, 0x00,
0x3f, 0x1f, 0x2f, 0x0f, 0x37, 0x17, 0x27, 0x07,
0x3b, 0x1b, 0x2b, 0x0b, 0x33, 0x13, 0x23, 0x03,
0x3d, 0x1d, 0x2d, 0x0d, 0x35, 0x15, 0x25, 0x05,
0x39, 0x19, 0x29, 0x09, 0x31, 0x11, 0x21, 0x01,
0x3e, 0x1e, 0x2e, 0x0e, 0x36, 0x16, 0x26, 0x06,
0x3a, 0x1a, 0x2a, 0x0a, 0x32, 0x12, 0x22, 0x02,
0x3c, 0x1c, 0x2c, 0x0c, 0x34, 0x14, 0x24, 0x04,
0x38, 0x18, 0x28, 0x08, 0x30, 0x10, 0x20, 0x00,
0x7f, 0x3f, 0x5f, 0x1f, 0x6f, 0x2f, 0x4f, 0x0f,
0x77, 0x37, 0x57, 0x17, 0x67, 0x27, 0x47, 0x07,
0x7b, 0x3b, 0x5b, 0x1b, 0x6b, 0x2b, 0x4b, 0x0b,
0x73, 0x33, 0x53, 0x13, 0x63, 0x23, 0x43, 0x03,
0x7d, 0x3d, 0x5d, 0x1d, 0x6d, 0x2d, 0x4d, 0x0d,
0x75, 0x35, 0x55, 0x15, 0x65, 0x25, 0x45, 0x05,
0x79, 0x39, 0x59, 0x19, 0x69, 0x29, 0x49, 0x09,
0x71, 0x31, 0x51, 0x11, 0x61, 0x21, 0x41, 0x01,
0x7e, 0x3e, 0x5e, 0x1e, 0x6e, 0x2e, 0x4e, 0x0e,
0x76, 0x36, 0x56, 0x16, 0x66, 0x26, 0x46, 0x06,
0x7a, 0x3a, 0x5a, 0x1a, 0x6a, 0x2a, 0x4a, 0x0a,
0x72, 0x32, 0x52, 0x12, 0x62, 0x22, 0x42, 0x02,
0x7c, 0x3c, 0x5c, 0x1c, 0x6c, 0x2c, 0x4c, 0x0c,
0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04,
0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08,
0x70, 0x30, 0x50, 0x10, 0x60, 0x20, 0x40, 0x00,
};
void SDD1::Decomp::GCD::get_run_count(uint8 code_number, uint8 &mps_count, bool &lps_index) {
uint8 codeword = self.im.get_codeword(code_number);
if(codeword & 0x80) {
lps_index = 1;
mps_count = run_count[codeword >> (code_number ^ 0x07)];
} else {
mps_count = 1 << code_number;
}
}
//bits generator
void SDD1::Decomp::BG::init() {
mps_count = 0;
lps_index = 0;
}
uint8 SDD1::Decomp::BG::get_bit(bool &end_of_run) {
if(!(mps_count || lps_index)) self.gcd.get_run_count(code_number, mps_count, lps_index);
uint8 bit;
if(mps_count) {
bit = 0;
mps_count--;
} else {
bit = 1;
lps_index = 0;
}
end_of_run = !(mps_count || lps_index);
return bit;
}
//probability estimation module
const SDD1::Decomp::PEM::State SDD1::Decomp::PEM::evolution_table[33] = {
{ 0, 25, 25 },
{ 0, 2, 1 },
{ 0, 3, 1 },
{ 0, 4, 2 },
{ 0, 5, 3 },
{ 1, 6, 4 },
{ 1, 7, 5 },
{ 1, 8, 6 },
{ 1, 9, 7 },
{ 2, 10, 8 },
{ 2, 11, 9 },
{ 2, 12, 10 },
{ 2, 13, 11 },
{ 3, 14, 12 },
{ 3, 15, 13 },
{ 3, 16, 14 },
{ 3, 17, 15 },
{ 4, 18, 16 },
{ 4, 19, 17 },
{ 5, 20, 18 },
{ 5, 21, 19 },
{ 6, 22, 20 },
{ 6, 23, 21 },
{ 7, 24, 22 },
{ 7, 24, 23 },
{ 0, 26, 1 },
{ 1, 27, 2 },
{ 2, 28, 4 },
{ 3, 29, 8 },
{ 4, 30, 12 },
{ 5, 31, 16 },
{ 6, 32, 18 },
{ 7, 24, 22 },
};
void SDD1::Decomp::PEM::init() {
for(unsigned i = 0; i < 32; i++) {
context_info[i].status = 0;
context_info[i].mps = 0;
}
}
uint8 SDD1::Decomp::PEM::get_bit(uint8 context) {
ContextInfo &info = context_info[context];
uint8 current_status = info.status;
uint8 current_mps = info.mps;
const State &s = SDD1::Decomp::PEM::evolution_table[current_status];
uint8 bit;
bool end_of_run;
switch(s.code_number) {
case 0: bit = self.bg0.get_bit(end_of_run); break;
case 1: bit = self.bg1.get_bit(end_of_run); break;
case 2: bit = self.bg2.get_bit(end_of_run); break;
case 3: bit = self.bg3.get_bit(end_of_run); break;
case 4: bit = self.bg4.get_bit(end_of_run); break;
case 5: bit = self.bg5.get_bit(end_of_run); break;
case 6: bit = self.bg6.get_bit(end_of_run); break;
case 7: bit = self.bg7.get_bit(end_of_run); break;
}
if(end_of_run) {
if(bit) {
if(!(current_status & 0xfe)) info.mps ^= 0x01;
info.status = s.next_if_lps;
} else {
info.status = s.next_if_mps;
}
}
return bit ^ current_mps;
}
//context model
void SDD1::Decomp::CM::init(unsigned offset) {
bitplanes_info = self.rom_read(offset) & 0xc0;
context_bits_info = self.rom_read(offset) & 0x30;
bit_number = 0;
for(unsigned i = 0; i < 8; i++) previous_bitplane_bits[i] = 0;
switch(bitplanes_info) {
case 0x00: current_bitplane = 1; break;
case 0x40: current_bitplane = 7; break;
case 0x80: current_bitplane = 3; break;
}
}
uint8 SDD1::Decomp::CM::get_bit() {
switch(bitplanes_info) {
case 0x00:
current_bitplane ^= 0x01;
break;
case 0x40:
current_bitplane ^= 0x01;
if(!(bit_number & 0x7f)) current_bitplane = ((current_bitplane + 2) & 0x07);
break;
case 0x80:
current_bitplane ^= 0x01;
if(!(bit_number & 0x7f)) current_bitplane ^= 0x02;
break;
case 0xc0:
current_bitplane = bit_number & 0x07;
break;
}
uint16 &context_bits = previous_bitplane_bits[current_bitplane];
uint8 current_context = (current_bitplane & 0x01) << 4;
switch(context_bits_info) {
case 0x00: current_context |= ((context_bits & 0x01c0) >> 5) | (context_bits & 0x0001); break;
case 0x10: current_context |= ((context_bits & 0x0180) >> 5) | (context_bits & 0x0001); break;
case 0x20: current_context |= ((context_bits & 0x00c0) >> 5) | (context_bits & 0x0001); break;
case 0x30: current_context |= ((context_bits & 0x0180) >> 5) | (context_bits & 0x0003); break;
}
uint8 bit = self.pem.get_bit(current_context);
context_bits <<= 1;
context_bits |= bit;
bit_number++;
return bit;
}
//output logic
void SDD1::Decomp::OL::init(unsigned offset) {
bitplanes_info = self.rom_read(offset) & 0xc0;
r0 = 0x01;
}
uint8 SDD1::Decomp::OL::decompress() {
switch(bitplanes_info) {
case 0x00: case 0x40: case 0x80:
if(r0 == 0) {
r0 = ~r0;
return r2;
}
for(r0 = 0x80, r1 = 0, r2 = 0; r0; r0 >>= 1) {
if(self.cm.get_bit()) r1 |= r0;
if(self.cm.get_bit()) r2 |= r0;
}
return r1;
case 0xc0:
for(r0 = 0x01, r1 = 0; r0; r0 <<= 1) {
if(self.cm.get_bit()) r1 |= r0;
}
return r1;
}
}
//core
void SDD1::Decomp::init(unsigned offset) {
im.init(offset);
bg0.init();
bg1.init();
bg2.init();
bg3.init();
bg4.init();
bg5.init();
bg6.init();
bg7.init();
pem.init();
cm.init(offset);
ol.init(offset);
}
uint8 SDD1::Decomp::read() {
return ol.decompress();
}
uint8 SDD1::Decomp::rom_read(unsigned offset) {
return sdd1.rom_read(offset);
}
SDD1::Decomp::Decomp() : im(*this), gcd(*this),
bg0(*this, 0), bg1(*this, 1), bg2(*this, 2), bg3(*this, 3),
bg4(*this, 4), bg5(*this, 5), bg6(*this, 6), bg7(*this, 7),
pem(*this), cm(*this), ol(*this) {
}

82
bsnes/snes/chip/sdd1/decomp.hpp Executable file
View File

@ -0,0 +1,82 @@
struct Decomp {
struct IM { //input manager
Decomp &self;
void init(unsigned offset);
uint8 get_codeword(uint8 code_length);
IM(SDD1::Decomp &self) : self(self) {}
private:
unsigned offset;
unsigned bit_count;
};
struct GCD { //golomb-code decoder
Decomp &self;
static const uint8 run_count[256];
void get_run_count(uint8 code_number, uint8 &mps_count, bool &lps_index);
GCD(SDD1::Decomp &self) : self(self) {}
};
struct BG { //bits generator
Decomp &self;
void init();
uint8 get_bit(bool &end_of_run);
BG(SDD1::Decomp &self, uint8 code_number) : self(self), code_number(code_number) {}
private:
const uint8 code_number;
uint8 mps_count;
bool lps_index;
};
struct PEM { //probability estimation module
Decomp &self;
void init();
uint8 get_bit(uint8 context);
PEM(SDD1::Decomp &self) : self(self) {}
private:
struct State {
uint8 code_number;
uint8 next_if_mps;
uint8 next_if_lps;
};
static const State evolution_table[33];
struct ContextInfo {
uint8 status;
uint8 mps;
} context_info[32];
};
struct CM { //context model
Decomp &self;
void init(unsigned offset);
uint8 get_bit();
CM(SDD1::Decomp &self) : self(self) {}
private:
uint8 bitplanes_info;
uint8 context_bits_info;
uint8 bit_number;
uint8 current_bitplane;
uint16 previous_bitplane_bits[8];
};
struct OL { //output logic
Decomp &self;
void init(unsigned offset);
uint8 decompress();
OL(SDD1::Decomp &self) : self(self) {}
private:
uint8 bitplanes_info;
uint8 r0, r1, r2;
};
void init(unsigned offset);
uint8 read();
uint8 rom_read(unsigned offset);
Decomp();
IM im;
GCD gcd;
BG bg0, bg1, bg2, bg3, bg4, bg5, bg6, bg7;
PEM pem;
CM cm;
OL ol;
};

View File

@ -5,8 +5,8 @@ namespace SNES {
SDD1 sdd1;
#include "decomp.cpp"
#include "serialization.cpp"
#include "sdd1emu.cpp"
void SDD1::init() {
}
@ -28,6 +28,7 @@ void SDD1::power() {
void SDD1::reset() {
sdd1_enable = 0x00;
xfer_enable = 0x00;
dma_ready = false;
mmc[0] = 0 << 20;
mmc[1] = 1 << 20;
@ -38,8 +39,6 @@ void SDD1::reset() {
dma[i].addr = 0;
dma[i].size = 0;
}
buffer.ready = false;
}
uint8 SDD1::mmio_read(unsigned addr) {
@ -86,6 +85,10 @@ void SDD1::mmio_write(unsigned addr, uint8 data) {
}
}
uint8 SDD1::rom_read(unsigned addr) {
return cartridge.rom.read(mmc[(addr >> 20) & 3] + (addr & 0x0fffff));
}
//SDD1::mcu_read() is mapped to $c0-ff:0000-ffff
//the design is meant to be as close to the hardware design as possible, thus this code
//avoids adding S-DD1 hooks inside S-CPU::DMA emulation.
@ -111,26 +114,16 @@ uint8 SDD1::mcu_read(unsigned addr) {
if(sdd1_enable & xfer_enable & (1 << i)) {
//S-DD1 always uses fixed transfer mode, so address will not change during transfer
if(addr == dma[i].addr) {
if(!buffer.ready) {
//first byte read for channel performs full decompression.
//this really should stream byte-by-byte, but it's not necessary since the size is known
buffer.offset = 0;
buffer.size = dma[i].size ? dma[i].size : 65536;
//sdd1emu calls this function; it needs to access uncompressed data;
//so temporarily disable decompression mode for decompress() call.
uint8 temp = sdd1_enable;
sdd1_enable = false;
sdd1emu.decompress(addr, buffer.size, buffer.data);
sdd1_enable = temp;
buffer.ready = true;
if(!dma_ready) {
//prepare streaming decompression
decomp.init(addr);
dma_ready = true;
}
//fetch a decompressed byte; once buffer is depleted, disable channel and invalidate buffer
uint8 data = buffer.data[(uint16)buffer.offset++];
if(buffer.offset >= buffer.size) {
buffer.ready = false;
//fetch a decompressed byte; once finished, disable channel and invalidate buffer
uint8 data = decomp.read();
if(--dma[i].size == 0) {
dma_ready = false;
xfer_enable &= ~(1 << i);
}

View File

@ -1,5 +1,3 @@
#include "sdd1emu.hpp"
class SDD1 {
public:
void init();
@ -11,6 +9,7 @@ public:
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
uint8 rom_read(unsigned addr);
uint8 mcu_read(unsigned addr);
void mcu_write(unsigned addr, uint8 data);
@ -19,22 +18,19 @@ public:
~SDD1();
private:
uint8 sdd1_enable; //channel bit-mask
uint8 xfer_enable; //channel bit-mask
unsigned mmc[4]; //memory map controller ROM indices
uint8 sdd1_enable; //channel bit-mask
uint8 xfer_enable; //channel bit-mask
bool dma_ready; //used to initialize decompression module
unsigned mmc[4]; //memory map controller ROM indices
struct {
unsigned addr; //$43x2-$43x4 -- DMA transfer address
uint16 size; //$43x5-$43x6 -- DMA transfer size
unsigned addr; //$43x2-$43x4 -- DMA transfer address
uint16 size; //$43x5-$43x6 -- DMA transfer size
} dma[8];
SDD1emu sdd1emu;
struct {
uint8 data[65536]; //pointer to decompressed S-DD1 data
uint16 offset; //read index into S-DD1 decompression buffer
unsigned size; //length of data buffer; reads decrement counter, set ready to false at 0
bool ready; //true when data[] is valid; false to invoke sdd1emu.decompress()
} buffer;
public:
#include "decomp.hpp"
Decomp decomp;
};
extern SDD1 sdd1;

View File

@ -1,452 +0,0 @@
#ifdef SDD1_CPP
/************************************************************************
S-DD1'algorithm emulation code
------------------------------
Author: Andreas Naive
Date: August 2003
Last update: October 2004
This code is Public Domain. There is no copyright holded by the author.
Said this, the author wish to explicitly emphasize his inalienable moral rights
over this piece of intelectual work and the previous research that made it
possible, as recognized by most of the copyright laws around the world.
This code is provided 'as-is', with no warranty, expressed or implied.
No responsability is assumed by the author in connection with it.
The author is greatly indebted with The Dumper, without whose help and
patience providing him with real S-DD1 data the research would have never been
possible. He also wish to note that in the very beggining of his research,
Neviksti had done some steps in the right direction. By last, the author is
indirectly indebted to all the people that worked and contributed in the
S-DD1 issue in the past.
An algorithm's documentation is available as a separate document.
The implementation is obvious when the algorithm is
understood.
************************************************************************/
typedef uint8 bool8;
#define SDD1_read(__addr) (sdd1.mcu_read(__addr))
////////////////////////////////////////////////////
void SDD1_IM::prepareDecomp(uint32 in_buf) {
byte_ptr=in_buf;
bit_count=4;
}
////////////////////////////////////////////////////
uint8 SDD1_IM::getCodeword(uint8 code_len) {
uint8 codeword;
uint8 comp_count;
codeword = (SDD1_read(byte_ptr))<<bit_count;
++bit_count;
if (codeword & 0x80) {
codeword |= SDD1_read(byte_ptr+1)>>(9-bit_count);
bit_count+=code_len;
}
if (bit_count & 0x08) {
byte_ptr++;
bit_count&=0x07;
}
return codeword;
}
//////////////////////////////////////////////////////
SDD1_GCD::SDD1_GCD(SDD1_IM *associatedIM) :
IM(associatedIM)
{
}
//////////////////////////////////////////////////////
void SDD1_GCD::getRunCount(uint8 code_num, uint8 *MPScount, bool8 *LPSind) {
const uint8 run_count[] = {
0x00, 0x00, 0x01, 0x00, 0x03, 0x01, 0x02, 0x00,
0x07, 0x03, 0x05, 0x01, 0x06, 0x02, 0x04, 0x00,
0x0f, 0x07, 0x0b, 0x03, 0x0d, 0x05, 0x09, 0x01,
0x0e, 0x06, 0x0a, 0x02, 0x0c, 0x04, 0x08, 0x00,
0x1f, 0x0f, 0x17, 0x07, 0x1b, 0x0b, 0x13, 0x03,
0x1d, 0x0d, 0x15, 0x05, 0x19, 0x09, 0x11, 0x01,
0x1e, 0x0e, 0x16, 0x06, 0x1a, 0x0a, 0x12, 0x02,
0x1c, 0x0c, 0x14, 0x04, 0x18, 0x08, 0x10, 0x00,
0x3f, 0x1f, 0x2f, 0x0f, 0x37, 0x17, 0x27, 0x07,
0x3b, 0x1b, 0x2b, 0x0b, 0x33, 0x13, 0x23, 0x03,
0x3d, 0x1d, 0x2d, 0x0d, 0x35, 0x15, 0x25, 0x05,
0x39, 0x19, 0x29, 0x09, 0x31, 0x11, 0x21, 0x01,
0x3e, 0x1e, 0x2e, 0x0e, 0x36, 0x16, 0x26, 0x06,
0x3a, 0x1a, 0x2a, 0x0a, 0x32, 0x12, 0x22, 0x02,
0x3c, 0x1c, 0x2c, 0x0c, 0x34, 0x14, 0x24, 0x04,
0x38, 0x18, 0x28, 0x08, 0x30, 0x10, 0x20, 0x00,
0x7f, 0x3f, 0x5f, 0x1f, 0x6f, 0x2f, 0x4f, 0x0f,
0x77, 0x37, 0x57, 0x17, 0x67, 0x27, 0x47, 0x07,
0x7b, 0x3b, 0x5b, 0x1b, 0x6b, 0x2b, 0x4b, 0x0b,
0x73, 0x33, 0x53, 0x13, 0x63, 0x23, 0x43, 0x03,
0x7d, 0x3d, 0x5d, 0x1d, 0x6d, 0x2d, 0x4d, 0x0d,
0x75, 0x35, 0x55, 0x15, 0x65, 0x25, 0x45, 0x05,
0x79, 0x39, 0x59, 0x19, 0x69, 0x29, 0x49, 0x09,
0x71, 0x31, 0x51, 0x11, 0x61, 0x21, 0x41, 0x01,
0x7e, 0x3e, 0x5e, 0x1e, 0x6e, 0x2e, 0x4e, 0x0e,
0x76, 0x36, 0x56, 0x16, 0x66, 0x26, 0x46, 0x06,
0x7a, 0x3a, 0x5a, 0x1a, 0x6a, 0x2a, 0x4a, 0x0a,
0x72, 0x32, 0x52, 0x12, 0x62, 0x22, 0x42, 0x02,
0x7c, 0x3c, 0x5c, 0x1c, 0x6c, 0x2c, 0x4c, 0x0c,
0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04,
0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08,
0x70, 0x30, 0x50, 0x10, 0x60, 0x20, 0x40, 0x00,
};
uint8 codeword=IM->getCodeword(code_num);
if (codeword & 0x80) {
*LPSind=1;
*MPScount=run_count[codeword>>(code_num^0x07)];
}
else {
*MPScount=(1<<code_num);
}
}
///////////////////////////////////////////////////////
SDD1_BG::SDD1_BG(SDD1_GCD *associatedGCD, uint8 code) :
GCD(associatedGCD), code_num(code)
{
}
///////////////////////////////////////////////
void SDD1_BG::prepareDecomp(void) {
MPScount=0;
LPSind=0;
}
//////////////////////////////////////////////
uint8 SDD1_BG::getBit(bool8 *endOfRun) {
uint8 bit;
if (!(MPScount || LPSind)) GCD->getRunCount(code_num, &MPScount, &LPSind);
if (MPScount) {
bit=0;
MPScount--;
}
else {
bit=1;
LPSind=0;
}
if (MPScount || LPSind) (*endOfRun)=0;
else (*endOfRun)=1;
return bit;
}
/////////////////////////////////////////////////
SDD1_PEM::SDD1_PEM(SDD1_BG *associatedBG0, SDD1_BG *associatedBG1,
SDD1_BG *associatedBG2, SDD1_BG *associatedBG3,
SDD1_BG *associatedBG4, SDD1_BG *associatedBG5,
SDD1_BG *associatedBG6, SDD1_BG *associatedBG7) {
BG[0]=associatedBG0;
BG[1]=associatedBG1;
BG[2]=associatedBG2;
BG[3]=associatedBG3;
BG[4]=associatedBG4;
BG[5]=associatedBG5;
BG[6]=associatedBG6;
BG[7]=associatedBG7;
}
/////////////////////////////////////////////////////////
const SDD1_PEM::state SDD1_PEM::evolution_table[]={
{ 0,25,25},
{ 0, 2, 1},
{ 0, 3, 1},
{ 0, 4, 2},
{ 0, 5, 3},
{ 1, 6, 4},
{ 1, 7, 5},
{ 1, 8, 6},
{ 1, 9, 7},
{ 2,10, 8},
{ 2,11, 9},
{ 2,12,10},
{ 2,13,11},
{ 3,14,12},
{ 3,15,13},
{ 3,16,14},
{ 3,17,15},
{ 4,18,16},
{ 4,19,17},
{ 5,20,18},
{ 5,21,19},
{ 6,22,20},
{ 6,23,21},
{ 7,24,22},
{ 7,24,23},
{ 0,26, 1},
{ 1,27, 2},
{ 2,28, 4},
{ 3,29, 8},
{ 4,30,12},
{ 5,31,16},
{ 6,32,18},
{ 7,24,22}
};
//////////////////////////////////////////////////////
void SDD1_PEM::prepareDecomp(void) {
for (uint8 i=0; i<32; i++) {
contextInfo[i].status=0;
contextInfo[i].MPS=0;
}
}
/////////////////////////////////////////////////////////
uint8 SDD1_PEM::getBit(uint8 context) {
bool8 endOfRun;
uint8 bit;
SDD1_ContextInfo *pContInfo=&contextInfo[context];
uint8 currStatus = pContInfo->status;
const state *pState=&SDD1_PEM::evolution_table[currStatus];
uint8 currentMPS=pContInfo->MPS;
bit=(BG[pState->code_num])->getBit(&endOfRun);
if (endOfRun)
if (bit) {
if (!(currStatus & 0xfe)) (pContInfo->MPS)^=0x01;
(pContInfo->status)=pState->nextIfLPS;
}
else
(pContInfo->status)=pState->nextIfMPS;
return bit^currentMPS;
}
//////////////////////////////////////////////////////////////
SDD1_CM::SDD1_CM(SDD1_PEM *associatedPEM) :
PEM(associatedPEM)
{
}
//////////////////////////////////////////////////////////////
void SDD1_CM::prepareDecomp(uint32 first_byte) {
bitplanesInfo = SDD1_read(first_byte) & 0xc0;
contextBitsInfo = SDD1_read(first_byte) & 0x30;
bit_number=0;
for (int i=0; i<8; i++) prevBitplaneBits[i]=0;
switch (bitplanesInfo) {
case 0x00:
currBitplane = 1;
break;
case 0x40:
currBitplane = 7;
break;
case 0x80:
currBitplane = 3;
}
}
/////////////////////////////////////////////////////////////
uint8 SDD1_CM::getBit(void) {
uint8 currContext;
uint16 *context_bits;
switch (bitplanesInfo) {
case 0x00:
currBitplane ^= 0x01;
break;
case 0x40:
currBitplane ^= 0x01;
if (!(bit_number & 0x7f)) currBitplane = ((currBitplane+2) & 0x07);
break;
case 0x80:
currBitplane ^= 0x01;
if (!(bit_number & 0x7f)) currBitplane ^= 0x02;
break;
case 0xc0:
currBitplane = bit_number & 0x07;
}
context_bits = &prevBitplaneBits[currBitplane];
currContext=(currBitplane & 0x01)<<4;
switch (contextBitsInfo) {
case 0x00:
currContext|=((*context_bits & 0x01c0)>>5)|(*context_bits & 0x0001);
break;
case 0x10:
currContext|=((*context_bits & 0x0180)>>5)|(*context_bits & 0x0001);
break;
case 0x20:
currContext|=((*context_bits & 0x00c0)>>5)|(*context_bits & 0x0001);
break;
case 0x30:
currContext|=((*context_bits & 0x0180)>>5)|(*context_bits & 0x0003);
}
uint8 bit=PEM->getBit(currContext);
*context_bits <<= 1;
*context_bits |= bit;
bit_number++;
return bit;
}
//////////////////////////////////////////////////
SDD1_OL::SDD1_OL(SDD1_CM *associatedCM) :
CM(associatedCM)
{
}
///////////////////////////////////////////////////
void SDD1_OL::prepareDecomp(uint32 first_byte, uint16 out_len, uint8 *out_buf) {
bitplanesInfo = SDD1_read(first_byte) & 0xc0;
length=out_len;
buffer=out_buf;
}
///////////////////////////////////////////////////
void SDD1_OL::launch(void) {
uint8 i;
uint8 register1, register2;
switch (bitplanesInfo) {
case 0x00:
case 0x40:
case 0x80:
i=1;
do { //if length==0, we output 2^16 bytes
if (!i) {
*(buffer++)=register2;
i=~i;
}
else {
for (register1=register2=0, i=0x80; i; i>>=1) {
if (CM->getBit()) register1 |= i;
if (CM->getBit()) register2 |= i;
}
*(buffer++)=register1;
}
} while (--length);
break;
case 0xc0:
do {
for (register1=0, i=0x01; i; i<<=1) {
if (CM->getBit()) register1 |= i;
}
*(buffer++)=register1;
} while (--length);
}
}
///////////////////////////////////////////////////////
void SDD1emu::decompress(uint32 in_buf, uint16 out_len, uint8 *out_buf) {
IM.prepareDecomp(in_buf);
BG0.prepareDecomp();
BG1.prepareDecomp();
BG2.prepareDecomp();
BG3.prepareDecomp();
BG4.prepareDecomp();
BG5.prepareDecomp();
BG6.prepareDecomp();
BG7.prepareDecomp();
PEM.prepareDecomp();
CM.prepareDecomp(in_buf);
OL.prepareDecomp(in_buf, out_len, out_buf);
OL.launch();
}
////////////////////////////////////////////////////////////
SDD1emu::SDD1emu() :
GCD(&IM),
BG0(&GCD, 0), BG1(&GCD, 1), BG2(&GCD, 2), BG3(&GCD, 3),
BG4(&GCD, 4), BG5(&GCD, 5), BG6(&GCD, 6), BG7(&GCD, 7),
PEM(&BG0, &BG1, &BG2, &BG3, &BG4, &BG5, &BG6, &BG7),
CM(&PEM),
OL(&CM)
{
}
///////////////////////////////////////////////////////////
#endif

View File

@ -1,164 +0,0 @@
/************************************************************************
S-DD1'algorithm emulation code
------------------------------
Author: Andreas Naive
Date: August 2003
Last update: October 2004
This code is Public Domain. There is no copyright holded by the author.
Said this, the author wish to explicitly emphasize his inalienable moral rights
over this piece of intelectual work and the previous research that made it
possible, as recognized by most of the copyright laws around the world.
This code is provided 'as-is', with no warranty, expressed or implied.
No responsability is assumed by the author in connection with it.
The author is greatly indebted with The Dumper, without whose help and
patience providing him with real S-DD1 data the research would have never been
possible. He also wish to note that in the very beggining of his research,
Neviksti had done some steps in the right direction. By last, the author is
indirectly indebted to all the people that worked and contributed in the
S-DD1 issue in the past.
An algorithm's documentation is available as a separate document.
The implementation is obvious when the algorithm is
understood.
************************************************************************/
#define bool8 uint8
class SDD1_IM { //Input Manager
public:
SDD1_IM(void) {}
void prepareDecomp(uint32 in_buf);
uint8 getCodeword(const uint8 code_len);
private:
uint32 byte_ptr;
uint8 bit_count;
};
////////////////////////////////////////////////////
class SDD1_GCD { //Golomb-Code Decoder
public:
SDD1_GCD(SDD1_IM *associatedIM);
void getRunCount(uint8 code_num, uint8 *MPScount, bool8 *LPSind);
private:
SDD1_IM *const IM;
};
//////////////////////////////////////////////////////
class SDD1_BG { // Bits Generator
public:
SDD1_BG(SDD1_GCD *associatedGCD, uint8 code);
void prepareDecomp(void);
uint8 getBit(bool8 *endOfRun);
private:
const uint8 code_num;
uint8 MPScount;
bool8 LPSind;
SDD1_GCD *const GCD;
};
////////////////////////////////////////////////
class SDD1_PEM { //Probability Estimation Module
public:
SDD1_PEM(SDD1_BG *associatedBG0, SDD1_BG *associatedBG1,
SDD1_BG *associatedBG2, SDD1_BG *associatedBG3,
SDD1_BG *associatedBG4, SDD1_BG *associatedBG5,
SDD1_BG *associatedBG6, SDD1_BG *associatedBG7);
void prepareDecomp(void);
uint8 getBit(uint8 context);
private:
struct state {
uint8 code_num;
uint8 nextIfMPS;
uint8 nextIfLPS;
};
static const state evolution_table[];
struct SDD1_ContextInfo {
uint8 status;
uint8 MPS;
} contextInfo[32];
SDD1_BG * BG[8];
};
///////////////////////////////////////////////////
class SDD1_CM { //Context Model
public:
SDD1_CM(SDD1_PEM *associatedPEM);
void prepareDecomp(uint32 first_byte);
uint8 getBit(void);
private:
uint8 bitplanesInfo;
uint8 contextBitsInfo;
uint8 bit_number;
uint8 currBitplane;
uint16 prevBitplaneBits[8];
SDD1_PEM *const PEM;
};
///////////////////////////////////////////////////
class SDD1_OL { //Output Logic
public:
SDD1_OL(SDD1_CM *associatedCM);
void prepareDecomp(uint32 first_byte, uint16 out_len, uint8 *out_buf);
void launch(void);
private:
uint8 bitplanesInfo;
uint16 length;
uint8 *buffer;
SDD1_CM *const CM;
};
/////////////////////////////////////////////////////////
class SDD1emu {
public:
SDD1emu(void);
void decompress(uint32 in_buf, uint16 out_len, uint8 *out_buf);
private:
SDD1_IM IM;
SDD1_GCD GCD;
SDD1_BG BG0; SDD1_BG BG1; SDD1_BG BG2; SDD1_BG BG3;
SDD1_BG BG4; SDD1_BG BG5; SDD1_BG BG6; SDD1_BG BG7;
SDD1_PEM PEM;
SDD1_CM CM;
SDD1_OL OL;
};
#undef bool8

View File

@ -3,17 +3,13 @@
void SDD1::serialize(serializer &s) {
s.integer(sdd1_enable);
s.integer(xfer_enable);
s.integer(dma_ready);
s.array(mmc);
for(unsigned n = 0; n < 8; n++) {
s.integer(dma[n].addr);
s.integer(dma[n].size);
}
s.array(buffer.data);
s.integer(buffer.offset);
s.integer(buffer.size);
s.integer(buffer.ready);
}
#endif

View File

@ -1,6 +1,6 @@
#ifdef SPC7110_CPP
uint8 SPC7110Decomp::read() {
uint8 SPC7110::Decomp::read() {
if(decomp_buffer_length == 0) {
//decompress at least (decomp_buffer_size / 2) bytes to the buffer
switch(decomp_mode) {
@ -17,19 +17,19 @@ uint8 SPC7110Decomp::read() {
return data;
}
void SPC7110Decomp::write(uint8 data) {
void SPC7110::Decomp::write(uint8 data) {
decomp_buffer[decomp_buffer_wroffset++] = data;
decomp_buffer_wroffset &= decomp_buffer_size - 1;
decomp_buffer_length++;
}
uint8 SPC7110Decomp::dataread() {
uint8 SPC7110::Decomp::dataread() {
unsigned size = cartridge.rom.size() - spc7110.data_rom_offset;
while(decomp_offset >= size) decomp_offset -= size;
return cartridge.rom.read(spc7110.data_rom_offset + decomp_offset++);
}
void SPC7110Decomp::init(unsigned mode, unsigned offset, unsigned index) {
void SPC7110::Decomp::init(unsigned mode, unsigned offset, unsigned index) {
decomp_mode = mode;
decomp_offset = offset;
@ -55,7 +55,7 @@ void SPC7110Decomp::init(unsigned mode, unsigned offset, unsigned index) {
//
void SPC7110Decomp::mode0(bool init) {
void SPC7110::Decomp::mode0(bool init) {
static uint8 val, in, span;
static int out, inverts, lps, in_count;
@ -122,7 +122,7 @@ void SPC7110Decomp::mode0(bool init) {
}
}
void SPC7110Decomp::mode1(bool init) {
void SPC7110::Decomp::mode1(bool init) {
static int pixelorder[4], realorder[4];
static uint8 in, val, span;
static int out, inverts, lps, in_count;
@ -219,13 +219,13 @@ void SPC7110Decomp::mode1(bool init) {
}
//turn pixel data into bitplanes
unsigned data = morton_2x8(out);
unsigned data = deinterleave_2x8(out);
write(data >> 8);
write(data >> 0);
}
}
void SPC7110Decomp::mode2(bool init) {
void SPC7110::Decomp::mode2(bool init) {
static int pixelorder[16], realorder[16];
static uint8 bitplanebuffer[16], buffer_index;
static uint8 in, val, span;
@ -327,7 +327,7 @@ void SPC7110Decomp::mode2(bool init) {
}
//convert pixel data into bitplanes
unsigned data = morton_4x8(out0);
unsigned data = deinterleave_4x8(out0);
write(data >> 24);
write(data >> 16);
bitplanebuffer[buffer_index++] = data >> 8;
@ -342,7 +342,7 @@ void SPC7110Decomp::mode2(bool init) {
//
const uint8 SPC7110Decomp::evolution_table[53][4] = {
const uint8 SPC7110::Decomp::evolution_table[53][4] = {
//{ prob, nextlps, nextmps, toggle invert },
{ 0x5a, 1, 1, 1 },
@ -404,7 +404,7 @@ const uint8 SPC7110Decomp::evolution_table[53][4] = {
{ 0x37, 51, 43, 0 },
};
const uint8 SPC7110Decomp::mode2_context_table[32][2] = {
const uint8 SPC7110::Decomp::mode2_context_table[32][2] = {
//{ next 0, next 1 },
{ 1, 2 },
@ -445,31 +445,38 @@ const uint8 SPC7110Decomp::mode2_context_table[32][2] = {
{ 31, 31 },
};
uint8 SPC7110Decomp::probability (unsigned n) { return evolution_table[context[n].index][0]; }
uint8 SPC7110Decomp::next_lps (unsigned n) { return evolution_table[context[n].index][1]; }
uint8 SPC7110Decomp::next_mps (unsigned n) { return evolution_table[context[n].index][2]; }
bool SPC7110Decomp::toggle_invert(unsigned n) { return evolution_table[context[n].index][3]; }
uint8 SPC7110::Decomp::probability (unsigned n) { return evolution_table[context[n].index][0]; }
uint8 SPC7110::Decomp::next_lps (unsigned n) { return evolution_table[context[n].index][1]; }
uint8 SPC7110::Decomp::next_mps (unsigned n) { return evolution_table[context[n].index][2]; }
bool SPC7110::Decomp::toggle_invert(unsigned n) { return evolution_table[context[n].index][3]; }
unsigned SPC7110Decomp::morton_2x8(unsigned data) {
unsigned SPC7110::Decomp::deinterleave_2x8(unsigned data) {
//reverse morton lookup: de-interleave two 8-bit values
//15, 13, 11, 9, 7, 5, 3, 1 -> 15- 8
//14, 12, 10, 8, 6, 4, 2, 0 -> 7- 0
return morton16[0][(data >> 0) & 255] + morton16[1][(data >> 8) & 255];
unsigned result = 0;
for(unsigned mask = 1u << 15; mask; mask >>= 2) result = (result << 1) | (bool)(data & mask);
for(unsigned mask = 1u << 14; mask; mask >>= 2) result = (result << 1) | (bool)(data & mask);
return result;
}
unsigned SPC7110Decomp::morton_4x8(unsigned data) {
unsigned SPC7110::Decomp::deinterleave_4x8(unsigned data) {
//reverse morton lookup: de-interleave four 8-bit values
//31, 27, 23, 19, 15, 11, 7, 3 -> 31-24
//30, 26, 22, 18, 14, 10, 6, 2 -> 23-16
//29, 25, 21, 17, 13, 9, 5, 1 -> 15- 8
//28, 24, 20, 16, 12, 8, 4, 0 -> 7- 0
return morton32[0][(data >> 0) & 255] + morton32[1][(data >> 8) & 255]
+ morton32[2][(data >> 16) & 255] + morton32[3][(data >> 24) & 255];
unsigned result = 0;
for(unsigned mask = 1u << 31; mask; mask >>= 4) result = (result << 1) | (bool)(data & mask);
for(unsigned mask = 1u << 30; mask; mask >>= 4) result = (result << 1) | (bool)(data & mask);
for(unsigned mask = 1u << 29; mask; mask >>= 4) result = (result << 1) | (bool)(data & mask);
for(unsigned mask = 1u << 28; mask; mask >>= 4) result = (result << 1) | (bool)(data & mask);
return result;
}
//
void SPC7110Decomp::reset() {
void SPC7110::Decomp::reset() {
//mode 3 is invalid; this is treated as a special case to always return 0x00
//set to mode 3 so that reading decomp port before starting first decomp will return 0x00
decomp_mode = 3;
@ -479,32 +486,12 @@ void SPC7110Decomp::reset() {
decomp_buffer_length = 0;
}
SPC7110Decomp::SPC7110Decomp() {
SPC7110::Decomp::Decomp() {
decomp_buffer = new uint8_t[decomp_buffer_size];
reset();
//initialize reverse morton lookup tables
for(unsigned i = 0; i < 256; i++) {
#define map(x, y) (((i >> x) & 1) << y)
//2x8-bit
morton16[1][i] = map(7, 15) + map(6, 7) + map(5, 14) + map(4, 6)
+ map(3, 13) + map(2, 5) + map(1, 12) + map(0, 4);
morton16[0][i] = map(7, 11) + map(6, 3) + map(5, 10) + map(4, 2)
+ map(3, 9) + map(2, 1) + map(1, 8) + map(0, 0);
//4x8-bit
morton32[3][i] = map(7, 31) + map(6, 23) + map(5, 15) + map(4, 7)
+ map(3, 30) + map(2, 22) + map(1, 14) + map(0, 6);
morton32[2][i] = map(7, 29) + map(6, 21) + map(5, 13) + map(4, 5)
+ map(3, 28) + map(2, 20) + map(1, 12) + map(0, 4);
morton32[1][i] = map(7, 27) + map(6, 19) + map(5, 11) + map(4, 3)
+ map(3, 26) + map(2, 18) + map(1, 10) + map(0, 2);
morton32[0][i] = map(7, 25) + map(6, 17) + map(5, 9) + map(4, 1)
+ map(3, 24) + map(2, 16) + map(1, 8) + map(0, 0);
#undef map
}
}
SPC7110Decomp::~SPC7110Decomp() {
SPC7110::Decomp::~Decomp() {
delete[] decomp_buffer;
}

View File

@ -1,12 +1,12 @@
class SPC7110Decomp {
class Decomp {
public:
uint8 read();
void init(unsigned mode, unsigned offset, unsigned index);
void reset();
void serialize(serializer&);
SPC7110Decomp();
~SPC7110Decomp();
Decomp();
~Decomp();
private:
unsigned decomp_mode;
@ -39,8 +39,6 @@ private:
uint8 next_mps(unsigned n);
bool toggle_invert(unsigned n);
unsigned morton16[2][256];
unsigned morton32[4][256];
unsigned morton_2x8(unsigned data);
unsigned morton_4x8(unsigned data);
unsigned deinterleave_2x8(unsigned data);
unsigned deinterleave_4x8(unsigned data);
};

View File

@ -1,6 +1,6 @@
#ifdef SPC7110_CPP
void SPC7110Decomp::serialize(serializer &s) {
void SPC7110::Decomp::serialize(serializer &s) {
s.integer(decomp_mode);
s.integer(decomp_offset);

View File

@ -1,21 +1,5 @@
/*****
* SPC7110 emulator - version 0.04 (2010-02-14)
* Copyright (c) 2008-2010, byuu and neviksti
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* The software is provided "as is" and the author disclaims all warranties
* with regard to this software including all implied warranties of
* merchantibility and fitness, in no event shall the author be liable for
* any special, direct, indirect, or consequential damages or any damages
* whatsoever resulting from loss of use, data or profits, whether in an
* action of contract, negligence or other tortious action, arising out of
* or in connection with the use or performance of this software.
*****/
#include "decomp.hpp"
//SPC7110 emulator - version 0.05 (2011-06-27)
//Copyright (c) 2008-2011, byuu and neviksti
class SPC7110 {
public:
@ -75,7 +59,8 @@ private:
uint8 r480b; //decompression control register
uint8 r480c; //decompression status
SPC7110Decomp decomp;
#include "decomp.hpp"
Decomp decomp;
//==============
//data port unit

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "080";
static const char Version[] = "080.01";
static const unsigned SerializerVersion = 21;
}
}

View File

@ -17,7 +17,8 @@ struct Interface : public SNES::Interface {
unsigned height = overscan ? 239 : 224;
if(interlace) height <<= 1;
data += 9 * 1024; //skip front porch
if(pvideo_refresh) return pvideo_refresh(data, width, height);
if(pvideo_refresh) pvideo_refresh(data, width, height);
if(pinput_poll) pinput_poll();
}
void audio_sample(uint16_t left, uint16_t right) {