mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-10-04 23:31:44 +02:00
This version adds speed regulation, greatly improves PPU rendering, and increases speed by ~30% over the previous version. Changelog: - Rewrote offset-per-tile mode emulation, should be correct now. Fixes Chrono Trigger, Contra III, Tetris Attack, etc. - Fixed a bug with HDMA occuring during interrupts. Fixes Tales of Phantasia souond test screen - Updated compiler to Visual Studio 2005, and enabled profile guided optimizations - Added conditional compilation of debugging functions (faster without them) - Added conditional compilation of core classes as pointers (allowing polymorphism) or objects (allowing inlining). The latter results in a speed increase - Small fixes to BG and OAM rendering routines - Corrected sprite tile bounds wrapping - Corrected sprite rendering in hires video modes - Rewrote color add/sub routines, should be correct now. Fixes Illusion of Gaia menu, etc. - Optimized video blitting routines, will temporarilly break mixed video mode screenshots - Prevented selecting menu options via return key from being recognized as keypresses by the emulator - Added system speed regulation (60hz/NTSC or 50hz/PAL)! Many thanks to kode54, GIGO, and Richard Bannister for their assistance I disabled the debugger and polymorphism, and enabled profile guided optimizations for this build, to maximize speed. The debugger and polymorphism can be re-enabled via uncommenting the respective #defines in src/base.h and recompiling, or bsnes v0.013 can be used. I may start releasing two separate builds in the future... not sure yet.
187 lines
6.0 KiB
C++
187 lines
6.0 KiB
C++
/*
|
|
S-RTC chip emulation
|
|
Used by Hudson Soft in Dai Kaijuu Monogatari II and Far East of Eden Zero.
|
|
Currently, only the former is supported by bsnes.
|
|
|
|
Original S-RTC emulation code via John Weidman/SNES9x
|
|
Rewritten for compatibility with bsnes via byuu
|
|
|
|
The S-RTC is a real-time clock chip that was added to the above two carts
|
|
to allow the games to maintain the current time, even when the game was not
|
|
powered on. Thus allowing special events at certain times, and on certain
|
|
dates. Hudson Soft called this the PLG (Player's Life Gameplay System).
|
|
|
|
This chip is a special case to the term 'emulation' itself.
|
|
There are a few different ways to go about emulating this chip, and each
|
|
result in a different style of emulation.
|
|
|
|
The first is to simply return the current PC system time when the S-RTC is
|
|
read from. This emulates the original S-RTC in the sense that it always
|
|
returns the true current time, ignoring the speed that the SNES itself is
|
|
running at. The downside to this method is that you lose the ability to set
|
|
the time to whatever you choose inside the game itself. It will always return
|
|
the true time, regardless. This can be overcome by changing the PC system time,
|
|
which actually adds a greater degree of control over event timing, very useful
|
|
for emulation. It also has a timeshifting flaw discussed below.
|
|
|
|
The second is to run the S-RTC relative to the SNES speed. This means that
|
|
if the emulator is sped up (via fast forward key, frameskipping, etc), or
|
|
slowed down (via slowdown key, system bottlenecking, etc); the time increments
|
|
slower, thus ~60 frames on the SNES equal one second. Without this, timeshifting
|
|
will occur between the S-RTC and the real SNES.
|
|
|
|
The third and final method is to save a copy of the local system time when the
|
|
S-RTC is initially set, and compare the current system time against this value
|
|
when setting the S-RTC time. This overcomes the first methods' shortcoming of
|
|
not allowing the player to set the time in-game, however a new problem arises.
|
|
You now have to save the time when the RTC was initially set to both savestates
|
|
and to save-game data. This would require an extra file, or the breaking of
|
|
perhaps the only standard format (.srm savegame backups) in the entire SNES
|
|
emulation scene. You also give up the control of being able to override the
|
|
RTC clock at will via the PC system time outside of emulation.
|
|
The first method has another advantage over the third: Dai Kaijuu Monogatari II
|
|
only allows dates in the range of the years 1996-2199. The first method gets
|
|
around this limitation. But who knows, maybe it will break something in the
|
|
game if the date exceeds 2199... I guess we'll worry about that in two hundred
|
|
years from now.
|
|
|
|
For my implementation, I chose to go with the first method. Both for simplicity
|
|
and because I did not wish to create a new method for saving the system time
|
|
whenever the RTC is set.
|
|
*/
|
|
|
|
#include "../../base.h"
|
|
|
|
void SRTC::set_time() {
|
|
time_t rawtime;
|
|
tm *t;
|
|
::time(&rawtime);
|
|
t = localtime(&rawtime);
|
|
|
|
//see srtc.h for format of srtc.data[]
|
|
srtc.data[0] = t->tm_sec % 10;
|
|
srtc.data[1] = t->tm_sec / 10;
|
|
srtc.data[2] = t->tm_min % 10;
|
|
srtc.data[3] = t->tm_min / 10;
|
|
srtc.data[4] = t->tm_hour % 10;
|
|
srtc.data[5] = t->tm_hour / 10;
|
|
srtc.data[6] = t->tm_mday % 10;
|
|
srtc.data[7] = t->tm_mday / 10;
|
|
srtc.data[8] = t->tm_mon + 1;
|
|
srtc.data[9] = t->tm_year % 10;
|
|
srtc.data[10] = (t->tm_year / 10) % 10;
|
|
srtc.data[11] = 9 + (t->tm_year / 100);
|
|
srtc.data[12] = t->tm_wday;
|
|
}
|
|
|
|
void SRTC::init() {
|
|
}
|
|
|
|
void SRTC::enable() {
|
|
r_mem->set_mmio_mapper(0x2800, mmio);
|
|
r_mem->set_mmio_mapper(0x2801, mmio);
|
|
}
|
|
|
|
void SRTC::power() {
|
|
memset(&srtc, 0, sizeof(srtc));
|
|
reset();
|
|
}
|
|
|
|
void SRTC::reset() {
|
|
srtc.index = -1;
|
|
srtc.mode = SRTC_READ;
|
|
}
|
|
|
|
//Please see notes above about the implementation of the S-RTC
|
|
//Writes are stored the srtc.data[] array, but they are ignored
|
|
//as reads will refresh the data array with the current system
|
|
//time. The write method is only here for the sake of faux
|
|
//emulation of the real hardware.
|
|
void SRTC::write(uint8 data) {
|
|
data &= 0x0f; //only the low four bits are used
|
|
|
|
if(data >= 0x0d) {
|
|
switch(data) {
|
|
case 0x0d:
|
|
srtc.mode = SRTC_READ;
|
|
srtc.index = -1;
|
|
break;
|
|
case 0x0e:
|
|
srtc.mode = SRTC_COMMAND;
|
|
break;
|
|
case 0x0f:
|
|
//unknown behaviour
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(srtc.mode == SRTC_WRITE) {
|
|
if(srtc.index >= 0 && srtc.index < MAX_SRTC_INDEX) {
|
|
srtc.data[srtc.index++] = data;
|
|
|
|
if(srtc.index == MAX_SRTC_INDEX) {
|
|
//all S-RTC data has been loaded by program
|
|
srtc.data[srtc.index++] = 0x00; //day_of_week
|
|
}
|
|
}
|
|
} else if(srtc.mode == SRTC_COMMAND) {
|
|
switch(data) {
|
|
case SRTC_COMMAND_CLEAR:
|
|
memset(srtc.data, 0, MAX_SRTC_INDEX + 1);
|
|
srtc.index = -1;
|
|
srtc.mode = SRTC_READY;
|
|
break;
|
|
case SRTC_COMMAND_WRITE:
|
|
srtc.index = 0;
|
|
srtc.mode = SRTC_WRITE;
|
|
break;
|
|
default:
|
|
//unknown behaviour
|
|
srtc.mode = SRTC_READY;
|
|
break;
|
|
}
|
|
} else {
|
|
if(srtc.mode == SRTC_READ) {
|
|
//ignore writes while in read mode
|
|
} else if(srtc.mode == SRTC_READY) {
|
|
//unknown behaviour
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8 SRTC::read() {
|
|
if(srtc.mode == SRTC_READ) {
|
|
if(srtc.index < 0) {
|
|
set_time();
|
|
srtc.index++;
|
|
return 0x0f; //send start message
|
|
} else if(srtc.index > MAX_SRTC_INDEX) {
|
|
srtc.index = -1;
|
|
return 0x0f; //send finished message
|
|
} else {
|
|
return srtc.data[srtc.index++];
|
|
}
|
|
} else {
|
|
return 0x00;
|
|
}
|
|
}
|
|
|
|
SRTC::SRTC() {
|
|
mmio = new SRTCMMIO();
|
|
}
|
|
|
|
uint8 SRTCMMIO::read(uint32 addr) {
|
|
switch(addr) {
|
|
case 0x2800:return srtc->read();
|
|
}
|
|
|
|
return r_cpu->regs.mdr;
|
|
}
|
|
|
|
void SRTCMMIO::write(uint32 addr, uint8 value) {
|
|
switch(addr) {
|
|
case 0x2801:srtc->write(value);break;
|
|
}
|
|
}
|