gpsp/main.c
David Guillen Fandos 6a59c71d55 Get rid of enums (to u32) for better compat
Serialization code needs u32 to ensure compatiblity. In platforms with
short enums this goes very wrong. Fixes issues on 3DS platform.
2021-10-15 21:28:40 +02:00

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