mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-04-29 23:18:53 +02:00
byuu says: Changelog: - MD/PSG: fixed 68K bus Z80 status read address location - MS, GG, MD/PSG: channels post-decrement their counters, not pre-decrement [Cydrak]¹ - MD/VDP: cache screen width registers once per scanline; screen height registers once per frame - MD/VDP: support 256-width display mode (used in Shining Force, etc) - MD/YM2612: implemented timers² - MD/YM2612: implemented 8-bit PCM DAC² - 68000: TRAP instruction should index the vector location by 32 (eg by 128 bytes), fixes Shining Force - nall: updated hex(), octal(), binary() functions to take uintmax instead of template<typename T> parameter³ ¹: this one makes an incredible difference. Sie noticed that lots of games set a period of 0, which would end up being a really long period with pre-decrement. By fixing this, noise shows up in many more games, and sounds way better in games even where it did before. You can hear extra sound on Lunar - Sanposuru Gakuen's title screen, the noise in Sonic The Hedgehog (Mega Drive) sounds better, etc. ²: this also really helps sound. The timers allow PSG music to play back at the correct speed instead of playing back way too quickly. And the PCM DAC lets you hear a lot of drum effects, as well as the "Sega!!" sound at the start of Sonic the Hedgehog, and the infamous, "Rise from your grave!" line from Altered Beast. Still, most music on the Mega Drive comes from the FM channels, so there's still not a whole lot to listen to. I didn't implement Cydrak's $02c test register just yet. Sie wasn't 100% certain on how the extended DAC bit worked, so I'd like to play it a little conservative and get sound working, then I'll go back and add a toggle or something to enable undocumented registers, that way we can use that to detect any potential problems they might be causing. ³: unfortunately we lose support for using hex() on nall/arithmetic types. If I have a const Pair& version of the function, then the compiler gets confused on whether Natural<32> should use uintmax or const Pair&, because compilers are stupid, and you can't have explicit arguments in overloaded functions. So even though either function would work, it just decides to error out instead >_> This is actually really annoying, because I want hex() to be useful for printing out nall/crypto keys and hashes directly. But ... this change had to be made. Negative signed integers would crash programs, and that was taking out my 68000 disassembler.
147 lines
3.8 KiB
C++
147 lines
3.8 KiB
C++
#pragma once
|
|
|
|
namespace nall {
|
|
|
|
//nall::format is a vector<string> of parameters that can be applied to a string
|
|
//each {#} token will be replaced with its appropriate format parameter
|
|
|
|
auto string::format(const nall::string_format& params) -> type& {
|
|
auto size = (int)this->size();
|
|
auto data = (char*)memory::allocate(size);
|
|
memory::copy(data, this->data(), size);
|
|
|
|
int x = 0;
|
|
while(x < size - 2) { //2 = minimum tag length
|
|
if(data[x] != '{') { x++; continue; }
|
|
|
|
int y = x + 1;
|
|
while(y < size - 1) { //-1 avoids going out of bounds on test after this loop
|
|
if(data[y] != '}') { y++; continue; }
|
|
break;
|
|
}
|
|
|
|
if(data[y++] != '}') { x++; continue; }
|
|
|
|
static auto isNumeric = [](char* s, char* e) -> bool {
|
|
if(s == e) return false; //ignore empty tags: {}
|
|
while(s < e) {
|
|
if(*s >= '0' && *s <= '9') { s++; continue; }
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
if(!isNumeric(&data[x + 1], &data[y - 1])) { x++; continue; }
|
|
|
|
uint index = toNatural(&data[x + 1]);
|
|
if(index >= params.size()) { x++; continue; }
|
|
|
|
uint sourceSize = y - x;
|
|
uint targetSize = params[index].size();
|
|
uint remaining = size - x;
|
|
|
|
if(sourceSize > targetSize) {
|
|
uint difference = sourceSize - targetSize;
|
|
memory::move(&data[x], &data[x + difference], remaining);
|
|
size -= difference;
|
|
} else if(targetSize > sourceSize) {
|
|
uint difference = targetSize - sourceSize;
|
|
data = (char*)realloc(data, size + difference);
|
|
size += difference;
|
|
memory::move(&data[x + difference], &data[x], remaining);
|
|
}
|
|
memory::copy(&data[x], params[index].data(), targetSize);
|
|
x += targetSize;
|
|
}
|
|
|
|
resize(size);
|
|
memory::copy(get(), data, size);
|
|
memory::free(data);
|
|
return *this;
|
|
}
|
|
|
|
template<typename T, typename... P> auto string_format::append(const T& value, P&&... p) -> string_format& {
|
|
vector<string>::append(value);
|
|
return append(forward<P>(p)...);
|
|
}
|
|
|
|
auto string_format::append() -> string_format& {
|
|
return *this;
|
|
}
|
|
|
|
template<typename... P> auto print(P&&... p) -> void {
|
|
string s{forward<P>(p)...};
|
|
fwrite(s.data(), 1, s.size(), stdout);
|
|
}
|
|
|
|
template<typename... P> auto print(FILE* fp, P&&... p) -> void {
|
|
string s{forward<P>(p)...};
|
|
fwrite(s.data(), 1, s.size(), fp);
|
|
}
|
|
|
|
template<typename T> auto pad(const T& value, long precision, char padchar) -> string {
|
|
string buffer{value};
|
|
if(precision) buffer.size(precision, padchar);
|
|
return buffer;
|
|
}
|
|
|
|
auto hex(uintmax value, long precision, char padchar) -> string {
|
|
string buffer;
|
|
buffer.resize(sizeof(uintmax) * 2);
|
|
char* p = buffer.get();
|
|
|
|
uint size = 0;
|
|
do {
|
|
uint n = value & 15;
|
|
p[size++] = n < 10 ? '0' + n : 'a' + n - 10;
|
|
value >>= 4;
|
|
} while(value);
|
|
buffer.resize(size);
|
|
buffer.reverse();
|
|
if(precision) buffer.size(precision, padchar);
|
|
return buffer;
|
|
}
|
|
|
|
auto octal(uintmax value, long precision, char padchar) -> string {
|
|
string buffer;
|
|
buffer.resize(sizeof(uintmax) * 3);
|
|
char* p = buffer.get();
|
|
|
|
uint size = 0;
|
|
do {
|
|
p[size++] = '0' + (value & 7);
|
|
value >>= 3;
|
|
} while(value);
|
|
buffer.resize(size);
|
|
buffer.reverse();
|
|
if(precision) buffer.size(precision, padchar);
|
|
return buffer;
|
|
}
|
|
|
|
auto binary(uintmax value, long precision, char padchar) -> string {
|
|
string buffer;
|
|
buffer.resize(sizeof(uintmax) * 8);
|
|
char* p = buffer.get();
|
|
|
|
uint size = 0;
|
|
do {
|
|
p[size++] = '0' + (value & 1);
|
|
value >>= 1;
|
|
} while(value);
|
|
buffer.resize(size);
|
|
buffer.reverse();
|
|
if(precision) buffer.size(precision, padchar);
|
|
return buffer;
|
|
}
|
|
|
|
auto pointer(uintptr value, long precision) -> string {
|
|
if(value == 0) return "(nullptr)";
|
|
return {"0x", hex(value, precision)};
|
|
}
|
|
|
|
template<typename T> auto pointer(const T* value, long precision) -> string {
|
|
if(value == nullptr) return "(nullptr)";
|
|
return {"0x", hex((uintptr)value, precision)};
|
|
}
|
|
|
|
}
|