Genesis-Plus-GX/source/system.c

423 lines
12 KiB
C
Raw Normal View History

2008-08-07 14:26:07 +02:00
/***************************************************************************************
* Genesis Plus
2008-08-07 14:26:07 +02:00
* Main Emulation
*
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
* Eke-Eke (2007,2008,2009), additional code & fixes for the GCN/Wii port
*
2008-08-07 14:26:07 +02:00
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
****************************************************************************************/
2007-08-10 22:34:06 +02:00
#include "shared.h"
#include "Fir_Resampler.h"
2009-05-26 18:40:35 +02:00
#include "eq.h"
2007-08-10 22:34:06 +02:00
#define SND_SIZE (snd.buffer_size * sizeof(int16))
2008-08-07 14:26:07 +02:00
/* Global variables */
2007-08-10 22:34:06 +02:00
t_bitmap bitmap;
t_snd snd;
2008-08-07 14:26:07 +02:00
uint32 count_m68k;
uint32 line_m68k;
2008-09-18 17:08:47 +02:00
uint32 hint_m68k;
2008-08-07 14:26:07 +02:00
uint32 count_z80;
uint32 line_z80;
int32 current_z80;
uint8 system_hw;
2009-05-26 18:40:35 +02:00
/****************************************************************
* AUDIO equalizer
****************************************************************/
static EQSTATE eq;
2009-05-26 18:40:35 +02:00
void audio_set_equalizer(void)
{
init_3band_state(&eq,config.low_freq,config.high_freq,snd.sample_rate);
2009-05-26 18:40:35 +02:00
eq.lg = (double)(config.lg);
eq.mg = (double)(config.mg);
eq.hg = (double)(config.hg);
}
/****************************************************************
* AUDIO stream update
****************************************************************/
2009-08-11 10:18:46 +02:00
static int llp,rrp;
void audio_update (int size)
{
2009-08-11 10:18:46 +02:00
int i, l, r;
int ll = llp;
int rr = rrp;
int psg_preamp = config.psg_preamp;
int fm_preamp = config.fm_preamp;
int filter = config.filter;
uint32 factora = (config.lp_range << 16) / 100;
uint32 factorb = 0x10000 - factora;
int16 *fm[2] = {snd.fm.buffer[0],snd.fm.buffer[1]};
int16 *psg = snd.psg.buffer;
#ifdef NGC
int16 *sb = (int16 *) soundbuffer[mixbuffer];
#endif
/* resampling */
if (config.hq_fm)
{
int len = Fir_Resampler_input_needed(size << 1);
sound_update(len >> 1,size);
Fir_Resampler_write(len);
Fir_Resampler_read(fm,size);
}
else
{
sound_update(size,size);
}
/* mix samples */
for (i = 0; i < size; i ++)
{
/* PSG samples (mono) */
l = r = ((*psg++) * psg_preamp)/100;
/* FM samples (stereo) */
l += (*fm[0]++ * fm_preamp)/100;
r += (*fm[1]++ * fm_preamp)/100;
2009-05-26 18:40:35 +02:00
/* filtering */
if (filter & 1)
{
2009-05-26 18:40:35 +02:00
/* single-pole low-pass filter (6 dB/octave) */
ll = (ll>>16)*factora + l*factorb;
rr = (rr>>16)*factora + r*factorb;
2009-08-11 10:18:46 +02:00
l = ll >> 16;
r = rr >> 16;
}
2009-05-26 18:40:35 +02:00
else if (filter & 2)
{
/* 3 Band EQ */
l = do_3band(&eq,l);
r = do_3band(&eq,r);
}
/* clipping */
if (l > 32767) l = 32767;
else if (l < -32768) l = -32768;
if (r > 32767) r = 32767;
else if (r < -32768) r = -32768;
/* update sound buffer */
#ifndef NGC
snd.buffer[0][i] = l;
snd.buffer[1][i] = r;
#else
*sb++ = r;
*sb++ = l;
#endif
}
2009-08-11 10:18:46 +02:00
/* save delayed samples */
llp = ll;
rrp = rr;
}
/****************************************************************
* AUDIO System initialization
****************************************************************/
int audio_init (int rate)
{
/* Shutdown first */
audio_shutdown();
/* Clear the sound data context */
memset(&snd, 0, sizeof (snd));
/* Make sure the requested sample rate is valid */
if (!rate || ((rate < 8000) | (rate > 48000))) return (-1);
snd.sample_rate = rate;
/* Calculate the sound buffer size (for one frame) */
snd.buffer_size = (rate / vdp_rate) + 32;
#ifndef NGC
/* Output buffers */
snd.buffer[0] = (int16 *) malloc(SND_SIZE);
snd.buffer[1] = (int16 *) malloc(SND_SIZE);
if (!snd.buffer[0] || !snd.buffer[1]) return (-1);
#endif
/* SN76489 stream buffers */
snd.psg.buffer = (int16 *)malloc (SND_SIZE);
if (!snd.psg.buffer) return (-1);
/* YM2612 stream buffers */
snd.fm.buffer[0] = (int16 *)malloc (SND_SIZE);
snd.fm.buffer[1] = (int16 *)malloc (SND_SIZE);
if (!snd.fm.buffer[0] || !snd.fm.buffer[1]) return (-1);
/* Resampling buffer */
if (config.hq_fm)
{
if (!Fir_Resampler_initialize(4096)) return (-1);
}
2009-05-26 18:40:35 +02:00
/* 3 band EQ */
audio_set_equalizer();
2009-05-26 18:40:35 +02:00
/* Set audio enable flag */
snd.enabled = 1;
/* Initialize Sound Chips emulation */
sound_init(rate);
return (0);
}
/****************************************************************
* AUDIO System shutdown
****************************************************************/
void audio_shutdown(void)
{
/* Sound buffers */
if (snd.buffer[0]) free(snd.buffer[0]);
if (snd.buffer[1]) free(snd.buffer[1]);
if (snd.fm.buffer[0]) free(snd.fm.buffer[0]);
if (snd.fm.buffer[1]) free(snd.fm.buffer[1]);
if (snd.psg.buffer) free(snd.psg.buffer);
/* Resampling buffer */
Fir_Resampler_shutdown();
}
2007-08-10 22:34:06 +02:00
2008-08-07 14:26:07 +02:00
/****************************************************************
* Virtual Genesis initialization
****************************************************************/
2007-08-10 22:34:06 +02:00
void system_init (void)
{
/* Genesis hardware */
gen_init();
vdp_init();
render_init();
io_init();
/* Cartridge hardware */
cart_hw_init();
2007-08-10 22:34:06 +02:00
}
2008-08-07 14:26:07 +02:00
/****************************************************************
* Virtual Genesis Hard Reset
2008-08-07 14:26:07 +02:00
****************************************************************/
2007-08-10 22:34:06 +02:00
void system_reset (void)
{
/* Cartridge Hardware */
2008-12-10 19:16:30 +01:00
cart_hw_reset();
/* Genesis hardware */
gen_reset(1);
SN76489_Reset();
vdp_reset();
render_reset();
2008-12-10 19:16:30 +01:00
io_reset();
/* Clear Sound Buffers */
if (snd.psg.buffer) memset (snd.psg.buffer, 0, SND_SIZE);
if (snd.fm.buffer[0]) memset (snd.fm.buffer[0], 0, SND_SIZE);
if (snd.fm.buffer[1]) memset (snd.fm.buffer[1], 0, SND_SIZE);
Fir_Resampler_clear();
2007-08-10 22:34:06 +02:00
}
2008-08-07 14:26:07 +02:00
/****************************************************************
* Virtual Genesis shutdown
****************************************************************/
2007-08-10 22:34:06 +02:00
void system_shutdown (void)
{
2008-12-10 19:16:30 +01:00
gen_shutdown ();
vdp_shutdown ();
render_shutdown ();
2007-08-10 22:34:06 +02:00
}
2008-08-07 14:26:07 +02:00
/****************************************************************
* Virtual Genesis Frame emulation
****************************************************************/
2007-08-10 22:34:06 +02:00
int system_frame (int do_skip)
{
2008-12-10 19:16:30 +01:00
if (!gen_running)
2008-12-04 20:32:22 +01:00
{
osd_input_Update();
2008-12-04 20:32:22 +01:00
return 0;
2008-12-10 19:16:30 +01:00
}
uint32 aim_m68k = 0;
uint32 aim_z80 = 0;
2008-12-10 19:16:30 +01:00
/* reset cycles counts */
count_m68k = 0;
count_z80 = 0;
fifo_write_cnt = 0;
fifo_lastwrite = 0;
2008-08-07 14:26:07 +02:00
2008-12-04 20:32:22 +01:00
/* update display settings */
int line;
int reset = resetline;
int vdp_height = bitmap.viewport.h;
int end_line = vdp_height + bitmap.viewport.y;
int start_line = lines_per_frame - bitmap.viewport.y;
2008-12-10 19:16:30 +01:00
int old_interlaced = interlaced;
interlaced = (reg[12] & 2) >> 1;
2008-12-10 19:16:30 +01:00
if (old_interlaced != interlaced)
2008-08-07 14:26:07 +02:00
{
bitmap.viewport.changed = 1;
im2_flag = ((reg[12] & 6) == 6);
2008-12-10 19:16:30 +01:00
odd_frame = 1;
}
2008-08-07 14:26:07 +02:00
odd_frame ^= 1;
2008-12-04 20:32:22 +01:00
2008-12-05 17:26:57 +01:00
/* clear VBLANK and DMA flags */
status &= 0xFFF5;
2008-12-10 19:16:30 +01:00
2008-12-05 17:26:57 +01:00
/* even/odd field flag (interlaced modes only) */
if (odd_frame && interlaced) status |= 0x0010;
2008-08-07 14:26:07 +02:00
else status &= 0xFFEF;
2007-08-10 22:34:06 +02:00
2008-12-10 19:16:30 +01:00
/* reload HCounter */
int h_counter = reg[10];
2007-08-10 22:34:06 +02:00
2008-12-04 20:32:22 +01:00
/* parse sprites for line 0 (done on last line) */
2008-12-10 19:16:30 +01:00
parse_satb (0x80);
2007-08-10 22:34:06 +02:00
/* process scanlines */
2008-12-10 19:16:30 +01:00
for (line = 0; line < lines_per_frame; line ++)
{
2008-12-05 17:26:57 +01:00
/* update VCounter */
2008-12-10 19:16:30 +01:00
v_counter = line;
2007-08-10 22:34:06 +02:00
2008-12-10 19:16:30 +01:00
/* update 6-Buttons or Menacer */
input_update();
2007-08-10 22:34:06 +02:00
2008-12-10 19:16:30 +01:00
/* update CPU cycle counters */
2008-09-18 17:08:47 +02:00
hint_m68k = count_m68k;
2008-08-07 14:26:07 +02:00
line_m68k = aim_m68k;
2008-12-10 19:16:30 +01:00
line_z80 = aim_z80;
2008-08-07 14:26:07 +02:00
aim_z80 += z80cycles_per_line;
aim_m68k += m68cycles_per_line;
2007-08-10 22:34:06 +02:00
2008-12-10 19:16:30 +01:00
/* Soft Reset ? */
if (line == reset)
{
/* Pro Action Replay (switch at "Trainer" position) */
if (config.lock_on == TYPE_AR)
datel_reset(0);
gen_reset(0);
}
2008-08-07 14:26:07 +02:00
2008-12-05 17:26:57 +01:00
/* active display */
2008-12-04 20:32:22 +01:00
if (line <= vdp_height)
2008-12-10 19:16:30 +01:00
{
/* H Interrupt */
2008-12-05 17:26:57 +01:00
if(--h_counter < 0)
2008-12-10 19:16:30 +01:00
{
h_counter = reg[10];
hint_pending = 1;
if (reg[0] & 0x10) irq_status = (irq_status & ~0x40) | 0x14;
2008-12-10 19:16:30 +01:00
2008-12-04 20:32:22 +01:00
/* adjust timings to take further decrement in account (see below) */
if ((line != 0) || (h_counter == 0)) aim_m68k += 36;
2008-09-13 17:42:24 +02:00
}
2008-08-07 14:26:07 +02:00
/* HINT will be triggered on next line, approx. 36 cycles before VDP starts line rendering */
/* during this period, any VRAM or VSRAM writes should NOT be taken in account before next line */
/* as a result, line is rendered immediately after HINT and current line is shortened */
/* CRAM and VDP register writes that could occur during HBLANK are handled separately */
/* fix Lotus 1, Lotus 2 RECS, Striker, Skitchin, Zero the Kamikaze Squirell */
2008-12-04 20:32:22 +01:00
if ((line < vdp_height) && (h_counter == 0)) aim_m68k -= 36;
2007-08-10 22:34:06 +02:00
2008-12-04 20:32:22 +01:00
/* update DMA timings */
if (dma_length) vdp_update_dma();
2007-08-10 22:34:06 +02:00
2008-12-10 19:16:30 +01:00
/* vertical retrace */
2008-12-04 20:32:22 +01:00
if (line == vdp_height)
2008-12-10 19:16:30 +01:00
{
2008-12-04 20:32:22 +01:00
/* render overscan */
if ((line < end_line) && (!do_skip)) render_line(line, 1);
2008-12-05 17:26:57 +01:00
/* update inputs (doing this here fix Warriors of Eternal Sun) */
osd_input_Update();
2008-08-07 14:26:07 +02:00
2008-12-05 17:26:57 +01:00
/* set VBLANK flag */
status |= 0x08;
2008-08-07 14:26:07 +02:00
2008-12-05 17:26:57 +01:00
/* Z80 interrupt is 16ms period (one frame) and 64us length (one scanline) */
2008-12-10 19:16:30 +01:00
zirq = 1;
z80_set_irq_line(0, ASSERT_LINE);
2008-12-05 17:26:57 +01:00
/* delay between HINT, VBLANK and VINT (Dracula, OutRunners, VR Troopers) */
m68k_run(line_m68k + 84);
2008-12-04 20:32:22 +01:00
if (zreset && !zbusreq)
{
current_z80 = line_z80 + 39 - count_z80;
if (current_z80 > 0) count_z80 += z80_execute(current_z80);
}
2008-12-05 17:26:57 +01:00
else count_z80 = line_z80 + 39;
2008-08-07 14:26:07 +02:00
2008-12-10 19:16:30 +01:00
/* V Interrupt */
2008-12-05 17:26:57 +01:00
status |= 0x80;
/* 36 cycles latency after VINT occurence flag (Ex-Mutants, Tyrant) */
m68k_run(line_m68k + 113);
vint_pending = 1;
if (reg[1] & 0x20) irq_status = (irq_status & ~0x40) | 0x36;
2008-12-04 20:32:22 +01:00
}
else if (!do_skip)
{
/* render scanline and parse sprites for line n+1 */
render_line(line, 0);
if (line < (vdp_height-1)) parse_satb(0x81 + line);
2008-12-10 19:16:30 +01:00
}
2008-12-04 20:32:22 +01:00
}
else
{
/* update DMA timings */
if (dma_length) vdp_update_dma();
2008-12-04 20:32:22 +01:00
/* render overscan */
2008-12-05 17:26:57 +01:00
if ((!do_skip) && ((line < end_line) || (line >= start_line))) render_line(line, 1);
2008-12-04 20:32:22 +01:00
2008-08-07 14:26:07 +02:00
/* clear any pending Z80 interrupt */
2008-12-04 20:32:22 +01:00
if (zirq)
{
2008-12-05 17:26:57 +01:00
zirq = 0;
z80_set_irq_line(0, CLEAR_LINE);
}
2008-08-07 14:26:07 +02:00
}
2008-12-10 19:16:30 +01:00
/* process line */
m68k_run(aim_m68k);
if (zreset == 1 && zbusreq == 0)
2008-12-04 20:32:22 +01:00
{
current_z80 = aim_z80 - count_z80;
if (current_z80 > 0) count_z80 += z80_execute(current_z80);
2008-12-10 19:16:30 +01:00
}
else count_z80 = aim_z80;
/* SVP chip */
if (svp) ssp1601_run(SVP_cycles);
}
return gen_running;
2007-08-10 22:34:06 +02:00
}