This fixes a race condition that happens whenever the ROM cache is flushed but the RAM one is not, causing any SWI calls (implemented as direct branches) to jump to random instructions. The fix could be to flush both caches at the same time (~expensive on low mem platforms), use indirect jumps (a bit expensive) or emit the SWI handler below the watermark to ensure it is never flushed. This is cheap and effective, requires minimal changes.
343 lines
8.2 KiB
C
343 lines
8.2 KiB
C
/* gameplaySP
|
|
*
|
|
* Copyright (C) 2006 Exophase <exophase@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include <ctype.h>
|
|
|
|
timer_type timer[4];
|
|
|
|
const u32 global_cycles_per_instruction = 1;
|
|
|
|
u32 cpu_ticks = 0;
|
|
|
|
u32 execute_cycles = 960;
|
|
s32 video_count = 960;
|
|
|
|
u32 last_frame = 0;
|
|
u32 flush_ram_count = 0;
|
|
u32 gbc_update_count = 0;
|
|
u32 oam_update_count = 0;
|
|
|
|
char main_path[512];
|
|
char save_path[512];
|
|
|
|
void trigger_ext_event(void);
|
|
|
|
static void update_timers(irq_type *irq_raised)
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if(timer[i].status == TIMER_INACTIVE)
|
|
continue;
|
|
|
|
if(timer[i].status != TIMER_CASCADE)
|
|
{
|
|
timer[i].count -= execute_cycles;
|
|
/* io_registers accessors range: REG_TM0D, REG_TM1D, REG_TM2D, REG_TM3D */
|
|
write_ioreg(REG_TM0D + (i * 2), -(timer[i].count > timer[i].prescale));
|
|
}
|
|
|
|
if(timer[i].count > 0)
|
|
continue;
|
|
|
|
/* irq_raised value range: IRQ_TIMER0, IRQ_TIMER1, IRQ_TIMER2, IRQ_TIMER3 */
|
|
if(timer[i].irq == TIMER_TRIGGER_IRQ)
|
|
*irq_raised |= (8 << i);
|
|
|
|
if((i != 3) && (timer[i + 1].status == TIMER_CASCADE))
|
|
{
|
|
timer[i + 1].count--;
|
|
write_ioreg(REG_TM0D + (i + 1) * 2, -timer[i+1].count);
|
|
}
|
|
|
|
if(i < 2)
|
|
{
|
|
if(timer[i].direct_sound_channels & 0x01)
|
|
sound_timer(timer[i].frequency_step, 0);
|
|
|
|
if(timer[i].direct_sound_channels & 0x02)
|
|
sound_timer(timer[i].frequency_step, 1);
|
|
}
|
|
|
|
timer[i].count += (timer[i].reload << timer[i].prescale);
|
|
}
|
|
}
|
|
|
|
void init_main(void)
|
|
{
|
|
u32 i;
|
|
|
|
for(i = 0; i < 4; i++)
|
|
{
|
|
dma[i].start_type = DMA_INACTIVE;
|
|
dma[i].direct_sound_channel = DMA_NO_DIRECT_SOUND;
|
|
timer[i].status = TIMER_INACTIVE;
|
|
timer[i].reload = 0x10000;
|
|
}
|
|
|
|
timer[0].direct_sound_channels = TIMER_DS_CHANNEL_BOTH;
|
|
timer[1].direct_sound_channels = TIMER_DS_CHANNEL_NONE;
|
|
|
|
cpu_ticks = 0;
|
|
|
|
execute_cycles = 960;
|
|
video_count = 960;
|
|
|
|
#ifdef HAVE_DYNAREC
|
|
init_emitter();
|
|
init_caches();
|
|
#endif
|
|
}
|
|
|
|
u32 update_gba(void)
|
|
{
|
|
irq_type irq_raised = IRQ_NONE;
|
|
|
|
do
|
|
{
|
|
unsigned i;
|
|
cpu_ticks += execute_cycles;
|
|
|
|
reg[CHANGED_PC_STATUS] = 0;
|
|
reg[COMPLETED_FRAME] = 0;
|
|
|
|
if(gbc_sound_update)
|
|
{
|
|
gbc_update_count++;
|
|
update_gbc_sound(cpu_ticks);
|
|
gbc_sound_update = 0;
|
|
}
|
|
|
|
update_timers(&irq_raised);
|
|
|
|
video_count -= execute_cycles;
|
|
|
|
if(video_count <= 0)
|
|
{
|
|
u32 vcount = read_ioreg(REG_VCOUNT);
|
|
u32 dispstat = read_ioreg(REG_DISPSTAT);
|
|
|
|
if((dispstat & 0x02) == 0)
|
|
{
|
|
// Transition from hrefresh to hblank
|
|
video_count += (272);
|
|
dispstat |= 0x02;
|
|
|
|
if((dispstat & 0x01) == 0)
|
|
{
|
|
u32 i;
|
|
if(reg[OAM_UPDATED])
|
|
oam_update_count++;
|
|
|
|
update_scanline();
|
|
|
|
// If in visible area also fire HDMA
|
|
for(i = 0; i < 4; i++)
|
|
{
|
|
if(dma[i].start_type == DMA_START_HBLANK)
|
|
dma_transfer(i);
|
|
}
|
|
}
|
|
|
|
if(dispstat & 0x10)
|
|
irq_raised |= IRQ_HBLANK;
|
|
}
|
|
else
|
|
{
|
|
// Transition from hblank to next line
|
|
video_count += 960;
|
|
dispstat &= ~0x02;
|
|
|
|
vcount++;
|
|
|
|
if(vcount == 160)
|
|
{
|
|
// Transition from vrefresh to vblank
|
|
u32 i;
|
|
|
|
dispstat |= 0x01;
|
|
if(dispstat & 0x8)
|
|
{
|
|
irq_raised |= IRQ_VBLANK;
|
|
}
|
|
|
|
video_reload_counters();
|
|
|
|
for(i = 0; i < 4; i++)
|
|
{
|
|
if(dma[i].start_type == DMA_START_VBLANK)
|
|
dma_transfer(i);
|
|
}
|
|
}
|
|
else
|
|
|
|
if(vcount == 228)
|
|
{
|
|
// Transition from vblank to next screen
|
|
dispstat &= ~0x01;
|
|
|
|
/* printf("frame update (%x), %d instructions total, %d RAM flushes\n",
|
|
reg[REG_PC], instruction_count - last_frame, flush_ram_count);
|
|
last_frame = instruction_count;
|
|
*/
|
|
/* printf("%d gbc audio updates\n", gbc_update_count);
|
|
printf("%d oam updates\n", oam_update_count); */
|
|
gbc_update_count = 0;
|
|
oam_update_count = 0;
|
|
flush_ram_count = 0;
|
|
|
|
update_gbc_sound(cpu_ticks);
|
|
gbc_sound_update = 0;
|
|
|
|
/* If there's no cheat hook, run on vblank! */
|
|
if (cheat_master_hook == ~0U)
|
|
process_cheats();
|
|
|
|
vcount = 0;
|
|
// We completed a frame, tell the dynarec to exit to the main thread
|
|
reg[COMPLETED_FRAME] = 1;
|
|
}
|
|
|
|
if(vcount == (dispstat >> 8))
|
|
{
|
|
// vcount trigger
|
|
dispstat |= 0x04;
|
|
if(dispstat & 0x20)
|
|
{
|
|
irq_raised |= IRQ_VCOUNT;
|
|
}
|
|
}
|
|
else
|
|
dispstat &= ~0x04;
|
|
|
|
write_ioreg(REG_VCOUNT, vcount);
|
|
}
|
|
write_ioreg(REG_DISPSTAT, dispstat);
|
|
}
|
|
|
|
if(irq_raised)
|
|
raise_interrupt(irq_raised);
|
|
|
|
execute_cycles = video_count;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if(timer[i].status != TIMER_PRESCALE)
|
|
continue;
|
|
|
|
if(timer[i].count < execute_cycles)
|
|
execute_cycles = timer[i].count;
|
|
}
|
|
} while(reg[CPU_HALT_STATE] != CPU_ACTIVE && !reg[COMPLETED_FRAME]);
|
|
|
|
return execute_cycles;
|
|
}
|
|
|
|
void reset_gba(void)
|
|
{
|
|
init_memory();
|
|
init_main();
|
|
init_cpu();
|
|
reset_sound();
|
|
}
|
|
|
|
u32 file_length(FILE *fp)
|
|
{
|
|
u32 length;
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
length = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
return length;
|
|
}
|
|
|
|
void change_ext(const char *src, char *buffer, const char *extension)
|
|
{
|
|
char *dot_position;
|
|
strcpy(buffer, src);
|
|
dot_position = strrchr(buffer, '.');
|
|
|
|
if(dot_position)
|
|
strcpy(dot_position, extension);
|
|
}
|
|
|
|
bool main_read_savestate(const u8 *src)
|
|
{
|
|
int i;
|
|
const u8 *p1 = bson_find_key(src, "emu");
|
|
const u8 *p2 = bson_find_key(src, "timers");
|
|
if (!p1 || !p2)
|
|
return false;
|
|
execute_cycles = 123;
|
|
if (!(bson_read_int32(p1, "cpu-ticks", &cpu_ticks) &&
|
|
bson_read_int32(p1, "exec-cycles", &execute_cycles) &&
|
|
bson_read_int32(p1, "video-count", (u32*)&video_count)))
|
|
return false;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
char tname[2] = {'0' + i, 0};
|
|
const u8 *p = bson_find_key(p2, tname);
|
|
|
|
if (!(
|
|
bson_read_int32(p, "count", (u32*)&timer[i].count) &&
|
|
bson_read_int32(p, "reload", &timer[i].reload) &&
|
|
bson_read_int32(p, "prescale", &timer[i].prescale) &&
|
|
bson_read_int32(p, "freq-step", &timer[i].frequency_step) &&
|
|
bson_read_int32(p, "dsc", &timer[i].direct_sound_channels) &&
|
|
bson_read_int32(p, "irq", &timer[i].irq) &&
|
|
bson_read_int32(p, "status", &timer[i].status)))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
unsigned main_write_savestate(u8* dst)
|
|
{
|
|
int i;
|
|
u8 *wbptr, *wbptr2, *startp = dst;
|
|
bson_start_document(dst, "emu", wbptr);
|
|
bson_write_int32(dst, "cpu-ticks", cpu_ticks);
|
|
bson_write_int32(dst, "exec-cycles", execute_cycles);
|
|
bson_write_int32(dst, "video-count", video_count);
|
|
bson_finish_document(dst, wbptr);
|
|
|
|
bson_start_document(dst, "timers", wbptr);
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
char tname[2] = {'0' + i, 0};
|
|
bson_start_document(dst, tname, wbptr2);
|
|
bson_write_int32(dst, "count", timer[i].count);
|
|
bson_write_int32(dst, "reload", timer[i].reload);
|
|
bson_write_int32(dst, "prescale", timer[i].prescale);
|
|
bson_write_int32(dst, "freq-step", timer[i].frequency_step);
|
|
bson_write_int32(dst, "dsc", timer[i].direct_sound_channels);
|
|
bson_write_int32(dst, "irq", timer[i].irq);
|
|
bson_write_int32(dst, "status", timer[i].status);
|
|
bson_finish_document(dst, wbptr2);
|
|
}
|
|
bson_finish_document(dst, wbptr);
|
|
|
|
return (unsigned int)(dst - startp);
|
|
}
|
|
|
|
|