Merge remote-tracking branch 'upstream/master'

This commit is contained in:
sergiobenrocha2 2017-01-02 01:14:52 -02:00
commit 0247ea6202
59 changed files with 2664 additions and 943 deletions

5
.gitignore vendored
View File

@ -3,3 +3,8 @@ psp2/*.elf
psp2/*.velf psp2/*.velf
psp2/build_vita/* psp2/build_vita/*
psp2/*.a psp2/*.a
sdl/gen_sdl
sdl/gen_sdl2
sdl/build_sdl
sdl/build_sdl2

View File

@ -28,6 +28,7 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke)
* fixed CDD "no disc" status code (fixes boot sequence loading time when no disc is loaded) * fixed CDD "no disc" status code (fixes boot sequence loading time when no disc is loaded)
* fixed AUDIO tracks length calculation when using separated files (WAV/OGG/BIN) with INDEX pause defined in CUE file * fixed AUDIO tracks length calculation when using separated files (WAV/OGG/BIN) with INDEX pause defined in CUE file
* fixed OGG file seeking when using with CUE file * fixed OGG file seeking when using with CUE file
* fixed WAV file detection to support files generated by Audacity/FFmpeg/libavformat with RIFF header metadata chunks
* fixed PRG-RAM access from MAIN-CPU side on system reset * fixed PRG-RAM access from MAIN-CPU side on system reset
* fixed state loading bug when SUB-CPU interrupt is pending * fixed state loading bug when SUB-CPU interrupt is pending
* fixed incorrect masking of Level 3 (GFX) interrupts (spurious freeze during Japanese BIOS intro) * fixed incorrect masking of Level 3 (GFX) interrupts (spurious freeze during Japanese BIOS intro)
@ -112,6 +113,11 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke)
* fixed 68k cycles delay on invalid VRAM writes (fixes "Microcosm" intro loop) * fixed 68k cycles delay on invalid VRAM writes (fixes "Microcosm" intro loop)
* optimized tile caching * optimized tile caching
[Core/Sound]
---------------
* rewrote optimized & more accurate PSG core from scratch
* removed PSG boost noise feature & added optional high-quality PSG resampling
[Gamecube/Wii] [Gamecube/Wii]
--------------- ---------------
* added configurable BIOS & Lock-on ROM files * added configurable BIOS & Lock-on ROM files

View File

@ -27,7 +27,7 @@ INCLUDES := core core/m68k core/z80 core/sound core/tremor core/ntsc core/input_
# options for code generation # options for code generation
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
CFLAGS = -O3 -fomit-frame-pointer -Wall -Wno-strict-aliasing $(MACHDEP) $(INCLUDE) -DUSE_LIBTREMOR -DDISABLE_MANY_OGG_OPEN_FILES -DUSE_16BPP_RENDERING -DALT_RENDERER CFLAGS = -O3 -fomit-frame-pointer -Wall -Wno-strict-aliasing $(MACHDEP) $(INCLUDE) -DUSE_LIBTREMOR -DDISABLE_MANY_OGG_OPEN_FILES -DUSE_16BPP_RENDERING -DALT_RENDERER -DBLIP_INVERT
CXXFLAGS = $(CFLAGS) CXXFLAGS = $(CFLAGS)
LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map

View File

@ -27,7 +27,7 @@ INCLUDES := core core/m68k core/z80 core/sound core/tremor core/ntsc core/input_
# options for code generation # options for code generation
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
CFLAGS = -O3 -fomit-frame-pointer -Wall -Wno-strict-aliasing $(MACHDEP) $(INCLUDE) -DUSE_LIBTREMOR -DUSE_16BPP_RENDERING -DALT_RENDERER -DHW_RVL CFLAGS = -O3 -fomit-frame-pointer -Wall -Wno-strict-aliasing $(MACHDEP) $(INCLUDE) -DUSE_LIBTREMOR -DUSE_16BPP_RENDERING -DALT_RENDERER -DBLIP_INVERT -DHW_RVL
CXXFLAGS = $(CFLAGS) CXXFLAGS = $(CFLAGS)
LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map,-wrap,wiiuse_set_ir,-wrap,wiiuse_handshake,-wrap,classic_ctrl_handshake,-wrap,classic_ctrl_event LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map,-wrap,wiiuse_set_ir,-wrap,wiiuse_handshake,-wrap,classic_ctrl_handshake,-wrap,classic_ctrl_event

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 MiB

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 MiB

After

Width:  |  Height:  |  Size: 3.3 MiB

View File

@ -2,7 +2,7 @@
* Genesis Plus * Genesis Plus
* Backup RAM support * Backup RAM support
* *
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -68,7 +68,17 @@ void sram_init()
sram.sram = cart.rom + 0x800000; sram.sram = cart.rom + 0x800000;
/* initialize Backup RAM */ /* initialize Backup RAM */
memset(sram.sram, 0xFF, 0x10000); if (strstr(rominfo.international,"Sonic 1 Remastered"))
{
/* Sonic 1 Remastered hack crashes if backup RAM is not initialized to zero */
memset(sram.sram, 0x00, 0x10000);
}
else
{
/* by default, assume backup RAM is initialized to 0xFF (Micro Machines 2, Dino Dini Soccer) */
memset(sram.sram, 0xFF, 0x10000);
}
sram.crc = crc32(0, sram.sram, 0x10000); sram.crc = crc32(0, sram.sram, 0x10000);
/* retrieve informations from header */ /* retrieve informations from header */

View File

@ -2,7 +2,7 @@
* Genesis Plus * Genesis Plus
* Backup RAM support * Backup RAM support
* *
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:

View File

@ -123,10 +123,10 @@ static const uint32 toc_ffightj[29] =
}; };
/* supported WAVE file header (16-bit stereo samples @44.1kHz) */ /* supported WAVE file header (16-bit stereo samples @44.1kHz) */
static const unsigned char waveHeader[32] = static const unsigned char waveHeader[28] =
{ {
0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00, 0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,0x10,0x00,0x00,0x00,0x01,0x00,
0x44,0xac,0x00,0x00,0x10,0xb1,0x02,0x00,0x04,0x00,0x10,0x00,0x64,0x61,0x74,0x61 0x02,0x00,0x44,0xac,0x00,0x00,0x10,0xb1,0x02,0x00,0x04,0x00,0x10,0x00
}; };
/* supported WAVE file extensions */ /* supported WAVE file extensions */
@ -179,8 +179,7 @@ void cdd_init(int samplerate)
{ {
/* CD-DA is running by default at 44100 Hz */ /* CD-DA is running by default at 44100 Hz */
/* Audio stream is resampled to desired rate using Blip Buffer */ /* Audio stream is resampled to desired rate using Blip Buffer */
blip_set_rates(snd.blips[2][0], 44100, samplerate); blip_set_rates(snd.blips[2], 44100, samplerate);
blip_set_rates(snd.blips[2][1], 44100, samplerate);
} }
void cdd_reset(void) void cdd_reset(void)
@ -449,16 +448,39 @@ int cdd_load(char *filename, char *header)
if (!strstr(lptr,"BINARY") && !strstr(lptr,"MOTOROLA")) if (!strstr(lptr,"BINARY") && !strstr(lptr,"MOTOROLA"))
{ {
/* read file header */ /* read file header */
unsigned char head[32]; unsigned char head[28];
fseek(cdd.toc.tracks[cdd.toc.last].fd, 8, SEEK_SET); fseek(cdd.toc.tracks[cdd.toc.last].fd, 8, SEEK_SET);
fread(head, 32, 1, cdd.toc.tracks[cdd.toc.last].fd); fread(head, 28, 1, cdd.toc.tracks[cdd.toc.last].fd);
fseek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_SET); fseek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_SET);
/* autodetect WAVE file header (44.1KHz 16-bit stereo format only) */ /* autodetect WAVE file header (44.1KHz 16-bit stereo format only) */
if (!memcmp(head, waveHeader, 32)) if (!memcmp(head, waveHeader, 28))
{ {
/* look for 'data' chunk id */
int dataOffset = 0;
fseek(cdd.toc.tracks[cdd.toc.last].fd, 36, SEEK_SET);
while (fread(head, 4, 1, cdd.toc.tracks[cdd.toc.last].fd))
{
if (!memcmp(head, "data", 4))
{
dataOffset = ftell(cdd.toc.tracks[cdd.toc.last].fd) + 4;
fseek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_SET);
break;
}
fseek(cdd.toc.tracks[cdd.toc.last].fd, -2, SEEK_CUR);
}
/* check if 'data' chunk has not been found */
if (!dataOffset)
{
/* invalid WAVE file */
fclose(cdd.toc.tracks[cdd.toc.last].fd);
cdd.toc.tracks[cdd.toc.last].fd = 0;
break;
}
/* adjust current track file read offset with WAVE header length */ /* adjust current track file read offset with WAVE header length */
cdd.toc.tracks[cdd.toc.last].offset -= 44; cdd.toc.tracks[cdd.toc.last].offset -= dataOffset;
} }
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) #if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
else if (!ov_open(cdd.toc.tracks[cdd.toc.last].fd,&cdd.toc.tracks[cdd.toc.last].vf,0,0)) else if (!ov_open(cdd.toc.tracks[cdd.toc.last].fd,&cdd.toc.tracks[cdd.toc.last].vf,0,0))
@ -699,14 +721,35 @@ int cdd_load(char *filename, char *header)
while (fd) while (fd)
{ {
/* read file HEADER */ /* read file HEADER */
unsigned char head[32]; unsigned char head[28];
fseek(fd, 8, SEEK_SET); fseek(fd, 8, SEEK_SET);
fread(head, 32, 1, fd); fread(head, 28, 1, fd);
fseek(fd, 0, SEEK_SET); fseek(fd, 0, SEEK_SET);
/* check if this is a valid WAVE file (44.1KHz 16-bit stereo format only) */ /* check if this is a valid WAVE file (44.1KHz 16-bit stereo format only) */
if (!memcmp(head, waveHeader, 32)) if (!memcmp(head, waveHeader, 28))
{ {
/* look for 'data' chunk id */
int dataOffset = 0;
fseek(fd, 36, SEEK_SET);
while (fread(head, 4, 1, fd))
{
if (!memcmp(head, "data", 4))
{
dataOffset = ftell(fd) + 4;
break;
}
fseek(fd, -2, SEEK_CUR);
}
/* check if 'data' chunk has not been found */
if (!dataOffset)
{
/* invalid WAVE file */
fclose(fd);
break;
}
/* initialize current track file descriptor */ /* initialize current track file descriptor */
cdd.toc.tracks[cdd.toc.last].fd = fd; cdd.toc.tracks[cdd.toc.last].fd = fd;
@ -718,7 +761,7 @@ int cdd_load(char *filename, char *header)
/* current track end time */ /* current track end time */
fseek(fd, 0, SEEK_END); fseek(fd, 0, SEEK_END);
cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + ((ftell(fd) - 44 + 2351) / 2352); cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + ((ftell(fd) - dataOffset + 2351) / 2352);
/* initialize file read offset for current track */ /* initialize file read offset for current track */
cdd.toc.tracks[cdd.toc.last].offset = cdd.toc.tracks[cdd.toc.last].start * 2352; cdd.toc.tracks[cdd.toc.last].offset = cdd.toc.tracks[cdd.toc.last].start * 2352;
@ -738,7 +781,7 @@ int cdd_load(char *filename, char *header)
cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end;
/* adjust file read offset for current track with WAVE header length */ /* adjust file read offset for current track with WAVE header length */
cdd.toc.tracks[cdd.toc.last].offset -= 44; cdd.toc.tracks[cdd.toc.last].offset -= dataOffset;
/* increment track number */ /* increment track number */
cdd.toc.last++; cdd.toc.last++;
@ -1015,16 +1058,16 @@ void cdd_read_data(uint8 *dst)
void cdd_read_audio(unsigned int samples) void cdd_read_audio(unsigned int samples)
{ {
/* previous audio outputs */ /* previous audio outputs */
int16 l = cdd.audio[0]; int prev_l = cdd.audio[0];
int16 r = cdd.audio[1]; int prev_r = cdd.audio[1];
/* get number of internal clocks (samples) needed */ /* get number of internal clocks (samples) needed */
samples = blip_clocks_needed(snd.blips[2][0], samples); samples = blip_clocks_needed(snd.blips[2], samples);
/* audio track playing ? */ /* audio track playing ? */
if (!scd.regs[0x36>>1].byte.h && cdd.toc.tracks[cdd.index].fd) if (!scd.regs[0x36>>1].byte.h && cdd.toc.tracks[cdd.index].fd)
{ {
int i, mul, delta; int i, mul, l, r;
/* current CD-DA fader volume */ /* current CD-DA fader volume */
int curVol = cdd.volume; int curVol = cdd.volume;
@ -1062,17 +1105,13 @@ void cdd_read_audio(unsigned int samples)
/* (MIN) 0,1,2,3,4,8,12,16,20...,1020,1024 (MAX) */ /* (MIN) 0,1,2,3,4,8,12,16,20...,1020,1024 (MAX) */
mul = (curVol & 0x7fc) ? (curVol & 0x7fc) : (curVol & 0x03); mul = (curVol & 0x7fc) ? (curVol & 0x7fc) : (curVol & 0x03);
/* left channel */ /* left & right channels */
delta = ((ptr[0] * mul) / 1024) - l; l = ((ptr[0] * mul) / 1024);
ptr++; r = ((ptr[1] * mul) / 1024);
l += delta; blip_add_delta_fast(snd.blips[2], i, l-prev_l, r-prev_r);
blip_add_delta_fast(snd.blips[2][0], i, delta); prev_l = l;
prev_r = r;
/* right channel */ ptr+=2;
delta = ((ptr[0] * mul) / 1024) - r;
ptr++;
r += delta;
blip_add_delta_fast(snd.blips[2][1], i, delta);
/* update CD-DA fader volume (one step/sample) */ /* update CD-DA fader volume (one step/sample) */
if (curVol < endVol) if (curVol < endVol)
@ -1109,27 +1148,19 @@ void cdd_read_audio(unsigned int samples)
/* (MIN) 0,1,2,3,4,8,12,16,20...,1020,1024 (MAX) */ /* (MIN) 0,1,2,3,4,8,12,16,20...,1020,1024 (MAX) */
mul = (curVol & 0x7fc) ? (curVol & 0x7fc) : (curVol & 0x03); mul = (curVol & 0x7fc) ? (curVol & 0x7fc) : (curVol & 0x03);
/* left channel */ /* left & right channels */
#ifdef LSB_FIRST #ifdef LSB_FIRST
delta = ((ptr[0] * mul) / 1024) - l; l = ((ptr[0] * mul) / 1024);
ptr++; r = ((ptr[1] * mul) / 1024);
ptr+=2;
#else #else
delta = (((int16)((ptr[0] + ptr[1]*256)) * mul) / 1024) - l; l = (((int16)((ptr[0] + ptr[1]*256)) * mul) / 1024);
ptr += 2; r = (((int16)((ptr[2] + ptr[3]*256)) * mul) / 1024);
ptr+=4;
#endif #endif
l += delta; blip_add_delta_fast(snd.blips[2], i, l-prev_l, r-prev_r);
blip_add_delta_fast(snd.blips[2][0], i, delta); prev_l = l;
prev_r = r;
/* right channel */
#ifdef LSB_FIRST
delta = ((ptr[0] * mul) / 1024) - r;
ptr++;
#else
delta = (((int16)((ptr[0] + ptr[1]*256)) * mul) / 1024) - r;
ptr += 2;
#endif
r += delta;
blip_add_delta_fast(snd.blips[2][1], i, delta);
/* update CD-DA fader volume (one step/sample) */ /* update CD-DA fader volume (one step/sample) */
if (curVol < endVol) if (curVol < endVol)
@ -1154,23 +1185,24 @@ void cdd_read_audio(unsigned int samples)
cdd.volume = curVol; cdd.volume = curVol;
/* save last audio output for next frame */ /* save last audio output for next frame */
cdd.audio[0] = l; cdd.audio[0] = prev_l;
cdd.audio[1] = r; cdd.audio[1] = prev_r;
} }
else else
{ {
/* no audio output */ /* no audio output */
if (l) blip_add_delta_fast(snd.blips[2][0], 0, -l); if (prev_l | prev_r)
if (r) blip_add_delta_fast(snd.blips[2][1], 0, -r); {
blip_add_delta_fast(snd.blips[2], 0, -prev_l, -prev_r);
/* save audio output for next frame */ /* save audio output for next frame */
cdd.audio[0] = 0; cdd.audio[0] = 0;
cdd.audio[1] = 0; cdd.audio[1] = 0;
}
} }
/* end of Blip Buffer timeframe */ /* end of Blip Buffer timeframe */
blip_end_frame(snd.blips[2][0], samples); blip_end_frame(snd.blips[2], samples);
blip_end_frame(snd.blips[2][1], samples);
} }
static void cdd_read_subcode(void) static void cdd_read_subcode(void)

View File

@ -2,7 +2,7 @@
* Genesis Plus * Genesis Plus
* PCM sound chip (315-5476A) (RF5C164 compatible) * PCM sound chip (315-5476A) (RF5C164 compatible)
* *
* Copyright (C) 2012-2014 Eke-Eke (Genesis Plus GX) * Copyright (C) 2012-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -45,8 +45,7 @@ void pcm_init(double clock, int samplerate)
{ {
/* PCM chip is running at original rate and is synchronized with SUB-CPU */ /* PCM chip is running at original rate and is synchronized with SUB-CPU */
/* Chip output is resampled to desired rate using Blip Buffer. */ /* Chip output is resampled to desired rate using Blip Buffer. */
blip_set_rates(snd.blips[1][0], clock / PCM_SCYCLES_RATIO, samplerate); blip_set_rates(snd.blips[1], clock / PCM_SCYCLES_RATIO, samplerate);
blip_set_rates(snd.blips[1][1], clock / PCM_SCYCLES_RATIO, samplerate);
} }
void pcm_reset(void) void pcm_reset(void)
@ -71,8 +70,7 @@ void pcm_reset(void)
pcm.cycles = 0; pcm.cycles = 0;
/* clear blip buffers */ /* clear blip buffers */
blip_clear(snd.blips[1][0]); blip_clear(snd.blips[1]);
blip_clear(snd.blips[1][1]);
} }
int pcm_context_save(uint8 *state) int pcm_context_save(uint8 *state)
@ -117,6 +115,11 @@ void pcm_run(unsigned int length)
#ifdef LOG_PCM #ifdef LOG_PCM
error("[%d][%d]run %d PCM samples (from %d)\n", v_counter, s68k.cycles, length, pcm.cycles); error("[%d][%d]run %d PCM samples (from %d)\n", v_counter, s68k.cycles, length, pcm.cycles);
#endif #endif
/* previous audio outputs */
int prev_l = pcm.out[0];
int prev_r = pcm.out[1];
/* check if PCM chip is running */ /* check if PCM chip is running */
if (pcm.enabled) if (pcm.enabled)
{ {
@ -180,41 +183,29 @@ void pcm_run(unsigned int length)
if (r < -32768) r = -32768; if (r < -32768) r = -32768;
else if (r > 32767) r = 32767; else if (r > 32767) r = 32767;
/* check if PCM left output changed */ /* update Blip Buffer */
if (pcm.out[0] != l) blip_add_delta_fast(snd.blips[1], i, l-prev_l, r-prev_r);
{ prev_l = l;
blip_add_delta_fast(snd.blips[1][0], i, l-pcm.out[0]); prev_r = r;
pcm.out[0] = l;
}
/* check if PCM right output changed */
if (pcm.out[1] != r)
{
blip_add_delta_fast(snd.blips[1][1], i, r-pcm.out[1]);
pcm.out[1] = r;
}
} }
/* save last audio outputs */
pcm.out[0] = prev_l;
pcm.out[1] = prev_r;
} }
else else
{ {
/* check if PCM left output changed */ /* check if PCM output was not muted */
if (pcm.out[0]) if (prev_l | prev_r)
{ {
blip_add_delta_fast(snd.blips[1][0], 0, -pcm.out[0]); blip_add_delta_fast(snd.blips[1], 0, -prev_l, -prev_r);
pcm.out[0] = 0; pcm.out[0] = 0;
}
/* check if PCM right output changed */
if (pcm.out[1])
{
blip_add_delta_fast(snd.blips[1][1], 0, -pcm.out[1]);
pcm.out[1] = 0; pcm.out[1] = 0;
} }
} }
/* end of blip buffer frame */ /* end of blip buffer frame */
blip_end_frame(snd.blips[1][0], length); blip_end_frame(snd.blips[1], length);
blip_end_frame(snd.blips[1][1], length);
/* update PCM master clock counter */ /* update PCM master clock counter */
pcm.cycles += length * PCM_SCYCLES_RATIO; pcm.cycles += length * PCM_SCYCLES_RATIO;
@ -223,7 +214,7 @@ void pcm_run(unsigned int length)
void pcm_update(unsigned int samples) void pcm_update(unsigned int samples)
{ {
/* get number of internal clocks (samples) needed */ /* get number of internal clocks (samples) needed */
unsigned int clocks = blip_clocks_needed(snd.blips[1][0], samples); unsigned int clocks = blip_clocks_needed(snd.blips[1], samples);
/* run PCM chip */ /* run PCM chip */
if (clocks > 0) if (clocks > 0)

View File

@ -2,7 +2,7 @@
* Genesis Plus * Genesis Plus
* PCM sound chip (315-5476A) (RF5C164 compatible) * PCM sound chip (315-5476A) (RF5C164 compatible)
* *
* Copyright (C) 2012-2014 Eke-Eke (Genesis Plus GX) * Copyright (C) 2012-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:

View File

@ -4,8 +4,8 @@
* *
* Support for SG-1000, Mark-III, Master System, Game Gear, Mega Drive & Mega CD hardware * Support for SG-1000, Mark-III, Master System, Game Gear, Mega Drive & Mega CD hardware
* *
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) * Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2014 Eke-Eke (Genesis Plus GX) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -234,24 +234,39 @@ void gen_reset(int hard_reset)
/* System Reset */ /* System Reset */
if (hard_reset) if (hard_reset)
{ {
/* clear RAM (TODO: use random bit patterns for all systems, like on real hardware) */ /* On hard reset, 68k CPU always starts at the same point in VDP frame */
/* Tests performed on VA4 PAL MD1 showed that the first HVC value read */
/* with 'move.w #0x8104,0xC00004' , 'move.w 0xC00008,%d0' sequence was */
/* 0x9F21 in 60HZ mode (0x9F00 if Mode 5 is not enabled by first MOVE) */
/* 0x8421 in 50HZ mode (0x8400 if Mode 5 is not enabled by first MOVE) */
/* Same value is returned on every power ON, indicating VDP is always */
/* starting at the same fixed point in frame (probably at the start of */
/* VSYNC and HSYNC) while 68k /VRES line remains asserted a fixed time */
/* after /SRES line has been released (13 msec approx). The difference */
/* between PAL & NTSC is caused by the top border area being 27 lines */
/* larger in PAL mode than in NTSC mode. CPU cycle counter is adjusted */
/* to match these results (taking in account emulated frame is started */
/* on line 192 */
m68k.cycles = ((lines_per_frame - 192 + 159 - (27 * vdp_pal)) * MCYCLES_PER_LINE) + 1004;
/* clear RAM (on real hardware, RAM values are random / undetermined on Power ON) */
memset(work_ram, 0x00, sizeof (work_ram)); memset(work_ram, 0x00, sizeof (work_ram));
memset(zram, 0x00, sizeof (zram)); memset(zram, 0x00, sizeof (zram));
} }
else else
{ {
/* when RESET button is pressed, 68k could be anywhere in VDP frame (Bonkers, Eternal Champions, X-Men 2) */
m68k.cycles = (uint32)((MCYCLES_PER_LINE * lines_per_frame) * ((double)rand() / (double)RAND_MAX));
/* reset YM2612 (on hard reset, this is done by sound_reset) */ /* reset YM2612 (on hard reset, this is done by sound_reset) */
fm_reset(0); fm_reset(0);
} }
/* 68k & Z80 could be anywhere in VDP frame (Bonkers, Eternal Champions, X-Men 2) */ /* 68k M-cycles should be a multiple of 7 */
m68k.cycles = Z80.cycles = (uint32)((MCYCLES_PER_LINE * lines_per_frame) * ((double)rand() / (double)RAND_MAX));
/* 68k cycles should be a multiple of 7 */
m68k.cycles = (m68k.cycles / 7) * 7; m68k.cycles = (m68k.cycles / 7) * 7;
/* Z80 cycles should be a multiple of 15 */ /* Z80 M-cycles should be a multiple of 15 */
Z80.cycles = (Z80.cycles / 15) * 15; Z80.cycles = (m68k.cycles / 15) * 15;
/* 8-bit / 16-bit modes */ /* 8-bit / 16-bit modes */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)

View File

@ -4,8 +4,8 @@
* *
* Support for SG-1000, Mark-III, Master System, Game Gear, Mega Drive & Mega CD hardware * Support for SG-1000, Mark-III, Master System, Game Gear, Mega Drive & Mega CD hardware
* *
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) * Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2014 Eke-Eke (Genesis Plus GX) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:

View File

@ -570,7 +570,7 @@ void io_gg_write(unsigned int offset, unsigned int data)
case 6: /* PSG Stereo output control */ case 6: /* PSG Stereo output control */
io_reg[6] = data; io_reg[6] = data;
SN76489_Config(Z80.cycles, config.psg_preamp, config.psgBoostNoise, data); psg_config(Z80.cycles, config.psg_preamp, data);
return; return;
default: /* Read-only */ default: /* Read-only */

View File

@ -207,7 +207,7 @@ void z80_write_byte(unsigned int address, unsigned int data)
default: /* ZRAM */ default: /* ZRAM */
{ {
zram[address & 0x1FFF] = data; zram[address & 0x1FFF] = data;
m68k.cycles += 8; /* ZRAM access latency (fixes Pacman 2: New Adventures) */ m68k.cycles += 2 * 7; /* ZRAM access latency (fixes Pacman 2: New Adventures & Puyo Puyo 2) */
return; return;
} }
} }
@ -1218,7 +1218,7 @@ void vdp_write_byte(unsigned int address, unsigned int data)
{ {
if (address & 1) if (address & 1)
{ {
SN76489_Write(m68k.cycles, data); psg_write(m68k.cycles, data);
return; return;
} }
m68k_unused_8_w(address, data); m68k_unused_8_w(address, data);
@ -1264,7 +1264,7 @@ void vdp_write_word(unsigned int address, unsigned int data)
case 0x10: /* PSG */ case 0x10: /* PSG */
case 0x14: case 0x14:
{ {
SN76489_Write(m68k.cycles, data & 0xFF); psg_write(m68k.cycles, data & 0xFF);
return; return;
} }

View File

@ -2,8 +2,8 @@
* Genesis Plus * Genesis Plus
* Z80 bank access to 68k bus * Z80 bank access to 68k bus
* *
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) * Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2011 Eke-Eke (Genesis Plus GX) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -294,7 +294,7 @@ void zbank_write_vdp(unsigned int address, unsigned int data)
{ {
if (address & 1) if (address & 1)
{ {
SN76489_Write(Z80.cycles, data); psg_write(Z80.cycles, data);
return; return;
} }
zbank_unused_w(address, data); zbank_unused_w(address, data);

View File

@ -2,8 +2,8 @@
* Genesis Plus * Genesis Plus
* Z80 bank access to 68k bus * Z80 bank access to 68k bus
* *
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) * Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2012 Eke-Eke (Genesis Plus GX) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:

View File

@ -231,7 +231,7 @@ void z80_md_port_w(unsigned int port, unsigned char data)
case 0x40: case 0x40:
case 0x41: case 0x41:
{ {
SN76489_Write(Z80.cycles, data); psg_write(Z80.cycles, data);
return; return;
} }
@ -345,7 +345,7 @@ void z80_gg_port_w(unsigned int port, unsigned char data)
case 0x40: case 0x40:
case 0x41: case 0x41:
{ {
SN76489_Write(Z80.cycles, data); psg_write(Z80.cycles, data);
return; return;
} }
@ -450,7 +450,7 @@ void z80_ms_port_w(unsigned int port, unsigned char data)
case 0x40: case 0x40:
case 0x41: case 0x41:
{ {
SN76489_Write(Z80.cycles, data); psg_write(Z80.cycles, data);
return; return;
} }
@ -493,7 +493,7 @@ void z80_ms_port_w(unsigned int port, unsigned char data)
1 0 : disable both PSG & FM output 1 0 : disable both PSG & FM output
1 1 : enable both PSG and FM output 1 1 : enable both PSG and FM output
*/ */
SN76489_Config(Z80.cycles, config.psg_preamp, config.psgBoostNoise, ((data + 1) & 0x02) ? 0x00 : 0xFF); psg_config(Z80.cycles, config.psg_preamp, ((data + 1) & 0x02) ? 0x00 : 0xFF);
fm_write(Z80.cycles, 0x02, data); fm_write(Z80.cycles, 0x02, data);
io_reg[6] = data; io_reg[6] = data;
return; return;
@ -611,7 +611,7 @@ void z80_m3_port_w(unsigned int port, unsigned char data)
case 0x40: case 0x40:
case 0x41: case 0x41:
{ {
SN76489_Write(Z80.cycles, data); psg_write(Z80.cycles, data);
return; return;
} }
@ -699,7 +699,7 @@ void z80_sg_port_w(unsigned int port, unsigned char data)
case 0x40: case 0x40:
case 0x41: case 0x41:
{ {
SN76489_Write(Z80.cycles, data); psg_write(Z80.cycles, data);
/* Z80 !WAIT input is tied to SN76489AN chip READY pin (held low for 32 clocks after each write access) */ /* Z80 !WAIT input is tied to SN76489AN chip READY pin (held low for 32 clocks after each write access) */
Z80.cycles += (32 * 15); Z80.cycles += (32 * 15);

View File

@ -22,7 +22,7 @@
#include "io_ctrl.h" #include "io_ctrl.h"
#include "input.h" #include "input.h"
#include "sound.h" #include "sound.h"
#include "sn76489.h" #include "psg.h"
#include "ym2413.h" #include "ym2413.h"
#include "ym2612.h" #include "ym2612.h"
#include "sram.h" #include "sram.h"

View File

@ -1,10 +1,11 @@
/* blip_buf $vers. http://www.slack.net/~ant/ */ /* blip_buf $vers. http://www.slack.net/~ant/ */
/* Modified for Genesis Plus GX by EkeEke (01/09/12) */ /* Modified for Genesis Plus GX by EkeEke */
/* - disabled assertions checks (define #BLIP_ASSERT to re-enable) */ /* - disabled assertions checks (define #BLIP_ASSERT to re-enable) */
/* - fixed multiple time-frames support & removed m->avail */ /* - fixed multiple time-frames support & removed m->avail */
/* - modified blip_read_samples to always output to stereo streams */ /* - added blip_mix_samples function (see blip_buf.h) */
/* - added blip_mix_samples function (see blip_buf.h) */ /* - added stereo buffer support (define #BLIP_MONO to disable) */
/* - added inverted stereo output (define #BLIP_INVERT to enable)*/
#include "blip_buf.h" #include "blip_buf.h"
@ -61,24 +62,32 @@ enum { phase_count = 1 << phase_bits };
enum { delta_bits = 15 }; enum { delta_bits = 15 };
enum { delta_unit = 1 << delta_bits }; enum { delta_unit = 1 << delta_bits };
enum { frac_bits = time_bits - pre_shift }; enum { frac_bits = time_bits - pre_shift };
enum { phase_shift = frac_bits - phase_bits };
/* We could eliminate avail and encode whole samples in offset, but that would /* We could eliminate avail and encode whole samples in offset, but that would
limit the total buffered samples to blip_max_frame. That could only be limit the total buffered samples to blip_max_frame. That could only be
increased by decreasing time_bits, which would reduce resample ratio accuracy. increased by decreasing time_bits, which would reduce resample ratio accuracy.
*/ */
typedef int buf_t;
struct blip_t struct blip_t
{ {
fixed_t factor; fixed_t factor;
fixed_t offset; fixed_t offset;
int size; int size;
#ifdef BLIP_MONO
int integrator; int integrator;
#else
int integrator[2];
buf_t* buffer[2];
#endif
}; };
typedef int buf_t; #ifdef BLIP_MONO
/* probably not totally portable */ /* probably not totally portable */
#define SAMPLES( buf ) ((buf_t*) ((buf) + 1)) #define SAMPLES( blip ) ((buf_t*) ((blip) + 1))
#endif
/* Arithmetic (sign-preserving) right shift */ /* Arithmetic (sign-preserving) right shift */
#define ARITH_SHIFT( n, shift ) \ #define ARITH_SHIFT( n, shift ) \
@ -124,9 +133,23 @@ blip_t* blip_new( int size )
assert( size >= 0 ); assert( size >= 0 );
#endif #endif
#ifdef BLIP_MONO
m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) ); m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) );
#else
m = (blip_t*) malloc( sizeof *m );
#endif
if ( m ) if ( m )
{ {
#ifndef BLIP_MONO
m->buffer[0] = (buf_t*) malloc( (size + buf_extra) * sizeof (buf_t));
m->buffer[1] = (buf_t*) malloc( (size + buf_extra) * sizeof (buf_t));
if ((m->buffer[0] == NULL) || (m->buffer[1] == NULL))
{
blip_delete(m);
return 0;
}
#endif
m->factor = time_unit / blip_max_ratio; m->factor = time_unit / blip_max_ratio;
m->size = size; m->size = size;
blip_clear( m ); blip_clear( m );
@ -141,7 +164,13 @@ void blip_delete( blip_t* m )
{ {
if ( m != NULL ) if ( m != NULL )
{ {
/* Clear fields in case user tries to use after freeing */ #ifndef BLIP_MONO
if (m->buffer[0] != NULL)
free(m->buffer[0]);
if (m->buffer[1] != NULL)
free(m->buffer[1]);
#endif
/* Clear fields in case user tries to use after freeing */
memset( m, 0, sizeof *m ); memset( m, 0, sizeof *m );
free( m ); free( m );
} }
@ -173,16 +202,23 @@ void blip_clear( blip_t* m )
Since we don't know rounding direction, factor/2 accommodates either, Since we don't know rounding direction, factor/2 accommodates either,
with the slight loss of showing an error in half the time. Since for with the slight loss of showing an error in half the time. Since for
a 64-bit factor this is years, the halving isn't a problem. */ a 64-bit factor this is years, the halving isn't a problem. */
m->offset = m->factor / 2; m->offset = m->factor / 2;
#ifdef BLIP_MONO
m->integrator = 0; m->integrator = 0;
memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) ); memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) );
#else
m->integrator[0] = 0;
m->integrator[1] = 0;
memset( m->buffer[0], 0, (m->size + buf_extra) * sizeof (buf_t) );
memset( m->buffer[1], 0, (m->size + buf_extra) * sizeof (buf_t) );
#endif
} }
int blip_clocks_needed( const blip_t* m, int samples ) int blip_clocks_needed( const blip_t* m, int samples )
{ {
fixed_t needed; fixed_t needed;
#ifdef BLIP_ASSERT #ifdef BLIP_ASSERT
/* Fails if buffer can't hold that many more samples */ /* Fails if buffer can't hold that many more samples */
assert( (samples >= 0) && (((m->offset >> time_bits) + samples) <= m->size) ); assert( (samples >= 0) && (((m->offset >> time_bits) + samples) <= m->size) );
@ -191,14 +227,14 @@ int blip_clocks_needed( const blip_t* m, int samples )
needed = (fixed_t) samples * time_unit; needed = (fixed_t) samples * time_unit;
if ( needed < m->offset ) if ( needed < m->offset )
return 0; return 0;
return (needed - m->offset + m->factor - 1) / m->factor; return (needed - m->offset + m->factor - 1) / m->factor;
} }
void blip_end_frame( blip_t* m, unsigned t ) void blip_end_frame( blip_t* m, unsigned t )
{ {
m->offset += t * m->factor; m->offset += t * m->factor;
#ifdef BLIP_ASSERT #ifdef BLIP_ASSERT
/* Fails if buffer size was exceeded */ /* Fails if buffer size was exceeded */
assert( (m->offset >> time_bits) <= m->size ); assert( (m->offset >> time_bits) <= m->size );
@ -212,91 +248,167 @@ int blip_samples_avail( const blip_t* m )
static void remove_samples( blip_t* m, int count ) static void remove_samples( blip_t* m, int count )
{ {
#ifdef BLIP_MONO
buf_t* buf = SAMPLES( m ); buf_t* buf = SAMPLES( m );
int remain = (m->offset >> time_bits) + buf_extra - count; #else
buf_t* buf = m->buffer[0];
#endif
int remain = (m->offset >> time_bits) + buf_extra - count;
m->offset -= count * time_unit; m->offset -= count * time_unit;
memmove( &buf [0], &buf [count], remain * sizeof buf [0] ); memmove( &buf [0], &buf [count], remain * sizeof (buf_t) );
memset( &buf [remain], 0, count * sizeof buf [0] ); memset( &buf [remain], 0, count * sizeof (buf_t) );
#ifndef BLIP_MONO
buf = m->buffer[1];
memmove( &buf [0], &buf [count], remain * sizeof (buf_t) );
memset( &buf [remain], 0, count * sizeof (buf_t) );
#endif
} }
int blip_read_samples( blip_t* m, short out [], int count) int blip_read_samples( blip_t* m, short out [], int count)
{ {
#ifdef BLIP_ASSERT #ifdef BLIP_ASSERT
assert( count >= 0 ); assert( count >= 0 );
if ( count > (m->offset >> time_bits) ) if ( count > (m->offset >> time_bits) )
count = m->offset >> time_bits; count = m->offset >> time_bits;
if ( count ) if ( count )
#endif #endif
{ {
buf_t const* in = SAMPLES( m ); #ifdef BLIP_MONO
buf_t const* end = in + count; buf_t const* in = SAMPLES( m );
int sum = m->integrator; int sum = m->integrator;
#else
buf_t const* in = m->buffer[0];
buf_t const* in2 = m->buffer[1];
int sum = m->integrator[0];
int sum2 = m->integrator[1];
#endif
buf_t const* end = in + count;
do do
{ {
/* Eliminate fraction */ /* Eliminate fraction */
int s = ARITH_SHIFT( sum, delta_bits ); int s = ARITH_SHIFT( sum, delta_bits );
sum += *in++; sum += *in++;
CLAMP( s ); CLAMP( s );
*out = s; *out++ = s;
out += 2;
/* High-pass filter */ /* High-pass filter */
sum -= s << (delta_bits - bass_shift); sum -= s << (delta_bits - bass_shift);
#ifndef BLIP_MONO
/* Eliminate fraction */
s = ARITH_SHIFT( sum2, delta_bits );
sum2 += *in2++;
CLAMP( s );
*out++ = s;
/* High-pass filter */
sum2 -= s << (delta_bits - bass_shift);
#endif
} }
while ( in != end ); while ( in != end );
#ifdef BLIP_MONO
m->integrator = sum; m->integrator = sum;
#else
m->integrator[0] = sum;
m->integrator[1] = sum2;
#endif
remove_samples( m, count ); remove_samples( m, count );
} }
return count; return count;
} }
int blip_mix_samples( blip_t* m, short out [], int count) int blip_mix_samples( blip_t* m1, blip_t* m2, blip_t* m3, short out [], int count)
{ {
#ifdef BLIP_ASSERT #ifdef BLIP_ASSERT
assert( count >= 0 ); assert( count >= 0 );
if ( count > (m->offset >> time_bits) ) if ( count > (m1->offset >> time_bits) )
count = m->offset >> time_bits; count = m1->offset >> time_bits;
if ( count > (m2->offset >> time_bits) )
if ( count ) count = m2->offset >> time_bits;
if ( count > (m3->offset >> time_bits) )
count = m3->offset >> time_bits;
if ( count )
#endif #endif
{ {
buf_t const* in = SAMPLES( m ); buf_t const* end;
buf_t const* end = in + count; buf_t const* in[3];
int sum = m->integrator; #ifdef BLIP_MONO
do int sum = m1->integrator;
{ in[0] = SAMPLES( m1 );
/* Eliminate fraction */ in[1] = SAMPLES( m2 );
int s = ARITH_SHIFT( sum, delta_bits ); in[2] = SAMPLES( m3 );
#else
sum += *in++; int sum = m1->integrator[0];
int sum2 = m1->integrator[1];
/* High-pass filter */ buf_t const* in2[3];
sum -= s << (delta_bits - bass_shift); in[0] = m1->buffer[0];
in[1] = m2->buffer[0];
in[2] = m3->buffer[0];
in2[0] = m1->buffer[1];
in2[1] = m2->buffer[1];
in2[2] = m3->buffer[1];
#endif
/* Add current buffer value */ end = in[0] + count;
s += *out; do
{
CLAMP( s ); /* Eliminate fraction */
int s = ARITH_SHIFT( sum, delta_bits );
*out = s;
out += 2; sum += *in[0]++;
} sum += *in[1]++;
while ( in != end ); sum += *in[2]++;
m->integrator = sum;
CLAMP( s );
remove_samples( m, count );
} *out++ = s;
return count; /* High-pass filter */
sum -= s << (delta_bits - bass_shift);
#ifndef BLIP_MONO
/* Eliminate fraction */
s = ARITH_SHIFT( sum2, delta_bits );
sum2 += *in2[0]++;
sum2 += *in2[1]++;
sum2 += *in2[2]++;
CLAMP( s );
*out++ = s;
/* High-pass filter */
sum2 -= s << (delta_bits - bass_shift);
#endif
}
while ( in[0] != end );
#ifdef BLIP_MONO
m1->integrator = sum;
#else
m1->integrator[0] = sum;
m1->integrator[1] = sum2;
#endif
remove_samples( m1, count );
remove_samples( m2, count );
remove_samples( m3, count );
}
return count;
} }
/* Things that didn't help performance on x86: /* Things that didn't help performance on x86:
@ -348,12 +460,180 @@ possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient.
And by having pre_shift 32, a 32-bit platform can easily do the shift by And by having pre_shift 32, a 32-bit platform can easily do the shift by
simply ignoring the low half. */ simply ignoring the low half. */
#ifndef BLIP_MONO
void blip_add_delta( blip_t* m, unsigned time, int delta_l, int delta_r )
{
if (delta_l | delta_r)
{
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
int phase = fixed >> phase_shift & (phase_count - 1);
short const* in = bl_step [phase];
short const* rev = bl_step [phase_count - phase];
int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1);
int pos = fixed >> frac_bits;
#ifdef BLIP_INVERT
buf_t* out_l = m->buffer[1] + pos;
buf_t* out_r = m->buffer[0] + pos;
#else
buf_t* out_l = m->buffer[0] + pos;
buf_t* out_r = m->buffer[1] + pos;
#endif
int delta;
#ifdef BLIP_ASSERT
/* Fails if buffer size was exceeded */
assert( pos <= m->size + end_frame_extra );
#endif
if (delta_l == delta_r)
{
buf_t out;
delta = (delta_l * interp) >> delta_bits;
delta_l -= delta;
out = in[0]*delta_l + in[half_width+0]*delta;
out_l[0] += out;
out_r[0] += out;
out = in[1]*delta_l + in[half_width+1]*delta;
out_l[1] += out;
out_r[1] += out;
out = in[2]*delta_l + in[half_width+2]*delta;
out_l[2] += out;
out_r[2] += out;
out = in[3]*delta_l + in[half_width+3]*delta;
out_l[3] += out;
out_r[3] += out;
out = in[4]*delta_l + in[half_width+4]*delta;
out_l[4] += out;
out_r[4] += out;
out = in[5]*delta_l + in[half_width+5]*delta;
out_l[5] += out;
out_r[5] += out;
out = in[6]*delta_l + in[half_width+6]*delta;
out_l[6] += out;
out_r[6] += out;
out = in[7]*delta_l + in[half_width+7]*delta;
out_l[7] += out;
out_r[7] += out;
out = rev[7]*delta_l + rev[7-half_width]*delta;
out_l[8] += out;
out_r[8] += out;
out = rev[6]*delta_l + rev[6-half_width]*delta;
out_l[9] += out;
out_r[9] += out;
out = rev[5]*delta_l + rev[5-half_width]*delta;
out_l[10] += out;
out_r[10] += out;
out = rev[4]*delta_l + rev[4-half_width]*delta;
out_l[11] += out;
out_r[11] += out;
out = rev[3]*delta_l + rev[3-half_width]*delta;
out_l[12] += out;
out_r[12] += out;
out = rev[2]*delta_l + rev[2-half_width]*delta;
out_l[13] += out;
out_r[13] += out;
out = rev[1]*delta_l + rev[1-half_width]*delta;
out_l[14] += out;
out_r[14] += out;
out = rev[0]*delta_l + rev[0-half_width]*delta;
out_l[15] += out;
out_r[15] += out;
}
else
{
delta = (delta_l * interp) >> delta_bits;
delta_l -= delta;
out_l [0] += in[0]*delta_l + in[half_width+0]*delta;
out_l [1] += in[1]*delta_l + in[half_width+1]*delta;
out_l [2] += in[2]*delta_l + in[half_width+2]*delta;
out_l [3] += in[3]*delta_l + in[half_width+3]*delta;
out_l [4] += in[4]*delta_l + in[half_width+4]*delta;
out_l [5] += in[5]*delta_l + in[half_width+5]*delta;
out_l [6] += in[6]*delta_l + in[half_width+6]*delta;
out_l [7] += in[7]*delta_l + in[half_width+7]*delta;
out_l [8] += rev[7]*delta_l + rev[7-half_width]*delta;
out_l [9] += rev[6]*delta_l + rev[6-half_width]*delta;
out_l [10] += rev[5]*delta_l + rev[5-half_width]*delta;
out_l [11] += rev[4]*delta_l + rev[4-half_width]*delta;
out_l [12] += rev[3]*delta_l + rev[3-half_width]*delta;
out_l [13] += rev[2]*delta_l + rev[2-half_width]*delta;
out_l [14] += rev[1]*delta_l + rev[1-half_width]*delta;
out_l [15] += rev[0]*delta_l + rev[0-half_width]*delta;
delta = (delta_r * interp) >> delta_bits;
delta_r -= delta;
out_r [0] += in[0]*delta_r + in[half_width+0]*delta;
out_r [1] += in[1]*delta_r + in[half_width+1]*delta;
out_r [2] += in[2]*delta_r + in[half_width+2]*delta;
out_r [3] += in[3]*delta_r + in[half_width+3]*delta;
out_r [4] += in[4]*delta_r + in[half_width+4]*delta;
out_r [5] += in[5]*delta_r + in[half_width+5]*delta;
out_r [6] += in[6]*delta_r + in[half_width+6]*delta;
out_r [7] += in[7]*delta_r + in[half_width+7]*delta;
out_r [8] += rev[7]*delta_r + rev[7-half_width]*delta;
out_r [9] += rev[6]*delta_r + rev[6-half_width]*delta;
out_r [10] += rev[5]*delta_r + rev[5-half_width]*delta;
out_r [11] += rev[4]*delta_r + rev[4-half_width]*delta;
out_r [12] += rev[3]*delta_r + rev[3-half_width]*delta;
out_r [13] += rev[2]*delta_r + rev[2-half_width]*delta;
out_r [14] += rev[1]*delta_r + rev[1-half_width]*delta;
out_r [15] += rev[0]*delta_r + rev[0-half_width]*delta;
}
}
}
void blip_add_delta_fast( blip_t* m, unsigned time, int delta_l, int delta_r )
{
if (delta_l | delta_r)
{
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1);
int pos = fixed >> frac_bits;
#ifdef STEREO_INVERT
buf_t* out_l = m->buffer[1] + pos;
buf_t* out_r = m->buffer[0] + pos;
#else
buf_t* out_l = m->buffer[0] + pos;
buf_t* out_r = m->buffer[1] + pos;
#endif
int delta = delta_l * interp;
#ifdef BLIP_ASSERT
/* Fails if buffer size was exceeded */
assert( pos <= m->size + end_frame_extra );
#endif
if (delta_l == delta_r)
{
delta_l = delta_l * delta_unit - delta;
out_l[7] += delta_l;
out_l[8] += delta;
out_r[7] += delta_l;
out_r[8] += delta;
}
else
{
out_l[7] += delta_l * delta_unit - delta;
out_l[8] += delta;
delta = delta_r * interp;
out_r[7] += delta_r * delta_unit - delta;
out_r[8] += delta;
}
}
}
#else
void blip_add_delta( blip_t* m, unsigned time, int delta ) void blip_add_delta( blip_t* m, unsigned time, int delta )
{ {
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift); unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
buf_t* out = SAMPLES( m ) + (fixed >> frac_bits); buf_t* out = SAMPLES( m ) + (fixed >> frac_bits);
int const phase_shift = frac_bits - phase_bits;
int phase = fixed >> phase_shift & (phase_count - 1); int phase = fixed >> phase_shift & (phase_count - 1);
short const* in = bl_step [phase]; short const* in = bl_step [phase];
short const* rev = bl_step [phase_count - phase]; short const* rev = bl_step [phase_count - phase];
@ -403,3 +683,4 @@ void blip_add_delta_fast( blip_t* m, unsigned time, int delta )
out [7] += delta * delta_unit - delta2; out [7] += delta * delta_unit - delta2;
out [8] += delta2; out [8] += delta2;
} }
#endif

View File

@ -28,12 +28,24 @@ blip_max_ratio = 1 << 20 };
/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */ /** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
void blip_clear( blip_t* ); void blip_clear( blip_t* );
#ifndef BLIP_MONO
/** Adds positive/negative deltas into stereo buffers at specified clock time. */
void blip_add_delta( blip_t*, unsigned time, int delta_l, int delta_r );
/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta_l, int delta_r );
#else
/** Adds positive/negative delta into buffer at specified clock time. */ /** Adds positive/negative delta into buffer at specified clock time. */
void blip_add_delta( blip_t*, unsigned int clock_time, int delta ); void blip_add_delta( blip_t*, unsigned int clock_time, int delta );
/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */ /** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta ); void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta );
#endif
/** Length of time frame, in clocks, needed to make sample_count additional /** Length of time frame, in clocks, needed to make sample_count additional
samples available. */ samples available. */
int blip_clocks_needed( const blip_t*, int sample_count ); int blip_clocks_needed( const blip_t*, int sample_count );
@ -56,9 +68,8 @@ element of 'out', allowing easy interleaving of two buffers into a stereo sample
stream. Outputs 16-bit signed samples. Returns number of samples actually read. */ stream. Outputs 16-bit signed samples. Returns number of samples actually read. */
int blip_read_samples( blip_t*, short out [], int count); int blip_read_samples( blip_t*, short out [], int count);
/* Same as above function except sample is added to output buffer previous value */ /* Same as above function except sample is mixed from three blip buffers source */
/* This allows easy mixing of different blip buffers into a single output stream */ int blip_mix_samples( blip_t* m1, blip_t* m2, blip_t* m3, short out [], int count);
int blip_mix_samples( blip_t* m, short out [], int count);
/** Frees buffer. No effect if NULL is passed. */ /** Frees buffer. No effect if NULL is passed. */
void blip_delete( blip_t* ); void blip_delete( blip_t* );

591
core/sound/psg.c Normal file
View File

@ -0,0 +1,591 @@
/***************************************************************************************
* Genesis Plus
* PSG sound chip (SN76489A compatible)
*
* Support for discrete chip & integrated (ASIC) clones
*
* Noise implementation based on http://www.smspower.org/Development/SN76489#NoiseChannel
*
* Copyright (C) 2016 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#include "blip_buf.h"
/* internal clock = input clock : 16 = (master clock : 15) : 16 */
#define PSG_MCYCLES_RATIO (15*16)
/* maximal channel output (roughly adjusted to match VA4 MD1 PSG/FM balance with 1.5x amplification of PSG output) */
#define PSG_MAX_VOLUME 2800
static const uint8 noiseShiftWidth[2] = {14,15};
static const uint8 noiseBitMask[2] = {0x6,0x9};
static const uint8 noiseFeedback[10] = {0,1,1,0,1,0,0,1,1,0};
static const uint16 chanVolume[16] = {
PSG_MAX_VOLUME, /* MAX */
PSG_MAX_VOLUME * 0.794328234, /* -2dB */
PSG_MAX_VOLUME * 0.630957344, /* -4dB */
PSG_MAX_VOLUME * 0.501187233, /* -6dB */
PSG_MAX_VOLUME * 0.398107170, /* -8dB */
PSG_MAX_VOLUME * 0.316227766, /* -10dB */
PSG_MAX_VOLUME * 0.251188643, /* -12dB */
PSG_MAX_VOLUME * 0.199526231, /* -14dB */
PSG_MAX_VOLUME * 0.158489319, /* -16dB */
PSG_MAX_VOLUME * 0.125892541, /* -18dB */
PSG_MAX_VOLUME * 0.1, /* -20dB */
PSG_MAX_VOLUME * 0.079432823, /* -22dB */
PSG_MAX_VOLUME * 0.063095734, /* -24dB */
PSG_MAX_VOLUME * 0.050118723, /* -26dB */
PSG_MAX_VOLUME * 0.039810717, /* -28dB */
0 /* OFF */
};
static struct
{
int clocks;
int latch;
int noiseShiftValue;
int noiseShiftWidth;
int noiseBitMask;
int regs[8];
int freqInc[4];
int freqCounter[4];
int polarity[4];
int chanDelta[4][2];
int chanOut[4][2];
int chanAmp[4][2];
} psg;
static void psg_update(unsigned int clocks);
void psg_init(PSG_TYPE type)
{
int i;
/* Initialize stereo amplification (default) */
for (i=0; i<4; i++)
{
psg.chanAmp[i][0] = 100;
psg.chanAmp[i][1] = 100;
}
/* Initialize Noise LSFR type */
psg.noiseShiftWidth = noiseShiftWidth[type];
psg.noiseBitMask = noiseBitMask[type];
}
void psg_reset()
{
int i;
/* power-on state (verified on 315-5313A & 315-5660 integrated version only) */
for (i=0; i<4; i++)
{
psg.regs[i*2] = 0;
psg.regs[i*2+1] = 0;
psg.freqInc[i] = (i < 3) ? (1 * PSG_MCYCLES_RATIO) : (16 * PSG_MCYCLES_RATIO);
psg.freqCounter[i] = 0;
psg.polarity[i] = -1;
psg.chanDelta[i][0] = 0;
psg.chanDelta[i][1] = 0;
psg.chanOut[i][0] = 0;
psg.chanOut[i][1] = 0;
}
/* noise attenuation register is latched on power-on (verified on 315-5313A & 315-5660 integrated version only) */
psg.latch = 7;
/* reset noise shift register */
psg.noiseShiftValue = 1 << psg.noiseShiftWidth;
/* reset internal M-cycles clock counter */
psg.clocks = 0;
}
int psg_context_save(uint8 *state)
{
int bufferptr = 0;
save_param(&psg.clocks,sizeof(psg.clocks));
save_param(&psg.latch,sizeof(psg.latch));
save_param(&psg.noiseShiftValue,sizeof(psg.noiseShiftValue));
save_param(psg.regs,sizeof(psg.regs));
save_param(psg.freqInc,sizeof(psg.freqInc));
save_param(psg.freqCounter,sizeof(psg.freqCounter));
save_param(psg.polarity,sizeof(psg.polarity));
save_param(psg.chanDelta,sizeof(psg.chanDelta));
save_param(psg.chanOut,sizeof(psg.chanOut));
return bufferptr;
}
int psg_context_load(uint8 *state)
{
int chanOut[4][2], delta[2];
int i, bufferptr = 0;
/* get current tone channels output */
for (i=0; i<3; i++)
{
if (psg.polarity[i] > 0)
{
chanOut[i][0] = psg.chanOut[i][0];
chanOut[i][1] = psg.chanOut[i][1];
}
else
{
chanOut[i][0] = 0;
chanOut[i][1] = 0;
}
}
/* get current noise channel output */
if (psg.noiseShiftValue & 1)
{
chanOut[3][0] = psg.chanOut[3][0];
chanOut[3][1] = psg.chanOut[3][1];
}
else
{
chanOut[3][0] = 0;
chanOut[3][1] = 0;
}
load_param(&psg.clocks,sizeof(psg.clocks));
load_param(&psg.latch,sizeof(psg.latch));
load_param(&psg.noiseShiftValue,sizeof(psg.noiseShiftValue));
load_param(psg.regs,sizeof(psg.regs));
load_param(psg.freqInc,sizeof(psg.freqInc));
load_param(psg.freqCounter,sizeof(psg.freqCounter));
load_param(psg.polarity,sizeof(psg.polarity));
load_param(psg.chanDelta,sizeof(psg.chanDelta));
load_param(psg.chanOut,sizeof(psg.chanOut));
/* apply any pending channel volume variation */
for (i=0; i<4; i++)
{
psg.chanOut[i][0] += psg.chanDelta[i][0];
psg.chanOut[i][1] += psg.chanDelta[i][1];
psg.chanDelta[i][0] = 0;
psg.chanDelta[i][1] = 0;
}
/* calculate noise channel output variations */
if (psg.noiseShiftValue & 1)
{
/* channel output is high */
delta[0] = psg.chanOut[3][0] - chanOut[3][0];
delta[1] = psg.chanOut[3][0] - chanOut[3][1];
}
else
{
/* channel output is low */
delta[0] = -chanOut[3][0];
delta[1] = -chanOut[3][1];
}
/* add tone channels output variations */
for (i=0; i<3; i++)
{
if (psg.polarity[i] > 0)
{
/* channel output is high */
delta[0] += (psg.chanOut[i][0] - chanOut[i][0]);
delta[1] += (psg.chanOut[i][0] - chanOut[i][1]);
}
else
{
/* channel output is low */
delta[0] -= chanOut[i][0];
delta[1] -= chanOut[i][1];
}
}
/* update mixed channels output */
if (config.hq_psg)
{
blip_add_delta(snd.blips[0], psg.clocks, delta[0], delta[1]);
}
else
{
blip_add_delta_fast(snd.blips[0], psg.clocks, delta[0], delta[1]);
}
return bufferptr;
}
void psg_write(unsigned int clocks, unsigned int data)
{
int index;
/* PSG chip synchronization */
if (clocks > psg.clocks)
{
/* run PSG chip until current timestamp */
psg_update(clocks);
/* update internal M-cycles clock counter */
psg.clocks += ((clocks - psg.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;
}
if (data & 0x80)
{
/* latch register index (1xxx----) */
psg.latch = index = (data >> 4) & 0x07;
}
else
{
/* restore latched register index */
index= psg.latch;
}
switch (index)
{
case 0:
case 2:
case 4: /* Tone channels frequency */
{
/* recalculate frequency register value */
if (data & 0x80)
{
/* update 10-bit register LSB (1---xxxx) */
data = (psg.regs[index] & 0x3f0) | (data & 0x0f);
}
else
{
/* update 10-bit register MSB (0-xxxxxx) */
data = (psg.regs[index] & 0x00f) | ((data & 0x3f) << 4);
}
/* update channel M-cycle counter increment */
if (data)
{
psg.freqInc[index>>1] = data * PSG_MCYCLES_RATIO;
}
else
{
/* zero value behaves the same as a value of 1 (verified on integrated version only) */
psg.freqInc[index>>1] = PSG_MCYCLES_RATIO;
}
/* update noise channel counter increment if required */
if ((index == 4) && ((psg.regs[6] & 0x03) == 0x03))
{
psg.freqInc[3] = psg.freqInc[2];
}
break;
}
case 6: /* Noise control */
{
/* noise signal generator frequency (----?xxx) */
int noiseFreq = (data & 0x03);
if (noiseFreq == 0x03)
{
/* noise generator is controlled by tone channel #3 generator */
psg.freqInc[3] = psg.freqInc[2];
psg.freqCounter[3] = psg.freqCounter[2];
}
else
{
/* noise generator is running at separate frequency */
psg.freqInc[3] = (0x10 << noiseFreq) * PSG_MCYCLES_RATIO;
}
/* reset shift register value */
psg.noiseShiftValue = 1 << psg.noiseShiftWidth;;
break;
}
case 7: /* Noise channel attenuation */
{
/* convert 4-bit attenuation value (----xxxx) to 16-bit volume value */
data = chanVolume[data & 0x0f];
/* check noise shift register output */
if (psg.noiseShiftValue & 1)
{
/* channel output is high, volume variation will be applied at next internal cycle update */
psg.chanDelta[3][0] = ((data * psg.chanAmp[3][0]) / 100) - psg.chanOut[3][0];
psg.chanDelta[3][1] = ((data * psg.chanAmp[3][1]) / 100) - psg.chanOut[3][1];
}
else
{
/* channel output is low, volume variation will be applied at next transition */
psg.chanOut[3][0] = (data * psg.chanAmp[3][0]) / 100;
psg.chanOut[3][1] = (data * psg.chanAmp[3][1]) / 100;
}
break;
}
default: /* Tone channels attenuation */
{
/* channel number (0-2) */
int i = index >> 1;
/* convert 4-bit attenuation value (----xxxx) to 16-bit volume value */
data = chanVolume[data & 0x0f];
/* check tone generator polarity */
if (psg.polarity[i] > 0)
{
/* channel output is high, volume variation will be applied at next internal cycle update */
psg.chanDelta[i][0] = ((data * psg.chanAmp[i][0]) / 100) - psg.chanOut[i][0];
psg.chanDelta[i][1] = ((data * psg.chanAmp[i][1]) / 100) - psg.chanOut[i][1];
}
else
{
/* channel output is low, volume variation will be applied at next transition */
psg.chanOut[i][0] = (data * psg.chanAmp[i][0]) / 100;
psg.chanOut[i][1] = (data * psg.chanAmp[i][1]) / 100;
}
break;
}
}
/* save register value */
psg.regs[index] = data;
}
void psg_config(unsigned int clocks, unsigned int preamp, unsigned int panning)
{
int i;
/* PSG chip synchronization */
if (clocks > psg.clocks)
{
/* run PSG chip until current timestamp */
psg_update(clocks);
/* update internal M-cycles clock counter */
psg.clocks += ((clocks - psg.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;
}
for (i=0; i<4; i++)
{
/* channel internal volume */
int volume = psg.regs[i*2+1];
/* update channel stereo amplification */
psg.chanAmp[i][0] = preamp * ((panning >> (i + 4)) & 1);
psg.chanAmp[i][1] = preamp * ((panning >> (i + 0)) & 1);
/* tone channels */
if (i < 3)
{
/* check tone generator polarity */
if (psg.polarity[i] > 0)
{
/* channel output is high, volume variation will be applied at next internal cycle update */
psg.chanDelta[i][0] = ((volume * psg.chanAmp[i][0]) / 100) - psg.chanOut[i][0];
psg.chanDelta[i][1] = ((volume * psg.chanAmp[i][1]) / 100) - psg.chanOut[i][1];
}
else
{
/* channel output is low, volume variation will be applied at next transition*/
psg.chanOut[i][0] = (volume * psg.chanAmp[i][0]) / 100;
psg.chanOut[i][1] = (volume * psg.chanAmp[i][1]) / 100;
}
}
/* noise channel */
else
{
/* check noise shift register output */
if (psg.noiseShiftValue & 1)
{
/* channel output is high, volume variation will be applied at next internal cycle update */
psg.chanDelta[3][0] = ((volume * psg.chanAmp[3][0]) / 100) - psg.chanOut[3][0];
psg.chanDelta[3][1] = ((volume * psg.chanAmp[3][1]) / 100) - psg.chanOut[3][1];
}
else
{
/* channel output is low, volume variation will be applied at next transition */
psg.chanOut[3][0] = (volume * psg.chanAmp[3][0]) / 100;
psg.chanOut[3][1] = (volume * psg.chanAmp[3][1]) / 100;
}
}
}
}
void psg_end_frame(unsigned int clocks)
{
int i;
if (clocks > psg.clocks)
{
/* run PSG chip until current timestamp */
psg_update(clocks);
/* update internal M-cycles clock counter */
psg.clocks += ((clocks - psg.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;
}
/* adjust internal M-cycles clock counter for next frame */
psg.clocks -= clocks;
/* adjust channels time counters for next frame */
for (i=0; i<4; ++i)
{
psg.freqCounter[i] -= clocks;
}
}
static void psg_update(unsigned int clocks)
{
int i, timestamp, polarity;
for (i=0; i<4; i++)
{
/* apply any pending channel volume variations */
if (psg.chanDelta[i][0] | psg.chanDelta[i][1])
{
/* update channel output */
if (config.hq_psg)
{
blip_add_delta(snd.blips[0], psg.clocks, psg.chanDelta[i][0], psg.chanDelta[i][1]);
}
else
{
blip_add_delta_fast(snd.blips[0], psg.clocks, psg.chanDelta[i][0], psg.chanDelta[i][1]);
}
/* update channel volume */
psg.chanOut[i][0] += psg.chanDelta[i][0];
psg.chanOut[i][1] += psg.chanDelta[i][1];
/* clear pending channel volume variations */
psg.chanDelta[i][0] = 0;
psg.chanDelta[i][1] = 0;
}
/* timestamp of next transition */
timestamp = psg.freqCounter[i];
/* current channel generator polarity */
polarity = psg.polarity[i];
/* Tone channels */
if (i < 3)
{
/* process all transitions occurring until current clock timestamp */
while (timestamp < clocks)
{
/* invert tone generator polarity */
polarity = -polarity;
/* update channel output */
if (config.hq_psg)
{
blip_add_delta(snd.blips[0], timestamp, polarity*psg.chanOut[i][0], polarity*psg.chanOut[i][1]);
}
else
{
blip_add_delta_fast(snd.blips[0], timestamp, polarity*psg.chanOut[i][0], polarity*psg.chanOut[i][1]);
}
/* timestamp of next transition */
timestamp += psg.freqInc[i];
}
}
/* Noise channel */
else
{
/* current noise shift register value */
int shiftValue = psg.noiseShiftValue;
/* process all transitions occurring until current clock timestamp */
while (timestamp < clocks)
{
/* invert noise generator polarity */
polarity = -polarity;
/* noise register is shifted on positive edge only */
if (polarity > 0)
{
/* current shift register output */
int shiftOutput = shiftValue & 0x01;
/* White noise (----1xxx) */
if (psg.regs[6] & 0x04)
{
/* shift and apply XOR feedback network */
shiftValue = (shiftValue >> 1) | (noiseFeedback[shiftValue & psg.noiseBitMask] << psg.noiseShiftWidth);
}
/* Periodic noise (----0xxx) */
else
{
/* shift and feedback current output */
shiftValue = (shiftValue >> 1) | (shiftOutput << psg.noiseShiftWidth);
}
/* shift register output variation */
shiftOutput = (shiftValue & 0x1) - shiftOutput;
/* update noise channel output */
if (config.hq_psg)
{
blip_add_delta(snd.blips[0], timestamp, shiftOutput*psg.chanOut[3][0], shiftOutput*psg.chanOut[3][1]);
}
else
{
blip_add_delta_fast(snd.blips[0], timestamp, shiftOutput*psg.chanOut[3][0], shiftOutput*psg.chanOut[3][1]);
}
}
/* timestamp of next transition */
timestamp += psg.freqInc[3];
}
/* save shift register value */
psg.noiseShiftValue = shiftValue;
}
/* save timestamp of next transition */
psg.freqCounter[i] = timestamp;
/* save channel generator polarity */
psg.polarity[i] = polarity;
}
}

60
core/sound/psg.h Normal file
View File

@ -0,0 +1,60 @@
/***************************************************************************************
* Genesis Plus
* PSG sound chip (SN76489A compatible)
*
* Support for discrete chip & integrated (ASIC) clones
*
* Noise implementation based on http://www.smspower.org/Development/SN76489#NoiseChannel
*
* Copyright (C) 2016 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _PSG_H_
#define _PSG_H_
typedef enum {
PSG_DISCRETE,
PSG_INTEGRATED
} PSG_TYPE;
/* Function prototypes */
extern void psg_init(PSG_TYPE type);
extern void psg_reset(void);
extern int psg_context_save(uint8 *state);
extern int psg_context_load(uint8 *state);
extern void psg_write(unsigned int clocks, unsigned int data);
extern void psg_config(unsigned int clocks, unsigned int preamp, unsigned int panning);
extern void psg_end_frame(unsigned int clocks);
#endif /* _PSG_H_ */

View File

@ -1,446 +0,0 @@
/*
SN76489 emulation
by Maxim in 2001 and 2002
converted from my original Delphi implementation
I'm a C newbie so I'm sure there are loads of stupid things
in here which I'll come back to some day and redo
Includes:
- Super-high quality tone channel "oversampling" by calculating fractional positions on transitions
- Noise output pattern reverse engineered from actual SMS output
- Volume levels taken from actual SMS output
07/08/04 Charles MacDonald
Modified for use with SMS Plus:
- Added support for multiple PSG chips.
- Added reset/config/update routines.
- Added context management routines.
- Removed SN76489_GetValues().
- Removed some unused variables.
25/04/07 Eke-Eke (Genesis Plus GX)
- Removed stereo GG support (unused)
- Made SN76489_Update outputs 16bits mono samples
- Replaced volume table with VGM plugin's one
05/01/09 Eke-Eke (Genesis Plus GX)
- Modified Cut-Off frequency (according to Steve Snake: http://www.smspower.org/forums/viewtopic.php?t=1746)
24/08/10 Eke-Eke (Genesis Plus GX)
- Removed multichip support (unused)
- Removed alternate volume table, panning & mute support (unused)
- Removed configurable Feedback and Shift Register Width (always use Sega ones)
- Added linear resampling using Blip Buffer (based on Blargg's implementation: http://www.smspower.org/forums/viewtopic.php?t=11376)
01/09/12 Eke-Eke (Genesis Plus GX)
- Added generic Blip-Buffer support internally, using common Master Clock as timebase
- Re-added stereo GG support
- Re-added configurable Feedback and Shift Register Width
- Rewrote core with various optimizations
*/
#include "shared.h"
#define PSG_MCYCLES_RATIO (16 * 15)
/* Initial state of shift register */
#define NoiseInitialState 0x8000
/* Value below which PSG does not output */
/*#define PSG_CUTOFF 0x6*/
#define PSG_CUTOFF 0x1
/* original Texas Instruments TMS SN76489AN (rev. A) used in SG-1000, SC-3000H & SF-7000 computers */
#define FB_DISCRETE 0x0006
#define SRW_DISCRETE 15
/* SN76489AN clone integrated in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */
#define FB_SEGAVDP 0x0009
#define SRW_SEGAVDP 16
typedef struct
{
/* Configuration */
int PreAmp[4][2]; /* stereo channels pre-amplification ratio (%) */
int NoiseFeedback;
int SRWidth;
/* PSG registers: */
int Registers[8]; /* Tone, vol x4 */
int LatchedRegister;
int NoiseShiftRegister;
int NoiseFreq; /* Noise channel signal generator frequency */
/* Output calculation variables */
int ToneFreqVals[4]; /* Frequency register values (counters) */
int ToneFreqPos[4]; /* Frequency channel flip-flops */
int Channel[4][2]; /* current amplitude of each (stereo) channel */
int ChanOut[4][2]; /* current output value of each (stereo) channel */
/* Internal M-clock counter */
unsigned long clocks;
} SN76489_Context;
static const uint16 PSGVolumeValues[16] =
{
/* These values are taken from a real SMS2's output */
/*{892,892,892,760,623,497,404,323,257,198,159,123,96,75,60,0}, */
/* I can't remember why 892... :P some scaling I did at some point */
/* these values are true volumes for 2dB drops at each step (multiply previous by 10^-0.1) */
1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0
};
static SN76489_Context SN76489;
void SN76489_Init(int type)
{
int i;
for (i=0; i<4; i++)
{
SN76489.PreAmp[i][0] = 100;
SN76489.PreAmp[i][1] = 100;
}
if (type == SN_DISCRETE)
{
SN76489.NoiseFeedback = FB_DISCRETE;
SN76489.SRWidth = SRW_DISCRETE;
}
else
{
SN76489.NoiseFeedback = FB_SEGAVDP;
SN76489.SRWidth = SRW_SEGAVDP;
}
}
void SN76489_Reset()
{
int i;
for(i = 0; i <= 3; i++)
{
/* Initialise PSG state */
SN76489.Registers[2*i] = 1; /* tone freq=1 */
SN76489.Registers[2*i+1] = 0xf; /* vol=off */
/* Set counters to 0 */
SN76489.ToneFreqVals[i] = 0;
/* Set flip-flops to 1 */
SN76489.ToneFreqPos[i] = 1;
/* Clear stereo channels amplitude */
SN76489.Channel[i][0] = 0;
SN76489.Channel[i][1] = 0;
/* Clear stereo channel outputs in delta buffer */
SN76489.ChanOut[i][0] = 0;
SN76489.ChanOut[i][1] = 0;
}
/* Initialise latched register index */
SN76489.LatchedRegister = 0;
/* Initialise noise generator */
SN76489.NoiseShiftRegister=NoiseInitialState;
SN76489.NoiseFreq = 0x10;
/* Reset internal M-cycle counter */
SN76489.clocks = 0;
}
void *SN76489_GetContextPtr(void)
{
return (uint8 *)&SN76489;
}
int SN76489_GetContextSize(void)
{
return sizeof(SN76489_Context);
}
/* Updates tone amplitude in delta buffer. Call whenever amplitude might have changed. */
INLINE void UpdateToneAmplitude(int i, int time)
{
int delta;
/* left output */
delta = (SN76489.Channel[i][0] * SN76489.ToneFreqPos[i]) - SN76489.ChanOut[i][0];
if (delta != 0)
{
SN76489.ChanOut[i][0] += delta;
blip_add_delta_fast(snd.blips[0][0], time, delta);
}
/* right output */
delta = (SN76489.Channel[i][1] * SN76489.ToneFreqPos[i]) - SN76489.ChanOut[i][1];
if (delta != 0)
{
SN76489.ChanOut[i][1] += delta;
blip_add_delta_fast(snd.blips[0][1], time, delta);
}
}
/* Updates noise amplitude in delta buffer. Call whenever amplitude might have changed. */
INLINE void UpdateNoiseAmplitude(int time)
{
int delta;
/* left output */
delta = (SN76489.Channel[3][0] * ( SN76489.NoiseShiftRegister & 0x1 )) - SN76489.ChanOut[3][0];
if (delta != 0)
{
SN76489.ChanOut[3][0] += delta;
blip_add_delta_fast(snd.blips[0][0], time, delta);
}
/* right output */
delta = (SN76489.Channel[3][1] * ( SN76489.NoiseShiftRegister & 0x1 )) - SN76489.ChanOut[3][1];
if (delta != 0)
{
SN76489.ChanOut[3][1] += delta;
blip_add_delta_fast(snd.blips[0][1], time, delta);
}
}
/* Runs tone channel for clock_length clocks */
static void RunTone(int i, int clocks)
{
int time;
/* Update in case a register changed etc. */
UpdateToneAmplitude(i, SN76489.clocks);
/* Time of next transition */
time = SN76489.ToneFreqVals[i];
/* Process any transitions that occur within clocks we're running */
while (time < clocks)
{
if (SN76489.Registers[i*2]>PSG_CUTOFF) {
/* Flip the flip-flop */
SN76489.ToneFreqPos[i] = -SN76489.ToneFreqPos[i];
} else {
/* stuck value */
SN76489.ToneFreqPos[i] = 1;
}
UpdateToneAmplitude(i, time);
/* Advance to time of next transition */
time += SN76489.Registers[i*2] * PSG_MCYCLES_RATIO;
}
/* Update channel tone counter */
SN76489.ToneFreqVals[i] = time;
}
/* Runs noise channel for clock_length clocks */
static void RunNoise(int clocks)
{
int time;
/* Noise channel: match to tone2 if in slave mode */
int NoiseFreq = SN76489.NoiseFreq;
if (NoiseFreq == 0x80)
{
NoiseFreq = SN76489.Registers[2*2];
SN76489.ToneFreqVals[3] = SN76489.ToneFreqVals[2];
}
/* Update in case a register changed etc. */
UpdateNoiseAmplitude(SN76489.clocks);
/* Time of next transition */
time = SN76489.ToneFreqVals[3];
/* Process any transitions that occur within clocks we're running */
while (time < clocks)
{
/* Flip the flip-flop */
SN76489.ToneFreqPos[3] = -SN76489.ToneFreqPos[3];
if (SN76489.ToneFreqPos[3] == 1)
{
/* On the positive edge of the square wave (only once per cycle) */
int Feedback = SN76489.NoiseShiftRegister;
if ( SN76489.Registers[6] & 0x4 )
{
/* White noise */
/* Calculate parity of fed-back bits for feedback */
/* Do some optimised calculations for common (known) feedback values */
/* If two bits fed back, I can do Feedback=(nsr & fb) && (nsr & fb ^ fb) */
/* since that's (one or more bits set) && (not all bits set) */
Feedback = ((Feedback & SN76489.NoiseFeedback) && ((Feedback & SN76489.NoiseFeedback) ^ SN76489.NoiseFeedback));
}
else /* Periodic noise */
Feedback = Feedback & 1;
SN76489.NoiseShiftRegister = (SN76489.NoiseShiftRegister >> 1) | (Feedback << (SN76489.SRWidth - 1));
UpdateNoiseAmplitude(time);
}
/* Advance to time of next transition */
time += NoiseFreq * PSG_MCYCLES_RATIO;
}
/* Update channel tone counter */
SN76489.ToneFreqVals[3] = time;
}
static void SN76489_RunUntil(unsigned int clocks)
{
int i;
/* Run noise first, since it might use current value of third tone frequency counter */
RunNoise(clocks);
/* Run tone channels */
for (i=0; i<3; ++i)
{
RunTone(i, clocks);
}
}
void SN76489_Config(unsigned int clocks, int preAmp, int boostNoise, int stereo)
{
int i;
/* cycle-accurate Game Gear stereo */
if (clocks > SN76489.clocks)
{
/* Run chip until current timestamp */
SN76489_RunUntil(clocks);
/* Update internal M-cycle counter */
SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;
}
for (i=0; i<4; i++)
{
/* stereo channel pre-amplification */
SN76489.PreAmp[i][0] = preAmp * ((stereo >> (i + 4)) & 1);
SN76489.PreAmp[i][1] = preAmp * ((stereo >> (i + 0)) & 1);
/* noise channel boost */
if (i == 3)
{
SN76489.PreAmp[3][0] = SN76489.PreAmp[3][0] << boostNoise;
SN76489.PreAmp[3][1] = SN76489.PreAmp[3][1] << boostNoise;
}
/* update stereo channel amplitude */
SN76489.Channel[i][0]= (PSGVolumeValues[SN76489.Registers[i*2 + 1]] * SN76489.PreAmp[i][0]) / 100;
SN76489.Channel[i][1]= (PSGVolumeValues[SN76489.Registers[i*2 + 1]] * SN76489.PreAmp[i][1]) / 100;
}
}
void SN76489_Update(unsigned int clocks)
{
int i;
if (clocks > SN76489.clocks)
{
/* Run chip until current timestamp */
SN76489_RunUntil(clocks);
/* Update internal M-cycle counter */
SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;
}
/* Adjust internal M-cycle counter for next frame */
SN76489.clocks -= clocks;
/* Adjust channel time counters for new frame */
for (i=0; i<4; ++i)
{
SN76489.ToneFreqVals[i] -= clocks;
}
}
void SN76489_Write(unsigned int clocks, unsigned int data)
{
unsigned int index;
if (clocks > SN76489.clocks)
{
/* run chip until current timestamp */
SN76489_RunUntil(clocks);
/* update internal M-cycle counter */
SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;
}
if (data & 0x80)
{
/* latch byte %1 cc t dddd */
SN76489.LatchedRegister = index = (data >> 4) & 0x07;
}
else
{
/* restore latched register index */
index = SN76489.LatchedRegister;
}
switch (index)
{
case 0:
case 2:
case 4: /* Tone Channels frequency */
{
if (data & 0x80)
{
/* Data byte %1 cc t dddd */
SN76489.Registers[index] = (SN76489.Registers[index] & 0x3f0) | (data & 0xf);
}
else
{
/* Data byte %0 - dddddd */
SN76489.Registers[index] = (SN76489.Registers[index] & 0x00f) | ((data & 0x3f) << 4);
}
/* zero frequency behaves the same as a value of 1 */
if (SN76489.Registers[index] == 0)
{
SN76489.Registers[index] = 1;
}
break;
}
case 1:
case 3:
case 5: /* Tone Channels attenuation */
{
data &= 0x0f;
SN76489.Registers[index] = data;
data = PSGVolumeValues[data];
index >>= 1;
SN76489.Channel[index][0] = (data * SN76489.PreAmp[index][0]) / 100;
SN76489.Channel[index][1] = (data * SN76489.PreAmp[index][1]) / 100;
break;
}
case 6: /* Noise control */
{
SN76489.Registers[6] = data & 0x0f;
/* reset shift register */
SN76489.NoiseShiftRegister = NoiseInitialState;
/* set noise signal generator frequency */
SN76489.NoiseFreq = 0x10 << (data&0x3);
break;
}
case 7: /* Noise attenuation */
{
data &= 0x0f;
SN76489.Registers[7] = data;
data = PSGVolumeValues[data];
SN76489.Channel[3][0] = (data * SN76489.PreAmp[3][0]) / 100;
SN76489.Channel[3][1] = (data * SN76489.PreAmp[3][1]) / 100;
break;
}
}
}

View File

@ -1,23 +0,0 @@
/*
SN76489 emulation
by Maxim in 2001 and 2002
*/
#ifndef _SN76489_H_
#define _SN76489_H_
#include "blip_buf.h"
#define SN_DISCRETE 0
#define SN_INTEGRATED 1
/* Function prototypes */
extern void SN76489_Init(int type);
extern void SN76489_Reset(void);
extern void SN76489_Config(unsigned int clocks, int preAmp, int boostNoise, int stereo);
extern void SN76489_Write(unsigned int clocks, unsigned int data);
extern void SN76489_Update(unsigned int cycles);
extern void *SN76489_GetContextPtr(void);
extern int SN76489_GetContextSize(void);
#endif /* _SN76489_H_ */

View File

@ -2,8 +2,8 @@
* Genesis Plus * Genesis Plus
* Sound Hardware * Sound Hardware
* *
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) * Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -86,7 +86,7 @@ void sound_init( void )
YM_Update = YM2612Update; YM_Update = YM2612Update;
YM_Write = YM2612Write; YM_Write = YM2612Write;
/* chip is running a VCLK / 144 = MCLK / 7 / 144 */ /* chip is running at VCLK / 144 = MCLK / 7 / 144 */
fm_cycles_ratio = 144 * 7; fm_cycles_ratio = 144 * 7;
} }
else else
@ -97,21 +97,20 @@ void sound_init( void )
YM_Update = YM2413Update; YM_Update = YM2413Update;
YM_Write = YM2413Write; YM_Write = YM2413Write;
/* chip is running a ZCLK / 72 = MCLK / 15 / 72 */ /* chip is running at ZCLK / 72 = MCLK / 15 / 72 */
fm_cycles_ratio = 72 * 15; fm_cycles_ratio = 72 * 15;
} }
/* Initialize PSG chip */ /* Initialize PSG chip */
SN76489_Init((system_hw == SYSTEM_SG) ? SN_DISCRETE : SN_INTEGRATED); psg_init((system_hw == SYSTEM_SG) ? PSG_DISCRETE : PSG_INTEGRATED);
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff);
} }
void sound_reset(void) void sound_reset(void)
{ {
/* reset sound chips */ /* reset sound chips */
YM_Reset(); YM_Reset();
SN76489_Reset(); psg_reset();
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff); psg_config(0, config.psg_preamp, 0xff);
/* reset FM buffer ouput */ /* reset FM buffer ouput */
fm_last[0] = fm_last[1] = 0; fm_last[0] = fm_last[1] = 0;
@ -125,21 +124,23 @@ void sound_reset(void)
int sound_update(unsigned int cycles) int sound_update(unsigned int cycles)
{ {
int delta, preamp, time, l, r, *ptr; int prev_l, prev_r, preamp, time, l, r, *ptr;
/* Run PSG & FM chips until end of frame */ /* Run PSG chip until end of frame */
SN76489_Update(cycles); psg_end_frame(cycles);
/* Run FM chip until end of frame */
fm_update(cycles); fm_update(cycles);
/* FM output pre-amplification */ /* FM output pre-amplification */
preamp = config.fm_preamp; preamp = config.fm_preamp;
/* FM frame initial timestamp */ /* FM frame initial timestamp */
time = fm_cycles_start; time = fm_cycles_start;
/* Restore last FM outputs from previous frame */ /* Restore last FM outputs from previous frame */
l = fm_last[0]; prev_l = fm_last[0];
r = fm_last[1]; prev_r = fm_last[1];
/* FM buffer start pointer */ /* FM buffer start pointer */
ptr = fm_buffer; ptr = fm_buffer;
@ -150,15 +151,12 @@ int sound_update(unsigned int cycles)
/* high-quality Band-Limited synthesis */ /* high-quality Band-Limited synthesis */
do do
{ {
/* left channel */ /* left & right channels */
delta = ((*ptr++ * preamp) / 100) - l; l = ((*ptr++ * preamp) / 100);
l += delta; r = ((*ptr++ * preamp) / 100);
blip_add_delta(snd.blips[0][0], time, delta); blip_add_delta(snd.blips[0], time, l-prev_l, r-prev_r);
prev_l = l;
/* right channel */ prev_r = r;
delta = ((*ptr++ * preamp) / 100) - r;
r += delta;
blip_add_delta(snd.blips[0][1], time, delta);
/* increment time counter */ /* increment time counter */
time += fm_cycles_ratio; time += fm_cycles_ratio;
@ -170,15 +168,12 @@ int sound_update(unsigned int cycles)
/* faster Linear Interpolation */ /* faster Linear Interpolation */
do do
{ {
/* left channel */ /* left & right channels */
delta = ((*ptr++ * preamp) / 100) - l; l = ((*ptr++ * preamp) / 100);
l += delta; r = ((*ptr++ * preamp) / 100);
blip_add_delta_fast(snd.blips[0][0], time, delta); blip_add_delta_fast(snd.blips[0], time, l-prev_l, r-prev_r);
prev_l = l;
/* right channel */ prev_r = r;
delta = ((*ptr++ * preamp) / 100) - r;
r += delta;
blip_add_delta_fast(snd.blips[0][1], time, delta);
/* increment time counter */ /* increment time counter */
time += fm_cycles_ratio; time += fm_cycles_ratio;
@ -190,18 +185,17 @@ int sound_update(unsigned int cycles)
fm_ptr = fm_buffer; fm_ptr = fm_buffer;
/* save last FM output for next frame */ /* save last FM output for next frame */
fm_last[0] = l; fm_last[0] = prev_l;
fm_last[1] = r; fm_last[1] = prev_r;
/* adjust FM cycle counters for next frame */ /* adjust FM cycle counters for next frame */
fm_cycles_count = fm_cycles_start = time - cycles; fm_cycles_count = fm_cycles_start = time - cycles;
/* end of blip buffers time frame */ /* end of blip buffer time frame */
blip_end_frame(snd.blips[0][0], cycles); blip_end_frame(snd.blips[0], cycles);
blip_end_frame(snd.blips[0][1], cycles);
/* return number of available samples */ /* return number of available samples */
return blip_samples_avail(snd.blips[0][0]); return blip_samples_avail(snd.blips[0]);
} }
int sound_context_save(uint8 *state) int sound_context_save(uint8 *state)
@ -217,7 +211,7 @@ int sound_context_save(uint8 *state)
save_param(YM2413GetContextPtr(),YM2413GetContextSize()); save_param(YM2413GetContextPtr(),YM2413GetContextSize());
} }
save_param(SN76489_GetContextPtr(),SN76489_GetContextSize()); bufferptr += psg_context_save(&state[bufferptr]);
save_param(&fm_cycles_start,sizeof(fm_cycles_start)); save_param(&fm_cycles_start,sizeof(fm_cycles_start));
@ -238,7 +232,7 @@ int sound_context_load(uint8 *state)
load_param(YM2413GetContextPtr(),YM2413GetContextSize()); load_param(YM2413GetContextPtr(),YM2413GetContextSize());
} }
load_param(SN76489_GetContextPtr(),SN76489_GetContextSize()); bufferptr += psg_context_load(&state[bufferptr]);
load_param(&fm_cycles_start,sizeof(fm_cycles_start)); load_param(&fm_cycles_start,sizeof(fm_cycles_start));
fm_cycles_count = fm_cycles_start; fm_cycles_count = fm_cycles_start;

View File

@ -2,8 +2,8 @@
* Genesis Plus * Genesis Plus
* Sound Hardware * Sound Hardware
* *
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) * Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:

View File

@ -2,7 +2,7 @@
* Genesis Plus * Genesis Plus
* Savestate support * Savestate support
* *
* Copyright (C) 2007-2014 Eke-Eke (Genesis Plus GX) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -116,11 +116,11 @@ int state_load(unsigned char *state)
bufferptr += sound_context_load(&state[bufferptr]); bufferptr += sound_context_load(&state[bufferptr]);
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{ {
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff); psg_config(0, config.psg_preamp, 0xff);
} }
else else
{ {
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, io_reg[6]); psg_config(0, config.psg_preamp, io_reg[6]);
} }
/* 68000 */ /* 68000 */

View File

@ -2,7 +2,7 @@
* Genesis Plus * Genesis Plus
* Savestate support * Savestate support
* *
* Copyright (C) 2007-2014 Eke-Eke (Genesis Plus GX) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:

View File

@ -68,11 +68,9 @@ int audio_init(int samplerate, double framerate)
memset(&snd, 0, sizeof (snd)); memset(&snd, 0, sizeof (snd));
/* Initialize Blip Buffers */ /* Initialize Blip Buffers */
snd.blips[0][0] = blip_new(samplerate / 10); snd.blips[0] = blip_new(samplerate / 10);
snd.blips[0][1] = blip_new(samplerate / 10); if (!snd.blips[0])
if (!snd.blips[0][0] || !snd.blips[0][1])
{ {
audio_shutdown();
return -1; return -1;
} }
@ -80,11 +78,9 @@ int audio_init(int samplerate, double framerate)
if (system_hw == SYSTEM_MCD) if (system_hw == SYSTEM_MCD)
{ {
/* allocate blip buffers */ /* allocate blip buffers */
snd.blips[1][0] = blip_new(samplerate / 10); snd.blips[1] = blip_new(samplerate / 10);
snd.blips[1][1] = blip_new(samplerate / 10); snd.blips[2] = blip_new(samplerate / 10);
snd.blips[2][0] = blip_new(samplerate / 10); if (!snd.blips[1] || !snd.blips[2])
snd.blips[2][1] = blip_new(samplerate / 10);
if (!snd.blips[1][0] || !snd.blips[1][1] || !snd.blips[2][0] || !snd.blips[2][1])
{ {
audio_shutdown(); audio_shutdown();
return -1; return -1;
@ -132,8 +128,7 @@ void audio_set_rate(int samplerate, double framerate)
/* master clock timebase so they remain perfectly synchronized together, while still */ /* master clock timebase so they remain perfectly synchronized together, while still */
/* being synchronized with 68K and Z80 CPUs as well. Mixed sound chip output is then */ /* being synchronized with 68K and Z80 CPUs as well. Mixed sound chip output is then */
/* resampled to desired rate at the end of each frame, using Blip Buffer. */ /* resampled to desired rate at the end of each frame, using Blip Buffer. */
blip_set_rates(snd.blips[0][0], mclk, samplerate); blip_set_rates(snd.blips[0], mclk, samplerate);
blip_set_rates(snd.blips[0][1], mclk, samplerate);
/* Mega CD sound hardware */ /* Mega CD sound hardware */
if (system_hw == SYSTEM_MCD) if (system_hw == SYSTEM_MCD)
@ -155,17 +150,14 @@ void audio_set_rate(int samplerate, double framerate)
void audio_reset(void) void audio_reset(void)
{ {
int i,j; int i;
/* Clear blip buffers */ /* Clear blip buffers */
for (i=0; i<3; i++) for (i=0; i<3; i++)
{ {
for (j=0; j<2; j++) if (snd.blips[i])
{ {
if (snd.blips[i][j]) blip_clear(snd.blips[i]);
{
blip_clear(snd.blips[i][j]);
}
} }
} }
@ -187,16 +179,13 @@ void audio_set_equalizer(void)
void audio_shutdown(void) void audio_shutdown(void)
{ {
int i,j; int i;
/* Delete blip buffers */ /* Delete blip buffers */
for (i=0; i<3; i++) for (i=0; i<3; i++)
{ {
for (j=0; j<2; j++) blip_delete(snd.blips[i]);
{ snd.blips[i] = 0;
blip_delete(snd.blips[i][j]);
snd.blips[i][j] = 0;
}
} }
} }
@ -213,37 +202,24 @@ int audio_update(int16 *buffer)
/* read CDDA samples */ /* read CDDA samples */
cdd_read_audio(size); cdd_read_audio(size);
}
#ifdef ALIGN_SND #ifdef ALIGN_SND
/* return an aligned number of samples if required */ /* return an aligned number of samples if required */
size &= ALIGN_SND; size &= ALIGN_SND;
#endif #endif
/* resample FM & PSG mixed stream to output buffer */ /* resample & mix FM/PSG, PCM & CD-DA streams to output buffer */
#ifdef LSB_FIRST blip_mix_samples(snd.blips[0], snd.blips[1], snd.blips[2], buffer, size);
blip_read_samples(snd.blips[0][0], buffer, size); }
blip_read_samples(snd.blips[0][1], buffer + 1, size); else
#else
blip_read_samples(snd.blips[0][0], buffer + 1, size);
blip_read_samples(snd.blips[0][1], buffer, size);
#endif
/* Mega CD specific */
if (system_hw == SYSTEM_MCD)
{ {
/* resample PCM & CD-DA streams to output buffer */ #ifdef ALIGN_SND
#ifdef LSB_FIRST /* return an aligned number of samples if required */
blip_mix_samples(snd.blips[1][0], buffer, size); size &= ALIGN_SND;
blip_mix_samples(snd.blips[1][1], buffer + 1, size);
blip_mix_samples(snd.blips[2][0], buffer, size);
blip_mix_samples(snd.blips[2][1], buffer + 1, size);
#else
blip_mix_samples(snd.blips[1][0], buffer + 1, size);
blip_mix_samples(snd.blips[1][1], buffer, size);
blip_mix_samples(snd.blips[2][0], buffer + 1, size);
blip_mix_samples(snd.blips[2][1], buffer, size);
#endif #endif
/* resample FM/PSG mixed stream to output buffer */
blip_read_samples(snd.blips[0], buffer, size);
} }
/* Audio filtering */ /* Audio filtering */
@ -434,13 +410,10 @@ void system_frame_gen(int do_skip)
} }
} }
/* initialize VCounter */
v_counter = bitmap.viewport.h;
/* first line of overscan */ /* first line of overscan */
if (bitmap.viewport.y) if (bitmap.viewport.y)
{ {
blank_line(v_counter, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x); blank_line(bitmap.viewport.h, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);
} }
/* clear DMA Busy, FIFO FULL & field flags */ /* clear DMA Busy, FIFO FULL & field flags */
@ -481,31 +454,38 @@ void system_frame_gen(int do_skip)
/* refresh inputs just before VINT (Warriors of Eternal Sun) */ /* refresh inputs just before VINT (Warriors of Eternal Sun) */
osd_input_update(); osd_input_update();
/* delay between VBLANK flag & Vertical Interrupt (Dracula, OutRunners, VR Troopers) */ /* VDP always starts after VBLANK so VINT cannot occur on first frame after a VDP reset (verified on real hardware) */
m68k_run(788); if (v_counter != bitmap.viewport.h)
if (zstate == 1)
{ {
z80_run(788); /* reinitialize VCounter */
} v_counter = bitmap.viewport.h;
else
{
Z80.cycles = 788;
}
/* set VINT flag */ /* delay between VBLANK flag & Vertical Interrupt (Dracula, OutRunners, VR Troopers) */
status |= 0x80; m68k_run(788);
if (zstate == 1)
{
z80_run(788);
}
else
{
Z80.cycles = 788;
}
/* Vertical Interrupt */ /* set VINT flag */
vint_pending = 0x20; status |= 0x80;
if (reg[1] & 0x20)
{ /* Vertical Interrupt */
/* level 6 interrupt */ vint_pending = 0x20;
m68k_set_irq(6); if (reg[1] & 0x20)
{
/* level 6 interrupt */
m68k_set_irq(6);
}
/* assert Z80 interrupt */
Z80.irq_state = ASSERT_LINE;
} }
/* assert Z80 interrupt */
Z80.irq_state = ASSERT_LINE;
/* run 68k & Z80 until end of line */ /* run 68k & Z80 until end of line */
m68k_run(MCYCLES_PER_LINE); m68k_run(MCYCLES_PER_LINE);
if (zstate == 1) if (zstate == 1)
@ -789,13 +769,10 @@ void system_frame_scd(int do_skip)
} }
} }
/* initialize VCounter */
v_counter = bitmap.viewport.h;
/* first line of overscan */ /* first line of overscan */
if (bitmap.viewport.y) if (bitmap.viewport.y)
{ {
blank_line(v_counter, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x); blank_line(bitmap.viewport.h, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);
} }
/* clear DMA Busy, FIFO FULL & field flags */ /* clear DMA Busy, FIFO FULL & field flags */
@ -836,31 +813,38 @@ void system_frame_scd(int do_skip)
/* refresh inputs just before VINT */ /* refresh inputs just before VINT */
osd_input_update(); osd_input_update();
/* delay between VBLANK flag & Vertical Interrupt (Dracula, OutRunners, VR Troopers) */ /* VDP always starts after VBLANK so VINT cannot occur on first frame after a VDP reset (verified on real hardware) */
m68k_run(788); if (v_counter != bitmap.viewport.h)
if (zstate == 1)
{ {
z80_run(788); /* reinitialize VCounter */
} v_counter = bitmap.viewport.h;
else
{
Z80.cycles = 788;
}
/* set VINT flag */ /* delay between VBLANK flag & Vertical Interrupt (Dracula, OutRunners, VR Troopers) */
status |= 0x80; m68k_run(788);
if (zstate == 1)
{
z80_run(788);
}
else
{
Z80.cycles = 788;
}
/* Vertical Interrupt */ /* set VINT flag */
vint_pending = 0x20; status |= 0x80;
if (reg[1] & 0x20)
{ /* Vertical Interrupt */
/* level 6 interrupt */ vint_pending = 0x20;
m68k_set_irq(6); if (reg[1] & 0x20)
{
/* level 6 interrupt */
m68k_set_irq(6);
}
/* assert Z80 interrupt */
Z80.irq_state = ASSERT_LINE;
} }
/* assert Z80 interrupt */
Z80.irq_state = ASSERT_LINE;
/* run both 68k & CD hardware until end of line */ /* run both 68k & CD hardware until end of line */
scd_update(MCYCLES_PER_LINE); scd_update(MCYCLES_PER_LINE);

View File

@ -91,7 +91,7 @@ typedef struct
int sample_rate; /* Output Sample rate (8000-48000) */ int sample_rate; /* Output Sample rate (8000-48000) */
double frame_rate; /* Output Frame rate (usually 50 or 60 frames per second) */ double frame_rate; /* Output Frame rate (usually 50 or 60 frames per second) */
int enabled; /* 1= sound emulation is enabled */ int enabled; /* 1= sound emulation is enabled */
blip_t* blips[3][2]; /* Blip Buffer resampling */ blip_t* blips[3]; /* Blip Buffer resampling (stereo) */
} t_snd; } t_snd;

View File

@ -71,7 +71,7 @@ OBJECTS += $(OBJDIR)/input.o \
$(OBJDIR)/graphic_board.o $(OBJDIR)/graphic_board.o
OBJECTS += $(OBJDIR)/sound.o \ OBJECTS += $(OBJDIR)/sound.o \
$(OBJDIR)/sn76489.o \ $(OBJDIR)/psg.o \
$(OBJDIR)/ym2413.o \ $(OBJDIR)/ym2413.o \
$(OBJDIR)/ym2612.o $(OBJDIR)/ym2612.o

View File

@ -42,7 +42,7 @@ void set_config_defaults(void)
config.psg_preamp = 150; config.psg_preamp = 150;
config.fm_preamp = 100; config.fm_preamp = 100;
config.hq_fm = 1; config.hq_fm = 1;
config.psgBoostNoise = 1; config.hq_psg = 1;
config.filter = 1; config.filter = 1;
config.low_freq = 200; config.low_freq = 200;
config.high_freq = 8000; config.high_freq = 8000;

View File

@ -20,7 +20,7 @@ typedef struct
{ {
uint8 hq_fm; uint8 hq_fm;
uint8 filter; uint8 filter;
uint8 psgBoostNoise; uint8 hq_psg;
uint8 dac_bits; uint8 dac_bits;
uint8 ym2413; uint8 ym2413;
int16 psg_preamp; int16 psg_preamp;

View File

@ -3,7 +3,7 @@
* *
* Genesis Plus GX configuration file support * Genesis Plus GX configuration file support
* *
* Copyright Eke-Eke (2007-2015) * Copyright Eke-Eke (2007-2016)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -101,7 +101,7 @@ void config_default(void)
config.psg_preamp = 150; config.psg_preamp = 150;
config.fm_preamp = 100; config.fm_preamp = 100;
config.hq_fm = 1; config.hq_fm = 1;
config.psgBoostNoise = 1; config.hq_psg = 1;
config.filter = 1; config.filter = 1;
config.lp_range = 0x9999; /* 0.6 in 16.16 fixed point */ config.lp_range = 0x9999; /* 0.6 in 16.16 fixed point */
config.low_freq = 880; config.low_freq = 880;

View File

@ -3,7 +3,7 @@
* *
* Genesis Plus GX configuration file support * Genesis Plus GX configuration file support
* *
* Copyright Eke-Eke (2007-2015) * Copyright Eke-Eke (2007-2016)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -51,7 +51,7 @@ typedef struct
char version[16]; char version[16];
uint8 hq_fm; uint8 hq_fm;
uint8 filter; uint8 filter;
uint8 psgBoostNoise; uint8 hq_psg;
uint8 dac_bits; uint8 dac_bits;
uint8 ym2413; uint8 ym2413;
uint8 mono; uint8 mono;

View File

@ -341,19 +341,19 @@ static gui_item items_options[] =
/* Audio options */ /* Audio options */
static gui_item items_audio[] = static gui_item items_audio[] =
{ {
{NULL,NULL,"Master System FM: AUTO", "Enable/disable YM2413 chip", 56,132,276,48}, {NULL,NULL,"Master System FM: AUTO", "Enable/Disable YM2413 chip", 56,132,276,48},
{NULL,NULL,"High-Quality FM: ON", "Adjust YM2612/YM2413 resampling quality", 56,132,276,48}, {NULL,NULL,"High-Quality FM: ON", "Enable/Disable YM2612/YM2413 high-quality resampling", 56,132,276,48},
{NULL,NULL,"FM Resolution: MAX", "Adjust YM2612 DAC precision", 56,132,276,48}, {NULL,NULL,"FM Resolution: MAX", "Adjust YM2612 DAC precision", 56,132,276,48},
{NULL,NULL,"FM Volume: 1.00", "Adjust YM2612/YM2413 output level", 56,132,276,48}, {NULL,NULL,"FM Volume: 1.00", "Adjust YM2612/YM2413 audio balance", 56,132,276,48},
{NULL,NULL,"PSG Volume: 2.50", "Adjust SN76489 output level", 56,132,276,48}, {NULL,NULL,"PSG Volume: 2.50", "Adjust SN76489 audio balance", 56,132,276,48},
{NULL,NULL,"PSG Noise Boost: OFF", "Boost SN76489 Noise Channel", 56,132,276,48}, {NULL,NULL,"High-Quality PSG: ON", "Enable/Disable SN76489 high-quality resampling", 56,132,276,48},
{NULL,NULL,"Audio Out: STEREO", "Select audio mixing output type", 56,132,276,48}, {NULL,NULL,"Audio Output: STEREO", "Select audio mixing output type", 56,132,276,48},
{NULL,NULL,"Filtering: 3-BAND EQ", "Setup Audio filtering", 56,132,276,48}, {NULL,NULL,"Filtering: 3-BAND EQ", "Select audio filtering type", 56,132,276,48},
{NULL,NULL,"Low Gain: 1.00", "Adjust EQ Low Band Gain", 56,132,276,48}, {NULL,NULL,"Low Gain: 1.00", "Adjust EQ Low Band Gain", 56,132,276,48},
{NULL,NULL,"Mid Gain: 1.00", "Adjust EQ Mid Band Gain", 56,132,276,48}, {NULL,NULL,"Mid Gain: 1.00", "Adjust EQ Mid Band Gain", 56,132,276,48},
{NULL,NULL,"High Gain: 1.00", "Adjust EQ High Band Gain", 56,132,276,48}, {NULL,NULL,"High Gain: 1.00", "Adjust EQ High Band Gain", 56,132,276,48},
{NULL,NULL,"Low Freq: 200 Hz", "Adjust EQ Lowest Frequency", 56,132,276,48}, {NULL,NULL,"Low Freq: 200 Hz", "Adjust EQ Lowest Frequency", 56,132,276,48},
{NULL,NULL,"High Freq: 20000 Hz", "Adjust EQ Highest Frequency", 56,132,276,48} {NULL,NULL,"High Freq: 20000 Hz", "Adjust EQ Highest Frequency", 56,132,276,48}
}; };
/* System ROM paths */ /* System ROM paths */
@ -381,10 +381,10 @@ static gui_item items_system[] =
{NULL,NULL,"VDP Mode: AUTO", "Select VDP mode", 56,132,276,48}, {NULL,NULL,"VDP Mode: AUTO", "Select VDP mode", 56,132,276,48},
{NULL,NULL,"System Clock: AUTO", "Select system clock frequency", 56,132,276,48}, {NULL,NULL,"System Clock: AUTO", "Select system clock frequency", 56,132,276,48},
{NULL,NULL,"System Boot: BIOS&CART", "Select system booting method", 56,132,276,48}, {NULL,NULL,"System Boot: BIOS&CART", "Select system booting method", 56,132,276,48},
{NULL,NULL,"System Lockups: ON", "Enable/disable original system lock-ups", 56,132,276,48}, {NULL,NULL,"System Lockups: ON", "Enable/Disable original system lock-ups", 56,132,276,48},
{NULL,NULL,"68k Address Error: ON", "Enable/disable 68k address error exceptions", 56,132,276,48}, {NULL,NULL,"68k Address Error: ON", "Enable/Disable 68k address error exceptions", 56,132,276,48},
{NULL,NULL,"Lock-on: OFF", "Select Lock-On cartridge type", 56,132,276,48}, {NULL,NULL,"Lock-on: OFF", "Select Lock-On cartridge type", 56,132,276,48},
{NULL,NULL,"Cartridge Swap: OFF", "Enable/disable cartridge hot swap", 56,132,276,48}, {NULL,NULL,"Cartridge Swap: OFF", "Enable/Disable cartridge hot swap", 56,132,276,48},
{NULL,NULL,"BIOS & Lock-On ROM paths","Configure BIOS & Lock-On ROM paths", 56,132,276,48}, {NULL,NULL,"BIOS & Lock-On ROM paths","Configure BIOS & Lock-On ROM paths", 56,132,276,48},
{NULL,NULL,"SVP Cycles: 1500", "Adjust SVP chip emulation speed", 56,132,276,48} {NULL,NULL,"SVP Cycles: 1500", "Adjust SVP chip emulation speed", 56,132,276,48}
}; };
@ -394,22 +394,22 @@ static gui_item items_video[] =
{ {
{NULL,NULL,"Display: PROGRESSIVE", "Select video mode", 56,132,276,48}, {NULL,NULL,"Display: PROGRESSIVE", "Select video mode", 56,132,276,48},
{NULL,NULL,"TV mode: 50/60HZ", "Select video refresh rate", 56,132,276,48}, {NULL,NULL,"TV mode: 50/60HZ", "Select video refresh rate", 56,132,276,48},
{NULL,NULL,"VSYNC: AUTO", "Enable/disable sync with video hardware", 56,132,276,48}, {NULL,NULL,"VSYNC: AUTO", "Enable/Disable sync with video hardware", 56,132,276,48},
{NULL,NULL,"Bilinear Filter: OFF", "Enable/disable GX hardware texture filtering", 56,132,276,48}, {NULL,NULL,"Bilinear Filter: OFF", "Enable/Disable GX hardware texture filtering", 56,132,276,48},
{NULL,NULL,"Deflickering Filter: AUTO", "Enable/disable GX hardware framebuffer filtering", 56,132,276,48}, {NULL,NULL,"Deflickering Filter: AUTO", "Enable/Disable GX hardware framebuffer filtering", 56,132,276,48},
#ifdef HW_RVL #ifdef HW_RVL
{NULL,NULL,"Trap Filter: ON", "Enable/disable VI hardware composite out filtering",56,132,276,48}, {NULL,NULL,"Trap Filter: ON", "Enable/Disable VI hardware composite out filtering",56,132,276,48},
{NULL,NULL,"Gamma Correction: 1.0", "Adjust VI hardware gamma correction", 56,132,276,48}, {NULL,NULL,"Gamma Correction: 1.0", "Adjust VI hardware gamma correction", 56,132,276,48},
#endif #endif
{NULL,NULL,"LCD Ghosting Filter: OFF", "Enable/disable software LCD image persistence", 56,132,276,48}, {NULL,NULL,"LCD Ghosting Filter: OFF", "Enable/Disable software LCD image persistence", 56,132,276,48},
{NULL,NULL,"NTSC Filter: COMPOSITE", "Enable/disable software NTSC filtering", 56,132,276,48}, {NULL,NULL,"NTSC Filter: COMPOSITE", "Enable/Disable software NTSC filtering", 56,132,276,48},
{NULL,NULL,"NTSC Sharpness: 0.0", "Adjust edge contrast enhancement/blurring", 56,132,276,48}, {NULL,NULL,"NTSC Sharpness: 0.0", "Adjust edge contrast enhancement/blurring", 56,132,276,48},
{NULL,NULL,"NTSC Resolution: 0.0", "Adjust image resolution", 56,132,276,48}, {NULL,NULL,"NTSC Resolution: 0.0", "Adjust image resolution", 56,132,276,48},
{NULL,NULL,"NTSC Artifacts: 0.0", "Adjust artifacts caused by color changes", 56,132,276,48}, {NULL,NULL,"NTSC Artifacts: 0.0", "Adjust artifacts caused by color changes", 56,132,276,48},
{NULL,NULL,"NTSC Color Bleed: 0.0", "Adjust color resolution reduction", 56,132,276,48}, {NULL,NULL,"NTSC Color Bleed: 0.0", "Adjust color resolution reduction", 56,132,276,48},
{NULL,NULL,"NTSC Color Fringing: 0.0", "Adjust artifacts caused by brightness changes", 56,132,276,48}, {NULL,NULL,"NTSC Color Fringing: 0.0", "Adjust artifacts caused by brightness changes", 56,132,276,48},
{NULL,NULL,"Borders: OFF", "Enable/disable overscan emulation", 56,132,276,48}, {NULL,NULL,"Borders: OFF", "Enable/Disable overscan emulation", 56,132,276,48},
{NULL,NULL,"GG screen: ORIGINAL", "Enable/disable Game Gear extended screen", 56,132,276,48}, {NULL,NULL,"GG screen: ORIGINAL", "Enable/Disable Game Gear extended screen", 56,132,276,48},
{NULL,NULL,"Aspect: ORIGINAL (4:3)", "Select display aspect ratio", 56,132,276,48}, {NULL,NULL,"Aspect: ORIGINAL (4:3)", "Select display aspect ratio", 56,132,276,48},
{NULL,NULL,"Screen Position (+0,+0)", "Adjust display position", 56,132,276,48}, {NULL,NULL,"Screen Position (+0,+0)", "Adjust display position", 56,132,276,48},
{NULL,NULL,"Screen Scaling (+0,+0)", "Adjust display scaling", 56,132,276,48} {NULL,NULL,"Screen Scaling (+0,+0)", "Adjust display scaling", 56,132,276,48}
@ -418,19 +418,19 @@ static gui_item items_video[] =
/* Menu options */ /* Menu options */
static gui_item items_prefs[] = static gui_item items_prefs[] =
{ {
{NULL,NULL,"Auto ROM Load: OFF", "Enable/disable automatic ROM loading on startup", 56,132,276,48}, {NULL,NULL,"Auto ROM Load: OFF", "Enable/Disable automatic ROM loading on startup", 56,132,276,48},
{NULL,NULL,"Auto Cheats: OFF", "Enable/disable automatic cheats activation", 56,132,276,48}, {NULL,NULL,"Auto Cheats: OFF", "Enable/Disable automatic cheats activation", 56,132,276,48},
{NULL,NULL,"Auto Saves: OFF", "Enable/disable automatic saves", 56,132,276,48}, {NULL,NULL,"Auto Saves: OFF", "Enable/Disable automatic saves", 56,132,276,48},
{NULL,NULL,"ROM Load Device: SD", "Configure default device for ROM files", 56,132,276,48}, {NULL,NULL,"ROM Load Device: SD", "Configure default device for ROM files", 56,132,276,48},
{NULL,NULL,"Saves Device: FAT", "Configure default device for Save files", 56,132,276,48}, {NULL,NULL,"Saves Device: FAT", "Configure default device for Save files", 56,132,276,48},
{NULL,NULL,"SFX Volume: 100", "Adjust sound effects volume", 56,132,276,48}, {NULL,NULL,"SFX Volume: 100", "Adjust sound effects volume", 56,132,276,48},
{NULL,NULL,"BGM Volume: 100", "Adjust background music volume", 56,132,276,48}, {NULL,NULL,"BGM Volume: 100", "Adjust background music volume", 56,132,276,48},
{NULL,NULL,"BG Overlay: ON", "Enable/disable background overlay", 56,132,276,48}, {NULL,NULL,"BG Overlay: ON", "Enable/Disable background overlay", 56,132,276,48},
{NULL,NULL,"Screen Width: 658", "Adjust menu screen width in pixels", 56,132,276,48}, {NULL,NULL,"Screen Width: 658", "Adjust menu screen width in pixels", 56,132,276,48},
{NULL,NULL,"Show CD Leds: OFF", "Enable/disable CD leds display", 56,132,276,48}, {NULL,NULL,"Show CD Leds: OFF", "Enable/Disable CD leds display", 56,132,276,48},
{NULL,NULL,"Show FPS: OFF", "Enable/disable FPS counter", 56,132,276,48}, {NULL,NULL,"Show FPS: OFF", "Enable/Disable FPS counter", 56,132,276,48},
#ifdef HW_RVL #ifdef HW_RVL
{NULL,NULL,"Wiimote Timeout: OFF","Enable/disable Wii remote automatic shutodwn", 56,132,276,48}, {NULL,NULL,"Wiimote Timeout: OFF","Enable/Disable Wii remote automatic shutodwn", 56,132,276,48},
{NULL,NULL,"Wiimote Calibration: AUTO","Calibrate Wii remote pointer", 56,132,276,48}, {NULL,NULL,"Wiimote Calibration: AUTO","Calibrate Wii remote pointer", 56,132,276,48},
#endif #endif
}; };
@ -899,16 +899,15 @@ static void soundmenu ()
else if (config.ym2413 == 1) sprintf (items[0].text, "Master System FM: ON"); else if (config.ym2413 == 1) sprintf (items[0].text, "Master System FM: ON");
else sprintf (items[0].text, "Master System FM: AUTO"); else sprintf (items[0].text, "Master System FM: AUTO");
if (config.hq_fm) sprintf (items[1].text, "High-Quality FM: ON"); sprintf (items[1].text, "High-Quality FM: %s", config.hq_fm ? "ON":"OFF");
else sprintf (items[1].text, "High-Quality FM: OFF");
if (config.dac_bits < 14) sprintf (items[2].text, "FM Resolution: %d bits", config.dac_bits); if (config.dac_bits < 14) sprintf (items[2].text, "FM Resolution: %d bits", config.dac_bits);
else sprintf (items[2].text, "FM Resolution: MAX"); else sprintf (items[2].text, "FM Resolution: MAX");
sprintf (items[3].text, "FM Volume: %1.2f", fm_volume); sprintf (items[3].text, "FM Volume: %1.2f", fm_volume);
sprintf (items[4].text, "PSG Volume: %1.2f", psg_volume); sprintf (items[4].text, "PSG Volume: %1.2f", psg_volume);
sprintf (items[5].text, "PSG Noise Boost: %s", config.psgBoostNoise ? "ON":"OFF"); sprintf (items[5].text, "High-Quality PSG: %s", config.hq_psg? "ON":"OFF");
sprintf (items[6].text, "Audio Out: %s", config.mono ? "MONO":"STEREO"); sprintf (items[6].text, "Audio Output: %s", config.mono ? "MONO":"STEREO");
if (config.filter == 2) if (config.filter == 2)
{ {
@ -971,8 +970,7 @@ static void soundmenu ()
case 1: case 1:
{ {
config.hq_fm ^= 1; config.hq_fm ^= 1;
if (config.hq_fm) sprintf (items[1].text, "High-Quality FM: ON"); sprintf (items[1].text, "High-Quality FM: %s", config.hq_fm ? "ON":"OFF");
else sprintf (items[1].text, "High-Quality FM: OFF");
break; break;
} }
@ -1001,27 +999,19 @@ static void soundmenu ()
config.psg_preamp = (int)(psg_volume * 100.0 + 0.5); config.psg_preamp = (int)(psg_volume * 100.0 + 0.5);
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{ {
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff); psg_config(0, config.psg_preamp, 0xff);
} }
else else
{ {
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, io_reg[6]); psg_config(0, config.psg_preamp, io_reg[6]);
} }
break; break;
} }
case 5: case 5:
{ {
config.psgBoostNoise ^= 1; config.hq_psg ^= 1;
sprintf (items[5].text, "PSG Noise Boost: %s", config.psgBoostNoise ? "ON":"OFF"); sprintf (items[5].text, "High-Quality PSG: %s", config.hq_psg ? "ON":"OFF");
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff);
}
else
{
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, io_reg[6]);
}
break; break;
} }
@ -1745,8 +1735,8 @@ static void videomenu ()
else else
sprintf (items[VI_OFFSET+1].text, "NTSC Filter: OFF"); sprintf (items[VI_OFFSET+1].text, "NTSC Filter: OFF");
strcpy(items[VI_OFFSET+2+ntsc_offset].comment, "Enable/disable overscan emulation"); strcpy(items[VI_OFFSET+2+ntsc_offset].comment, "Enable/Disable overscan emulation");
strcpy(items[VI_OFFSET+3+ntsc_offset].comment, "Enable/disable Game Gear extended screen"); strcpy(items[VI_OFFSET+3+ntsc_offset].comment, "Enable/Disable Game Gear extended screen");
strcpy(items[VI_OFFSET+4+ntsc_offset].comment, "Select display aspect ratio"); strcpy(items[VI_OFFSET+4+ntsc_offset].comment, "Select display aspect ratio");
strcpy(items[VI_OFFSET+5+ntsc_offset].comment, "Adjust display position"); strcpy(items[VI_OFFSET+5+ntsc_offset].comment, "Adjust display position");
strcpy(items[VI_OFFSET+6+ntsc_offset].comment, "Adjust display scaling"); strcpy(items[VI_OFFSET+6+ntsc_offset].comment, "Adjust display scaling");
@ -3645,15 +3635,15 @@ static void showcredits(void)
gxDrawTexture(texture, (640-texture->width)/2, (480-texture->height)/2, texture->width, texture->height,255); gxDrawTexture(texture, (640-texture->width)/2, (480-texture->height)/2, texture->width, texture->height,255);
FONT_writeCenter("Genesis Plus Core", 24, 0, 640, 480 - offset, (GXColor)LIGHT_BLUE); FONT_writeCenter("Genesis Plus Core", 24, 0, 640, 480 - offset, (GXColor)LIGHT_BLUE);
FONT_writeCenter("improved emulation code, fixes & extra features by Eke-Eke", 18, 0, 640, 516 - offset, (GXColor)WHITE); FONT_writeCenter("improved emulation code & extra features by Eke-Eke", 18, 0, 640, 516 - offset, (GXColor)WHITE);
FONT_writeCenter("original 1.3 version by Charles MacDonald", 18, 0, 640, 534 - offset, (GXColor)WHITE); FONT_writeCenter("original 1.3 version by Charles MacDonald", 18, 0, 640, 534 - offset, (GXColor)WHITE);
FONT_writeCenter("original Z80 core by Juergen Buchmueller", 18, 0, 640, 552 - offset, (GXColor)WHITE); FONT_writeCenter("original Z80 core by Juergen Buchmueller", 18, 0, 640, 552 - offset, (GXColor)WHITE);
FONT_writeCenter("original 68k core (Musashi) by Karl Stenerud", 18, 0, 640, 570 - offset, (GXColor)WHITE); FONT_writeCenter("original 68k core (Musashi) by Karl Stenerud", 18, 0, 640, 570 - offset, (GXColor)WHITE);
FONT_writeCenter("original YM2612/2413 cores by Jarek Burczynski, Tatsuyuki Satoh", 18, 0, 640, 588 - offset, (GXColor)WHITE); FONT_writeCenter("original YM2612/2413 cores by Jarek Burczynski, Tatsuyuki Satoh", 18, 0, 640, 588 - offset, (GXColor)WHITE);
FONT_writeCenter("original SN76489 core by Maxim", 18, 0, 640, 606 - offset, (GXColor)WHITE); FONT_writeCenter("SVP core by Gravydas Ignotas (Notaz)", 18, 0, 640, 606 - offset, (GXColor)WHITE);
FONT_writeCenter("SVP core by Gravydas Ignotas (Notaz)", 18, 0, 640, 624 - offset, (GXColor)WHITE); FONT_writeCenter("Blip Buffer Library & NTSC Video Filter by Shay Green (Blargg)", 18, 0, 640, 624 - offset, (GXColor)WHITE);
FONT_writeCenter("Blip Buffer Library & NTSC Video Filter by Shay Green (Blargg)", 18, 0, 640, 642 - offset, (GXColor)WHITE); FONT_writeCenter("3-Band EQ implementation by Neil C", 18, 0, 640, 642 - offset, (GXColor)WHITE);
FONT_writeCenter("3-Band EQ implementation by Neil C", 18, 0, 640, 660 - offset, (GXColor)WHITE); FONT_writeCenter("Ogg Vorbis 'Tremor' Library by Xiph.org Foundation", 18, 0, 640, 660 - offset, (GXColor)WHITE);
FONT_writeCenter("Special thanks to ...", 20, 0, 640, 700 - offset, (GXColor)LIGHT_GREEN); FONT_writeCenter("Special thanks to ...", 20, 0, 640, 700 - offset, (GXColor)LIGHT_GREEN);
FONT_writeCenter("Nemesis, Tasco Deluxe, Bart Trzynadlowski, Jorge Cwik, Haze,", 18, 0, 640, 736 - offset, (GXColor)WHITE); FONT_writeCenter("Nemesis, Tasco Deluxe, Bart Trzynadlowski, Jorge Cwik, Haze,", 18, 0, 640, 736 - offset, (GXColor)WHITE);
@ -3670,7 +3660,7 @@ static void showcredits(void)
FONT_writeCenter("libfat by Chism", 18, 0, 640, 978 - offset, (GXColor)WHITE); FONT_writeCenter("libfat by Chism", 18, 0, 640, 978 - offset, (GXColor)WHITE);
FONT_writeCenter("wiiuse by Michael Laforest (Para)", 18, 0, 640, 996 - offset, (GXColor)WHITE); FONT_writeCenter("wiiuse by Michael Laforest (Para)", 18, 0, 640, 996 - offset, (GXColor)WHITE);
FONT_writeCenter("asndlib & OGG player by Francisco Muñoz (Hermes)", 18, 0, 640, 1014 - offset, (GXColor)WHITE); FONT_writeCenter("asndlib & OGG player by Francisco Muñoz (Hermes)", 18, 0, 640, 1014 - offset, (GXColor)WHITE);
FONT_writeCenter("zlib, libpng & libtremor by their respective authors", 18, 0, 640, 1032 - offset, (GXColor)WHITE); FONT_writeCenter("zlib & libpng by their respective authors", 18, 0, 640, 1032 - offset, (GXColor)WHITE);
FONT_writeCenter("devkitPPC by Wintermute", 18, 0, 640, 1050 - offset, (GXColor)WHITE); FONT_writeCenter("devkitPPC by Wintermute", 18, 0, 640, 1050 - offset, (GXColor)WHITE);
FONT_writeCenter("Special thanks to ...", 20, 0, 640, 1090 - offset, (GXColor)LIGHT_GREEN); FONT_writeCenter("Special thanks to ...", 20, 0, 640, 1090 - offset, (GXColor)LIGHT_GREEN);

View File

@ -13,7 +13,7 @@ Upstream Authors:
Files: * Files: *
Copyright: 1998, 1999, 2000, 2001, 2002, 2003 Charles MacDonald Copyright: 1998, 1999, 2000, 2001, 2002, 2003 Charles MacDonald
Some portions copyright Nicola Salmoria and the MAME team. All rights reserved. Some portions copyright Nicola Salmoria and the MAME team. All rights reserved.
2007-2015 Eke-Eke. All rights reserved. 2007-2016 Eke-Eke. All rights reserved.
License: License:
Unless otherwise explicitly stated, all code in Genesis Plus GX is released Unless otherwise explicitly stated, all code in Genesis Plus GX is released
under the following license: under the following license:
@ -77,7 +77,6 @@ License: LGPLv2.1
Files: core/sound/blip_buf.c Files: core/sound/blip_buf.c
core/sound/blip_buf.h core/sound/blip_buf.h
Copyright: 2003-2009 Shay Green Copyright: 2003-2009 Shay Green
2012-2013 EkeEke
License: LGPLv2.1 License: LGPLv2.1
Files: core/sound/eq.c Files: core/sound/eq.c
@ -90,13 +89,6 @@ License: Public domain
The author assumes NO RESPONSIBILITY for any problems caused by the use of The author assumes NO RESPONSIBILITY for any problems caused by the use of
this software. this software.
Files: core/sound/sn76489.c
core/sound/sn76489.h
Copyright: 2001, 2002 Maxim
2004 Charles MacDonald
2007, 2009, 2010, 2012 Eke-Eke
License: Genesis Plus GX license
Files: core/sound/ym2413.c Files: core/sound/ym2413.c
core/sound/ym2413.h core/sound/ym2413.h
Copyright: 2002 Jarek Burczynski Copyright: 2002 Jarek Burczynski

View File

@ -97,6 +97,7 @@ char CART_BRAM[256];
static int vwidth; static int vwidth;
static int vheight; static int vheight;
static double vaspect_ratio;
static uint32_t brm_crc[2]; static uint32_t brm_crc[2];
static uint8_t brm_format[0x40] = static uint8_t brm_format[0x40] =
@ -494,8 +495,8 @@ static void config_default(void)
/* sound options */ /* sound options */
config.psg_preamp = 150; config.psg_preamp = 150;
config.fm_preamp = 100; config.fm_preamp = 100;
config.hq_fm = 1; /* high-quality resampling */ config.hq_fm = 1; /* high-quality FM resampling (slower) */
config.psgBoostNoise = 1; config.hq_psg = 1; /* high-quality PSG resampling (slower) */
config.filter = 0; /* no filter */ config.filter = 0; /* no filter */
config.lp_range = 0x9999; /* 0.6 in 16.16 fixed point */ config.lp_range = 0x9999; /* 0.6 in 16.16 fixed point */
config.low_freq = 880; config.low_freq = 880;
@ -503,7 +504,7 @@ static void config_default(void)
config.lg = 1.0; config.lg = 1.0;
config.mg = 1.0; config.mg = 1.0;
config.hg = 1.0; config.hg = 1.0;
config.dac_bits = 14; /* MAX DEPTH */ config.dac_bits = 14; /* MAX DEPTH */
config.ym2413 = 2; /* AUTO */ config.ym2413 = 2; /* AUTO */
config.mono = 0; /* STEREO output */ config.mono = 0; /* STEREO output */
@ -520,6 +521,7 @@ static void config_default(void)
/* video options */ /* video options */
config.overscan = 0; config.overscan = 0;
config.aspect_ratio = 0;
config.gg_extra = 0; config.gg_extra = 0;
config.ntsc = 0; config.ntsc = 0;
config.lcd = 0; config.lcd = 0;
@ -736,13 +738,46 @@ static void extract_directory(char *buf, const char *path, size_t size)
buf[0] = '\0'; buf[0] = '\0';
} }
static double calculate_display_aspect_ratio(void)
{
if (config.aspect_ratio == 0)
{
if ((system_hw == SYSTEM_GG || system_hw == SYSTEM_GGMS) && config.overscan == 0 && config.gg_extra == 0)
{
return (6.0 / 5.0) * ((double)vwidth / (double)vheight);
}
}
bool is_h40 = bitmap.viewport.w == 320; /* Could be read directly from the register as well. */
double dotrate = system_clock / (is_h40 ? 8.0 : 10.0);
double videosamplerate;
if (config.aspect_ratio == 1) /* Force NTSC PAR */
{
videosamplerate = 135000000.0 / 11.0;
}
else if (config.aspect_ratio == 2) /* Force PAL PAR */
{
videosamplerate = 14750000.0;
}
else
{
videosamplerate = vdp_pal ? 14750000.0 : 135000000.0 / 11.0;
}
return (videosamplerate / dotrate) * ((double)vwidth / ((double)vheight * 2.0));
}
static bool update_viewport(void) static bool update_viewport(void)
{ {
int ow = vwidth; int ow = vwidth;
int oh = vheight; int oh = vheight;
double oar = vaspect_ratio;
vwidth = bitmap.viewport.w + (bitmap.viewport.x * 2); vwidth = bitmap.viewport.w + (bitmap.viewport.x * 2);
vheight = bitmap.viewport.h + (bitmap.viewport.y * 2); vheight = bitmap.viewport.h + (bitmap.viewport.y * 2);
vaspect_ratio = calculate_display_aspect_ratio();
if (config.ntsc) if (config.ntsc)
{ {
@ -756,8 +791,7 @@ static bool update_viewport(void)
{ {
vheight = vheight * 2; vheight = vheight * 2;
} }
return ((ow != vwidth) || (oh != vheight) || (oar != vaspect_ratio));
return ((ow != vwidth) || (oh != vheight));
} }
static void check_variables(void) static void check_variables(void)
@ -933,12 +967,7 @@ static void check_variables(void)
break; break;
} }
/* force overscan change */ update_viewports = true;
bitmap.viewport.changed = 3;
/* reinitialize libretro audio/video timings */
retro_get_system_av_info(&info);
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &info);
} }
} }
} }
@ -1088,6 +1117,20 @@ static void check_variables(void)
update_viewports = true; update_viewports = true;
} }
var.key = "genesis_plus_gx_aspect_ratio";
environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var);
{
orig_value = config.aspect_ratio;
if (strcmp(var.value, "NTSC PAR") == 0)
config.aspect_ratio = 1;
else if (strcmp(var.value, "PAL PAR") == 0)
config.aspect_ratio = 2;
else
config.aspect_ratio = 0;
if (orig_value != config.aspect_ratio)
update_viewports = true;
}
var.key = "genesis_plus_gx_render"; var.key = "genesis_plus_gx_render";
environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var);
{ {
@ -1125,11 +1168,12 @@ static void check_variables(void)
system_init(); system_init();
system_reset(); system_reset();
memcpy(sram.sram, temp, sizeof(temp)); memcpy(sram.sram, temp, sizeof(temp));
update_viewports = true;
} }
if (update_viewports) if (update_viewports)
{ {
bitmap.viewport.changed = 3; bitmap.viewport.changed = 11;
if ((system_hw == SYSTEM_GG) && !config.gg_extra) if ((system_hw == SYSTEM_GG) && !config.gg_extra)
bitmap.viewport.x = (config.overscan & 2) ? 14 : -48; bitmap.viewport.x = (config.overscan & 2) ? 14 : -48;
else else
@ -1475,7 +1519,7 @@ unsigned retro_api_version(void) { return RETRO_API_VERSION; }
void retro_set_environment(retro_environment_t cb) void retro_set_environment(retro_environment_t cb)
{ {
struct retro_variable vars[] = { static const struct retro_variable vars[] = {
{ "genesis_plus_gx_system_hw", "System hardware; auto|sg-1000|sg-1000 II|mark-III|master system|master system II|game gear|mega drive / genesis" }, { "genesis_plus_gx_system_hw", "System hardware; auto|sg-1000|sg-1000 II|mark-III|master system|master system II|game gear|mega drive / genesis" },
{ "genesis_plus_gx_region_detect", "System region; auto|ntsc-u|pal|ntsc-j" }, { "genesis_plus_gx_region_detect", "System region; auto|ntsc-u|pal|ntsc-j" },
{ "genesis_plus_gx_force_dtack", "System lockups; enabled|disabled" }, { "genesis_plus_gx_force_dtack", "System lockups; enabled|disabled" },
@ -1489,13 +1533,14 @@ void retro_set_environment(retro_environment_t cb)
{ "genesis_plus_gx_lcd_filter", "LCD Ghosting filter; disabled|enabled" }, { "genesis_plus_gx_lcd_filter", "LCD Ghosting filter; disabled|enabled" },
{ "genesis_plus_gx_overscan", "Borders; disabled|top/bottom|left/right|full" }, { "genesis_plus_gx_overscan", "Borders; disabled|top/bottom|left/right|full" },
{ "genesis_plus_gx_gg_extra", "Game Gear extended screen; disabled|enabled" }, { "genesis_plus_gx_gg_extra", "Game Gear extended screen; disabled|enabled" },
{ "genesis_plus_gx_aspect_ratio", "Core-provided aspect ratio; auto|NTSC PAR|PAL PAR" },
{ "genesis_plus_gx_render", "Interlaced mode 2 output; single field|double field" }, { "genesis_plus_gx_render", "Interlaced mode 2 output; single field|double field" },
{ "genesis_plus_gx_gun_cursor", "Show Lightgun crosshair; no|yes" }, { "genesis_plus_gx_gun_cursor", "Show Lightgun crosshair; no|yes" },
{ "genesis_plus_gx_invert_mouse", "Invert Mouse Y-axis; no|yes" }, { "genesis_plus_gx_invert_mouse", "Invert Mouse Y-axis; no|yes" },
{ NULL, NULL }, { NULL, NULL },
}; };
struct retro_controller_description port_1[] = { static const struct retro_controller_description port_1[] = {
{ "Joypad Auto", RETRO_DEVICE_JOYPAD }, { "Joypad Auto", RETRO_DEVICE_JOYPAD },
{ "Joypad Port Empty", RETRO_DEVICE_NONE }, { "Joypad Port Empty", RETRO_DEVICE_NONE },
{ "MD Joypad 3 Button", RETRO_DEVICE_MDPAD_3B }, { "MD Joypad 3 Button", RETRO_DEVICE_MDPAD_3B },
@ -1514,7 +1559,7 @@ void retro_set_environment(retro_environment_t cb)
{ "MD Mouse", RETRO_DEVICE_MOUSE }, { "MD Mouse", RETRO_DEVICE_MOUSE },
}; };
struct retro_controller_description port_2[] = { static const struct retro_controller_description port_2[] = {
{ "Joypad Auto", RETRO_DEVICE_JOYPAD }, { "Joypad Auto", RETRO_DEVICE_JOYPAD },
{ "Joypad Port Empty", RETRO_DEVICE_NONE }, { "Joypad Port Empty", RETRO_DEVICE_NONE },
{ "MD Joypad 3 Button", RETRO_DEVICE_MDPAD_3B }, { "MD Joypad 3 Button", RETRO_DEVICE_MDPAD_3B },
@ -1535,13 +1580,13 @@ void retro_set_environment(retro_environment_t cb)
{ "MD Mouse", RETRO_DEVICE_MOUSE }, { "MD Mouse", RETRO_DEVICE_MOUSE },
}; };
struct retro_controller_info ports[] = { static const struct retro_controller_info ports[] = {
{ port_1, 16 }, { port_1, 16 },
{ port_2, 18 }, { port_2, 18 },
{ 0 }, { 0 },
}; };
struct retro_input_descriptor desc[] = { static const struct retro_input_descriptor desc[] = {
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
@ -1679,30 +1724,48 @@ void retro_get_system_av_info(struct retro_system_av_info *info)
info->geometry.base_height = vheight; info->geometry.base_height = vheight;
info->geometry.max_width = 720; info->geometry.max_width = 720;
info->geometry.max_height = 576; info->geometry.max_height = 576;
info->geometry.aspect_ratio = 4.0 / 3.0; info->geometry.aspect_ratio = vaspect_ratio;
info->timing.fps = (double)(system_clock) / (double)lines_per_frame / (double)MCYCLES_PER_LINE; info->timing.fps = (double)(system_clock) / (double)lines_per_frame / (double)MCYCLES_PER_LINE;
info->timing.sample_rate = 44100; info->timing.sample_rate = 44100;
} }
void retro_set_controller_port_device(unsigned port, unsigned device) void retro_set_controller_port_device(unsigned port, unsigned device)
{ {
if (port > 2)
return;
switch(device) switch(device)
{ {
case RETRO_DEVICE_NONE: case RETRO_DEVICE_NONE:
input.system[port] = NO_SYSTEM; input.system[port] = NO_SYSTEM;
break; break;
case RETRO_DEVICE_MDPAD_3B: case RETRO_DEVICE_MDPAD_3B:
config.input[port*4].padtype = DEVICE_PAD3B; {
if (port && (input.system[0] >= RETRO_DEVICE_MDPAD_3B_WAYPLAY) && (input.system[0] <= RETRO_DEVICE_MSPAD_2B_MASTERTAP))
config.input[4].padtype = DEVICE_PAD3B;
else
config.input[port].padtype = DEVICE_PAD3B;
input.system[port] = SYSTEM_GAMEPAD; input.system[port] = SYSTEM_GAMEPAD;
break; break;
}
case RETRO_DEVICE_MDPAD_6B: case RETRO_DEVICE_MDPAD_6B:
config.input[port*4].padtype = DEVICE_PAD6B; {
if (port && (input.system[0] >= RETRO_DEVICE_MDPAD_3B_WAYPLAY) && (input.system[0] <= RETRO_DEVICE_MSPAD_2B_MASTERTAP))
config.input[4].padtype = DEVICE_PAD6B;
else
config.input[port].padtype = DEVICE_PAD6B;
input.system[port] = SYSTEM_GAMEPAD; input.system[port] = SYSTEM_GAMEPAD;
break; break;
}
case RETRO_DEVICE_MSPAD_2B: case RETRO_DEVICE_MSPAD_2B:
config.input[port*4].padtype = DEVICE_PAD2B; {
if (port && (input.system[0] >= RETRO_DEVICE_MDPAD_3B_WAYPLAY) && (input.system[0] <= RETRO_DEVICE_MSPAD_2B_MASTERTAP))
config.input[4].padtype = DEVICE_PAD2B;
else
config.input[port].padtype = DEVICE_PAD2B;
input.system[port] = SYSTEM_GAMEPAD; input.system[port] = SYSTEM_GAMEPAD;
break; break;
}
case RETRO_DEVICE_MDPAD_3B_WAYPLAY: case RETRO_DEVICE_MDPAD_3B_WAYPLAY:
{ {
int i; int i;
@ -1779,9 +1842,14 @@ void retro_set_controller_port_device(unsigned port, unsigned device)
break; break;
case RETRO_DEVICE_JOYPAD: case RETRO_DEVICE_JOYPAD:
default: default:
config.input[port*4].padtype = DEVICE_PAD2B | DEVICE_PAD6B | DEVICE_PAD3B; {
if (port && (input.system[0] >= RETRO_DEVICE_MDPAD_3B_WAYPLAY) && (input.system[0] <= RETRO_DEVICE_MSPAD_2B_MASTERTAP))
config.input[4].padtype = DEVICE_PAD2B | DEVICE_PAD6B | DEVICE_PAD3B;
else
config.input[port].padtype = DEVICE_PAD2B | DEVICE_PAD6B | DEVICE_PAD3B;
input.system[port] = SYSTEM_GAMEPAD; input.system[port] = SYSTEM_GAMEPAD;
break; break;
}
} }
old_system[0] = input.system[0]; old_system[0] = input.system[0];
@ -2082,14 +2150,22 @@ void retro_run(void)
else else
system_frame_sms(0); system_frame_sms(0);
if (bitmap.viewport.changed & 1) if (bitmap.viewport.changed & 9)
{ {
bool geometry_updated = update_viewport();
bitmap.viewport.changed &= ~1; bitmap.viewport.changed &= ~1;
if (update_viewport()) if (bitmap.viewport.changed & 8)
{ {
struct retro_system_av_info info; struct retro_system_av_info info;
retro_get_system_av_info(&info); bitmap.viewport.changed &= ~8;
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info.geometry); retro_get_system_av_info(&info);
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &info);
}
else if (geometry_updated)
{
struct retro_system_av_info info;
retro_get_system_av_info(&info);
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info.geometry);
} }
} }

View File

@ -394,7 +394,7 @@
RelativePath="..\..\..\core\sound\eq.c"> RelativePath="..\..\..\core\sound\eq.c">
</File> </File>
<File <File
RelativePath="..\..\..\core\sound\sn76489.c"> RelativePath="..\..\..\core\sound\psg.c">
</File> </File>
<File <File
RelativePath="..\..\..\core\sound\sound.c"> RelativePath="..\..\..\core\sound\sound.c">

View File

@ -143,7 +143,7 @@
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Release_LTCG|Xbox 360'">CompileAsC</CompileAs> <CompileAs Condition="'$(Configuration)|$(Platform)'=='Release_LTCG|Xbox 360'">CompileAsC</CompileAs>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\core\sound\eq.c" /> <ClCompile Include="..\..\..\core\sound\eq.c" />
<ClCompile Include="..\..\..\core\sound\sn76489.c" /> <ClCompile Include="..\..\..\core\sound\psg.c" />
<ClCompile Include="..\..\..\core\sound\sound.c" /> <ClCompile Include="..\..\..\core\sound\sound.c" />
<ClCompile Include="..\..\..\core\sound\ym2413.c" /> <ClCompile Include="..\..\..\core\sound\ym2413.c" />
<ClCompile Include="..\..\..\core\sound\ym2612.c" /> <ClCompile Include="..\..\..\core\sound\ym2612.c" />

View File

@ -80,7 +80,7 @@
<ClCompile Include="..\..\..\core\sound\eq.c"> <ClCompile Include="..\..\..\core\sound\eq.c">
<Filter>Source Files\sound</Filter> <Filter>Source Files\sound</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\core\sound\sn76489.c"> <ClCompile Include="..\..\..\core\sound\psg.c">
<Filter>Source Files\sound</Filter> <Filter>Source Files\sound</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\core\sound\sound.c"> <ClCompile Include="..\..\..\core\sound\sound.c">

View File

@ -49,7 +49,7 @@
<ClCompile Include="..\..\..\core\ntsc\sms_ntsc.c" /> <ClCompile Include="..\..\..\core\ntsc\sms_ntsc.c" />
<ClCompile Include="..\..\..\core\sound\blip_buf.c" /> <ClCompile Include="..\..\..\core\sound\blip_buf.c" />
<ClCompile Include="..\..\..\core\sound\eq.c" /> <ClCompile Include="..\..\..\core\sound\eq.c" />
<ClCompile Include="..\..\..\core\sound\sn76489.c" /> <ClCompile Include="..\..\..\core\sound\psg.c" />
<ClCompile Include="..\..\..\core\sound\sound.c" /> <ClCompile Include="..\..\..\core\sound\sound.c" />
<ClCompile Include="..\..\..\core\sound\ym2413.c" /> <ClCompile Include="..\..\..\core\sound\ym2413.c" />
<ClCompile Include="..\..\..\core\sound\ym2612.c" /> <ClCompile Include="..\..\..\core\sound\ym2612.c" />

View File

@ -117,7 +117,7 @@
<ClCompile Include="..\..\..\core\sound\eq.c"> <ClCompile Include="..\..\..\core\sound\eq.c">
<Filter>Source Files\sound</Filter> <Filter>Source Files\sound</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\core\sound\sn76489.c"> <ClCompile Include="..\..\..\core\sound\psg.c">
<Filter>Source Files\sound</Filter> <Filter>Source Files\sound</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\core\sound\sound.c"> <ClCompile Include="..\..\..\core\sound\sound.c">

View File

@ -84,7 +84,7 @@ struct
char version[16]; char version[16];
uint8 hq_fm; uint8 hq_fm;
uint8 filter; uint8 filter;
uint8 psgBoostNoise; uint8 hq_psg;
uint8 dac_bits; uint8 dac_bits;
uint8 ym2413; uint8 ym2413;
uint8 mono; uint8 mono;
@ -105,6 +105,7 @@ struct
uint8 bios; uint8 bios;
uint8 lock_on; uint8 lock_on;
uint8 overscan; uint8 overscan;
uint8 aspect_ratio;
uint8 ntsc; uint8 ntsc;
uint8 lcd; uint8 lcd;
uint8 gg_extra; uint8 gg_extra;

View File

@ -69,7 +69,7 @@ OBJECTS += $(OBJDIR)/input.o \
$(OBJDIR)/graphic_board.o $(OBJDIR)/graphic_board.o
OBJECTS += $(OBJDIR)/sound.o \ OBJECTS += $(OBJDIR)/sound.o \
$(OBJDIR)/sn76489.o \ $(OBJDIR)/psg.o \
$(OBJDIR)/ym2413.o \ $(OBJDIR)/ym2413.o \
$(OBJDIR)/ym2612.o $(OBJDIR)/ym2612.o

View File

@ -11,7 +11,7 @@ void set_config_defaults(void)
config.psg_preamp = 150; config.psg_preamp = 150;
config.fm_preamp = 100; config.fm_preamp = 100;
config.hq_fm = 0; config.hq_fm = 0;
config.psgBoostNoise = 1; config.hq_psg = 0;
config.filter = 1; config.filter = 1;
config.low_freq = 200; config.low_freq = 200;
config.high_freq = 8000; config.high_freq = 8000;

View File

@ -19,7 +19,7 @@ typedef struct
{ {
uint8 hq_fm; uint8 hq_fm;
uint8 filter; uint8 filter;
uint8 psgBoostNoise; uint8 hq_psg;
uint8 dac_bits; uint8 dac_bits;
uint8 ym2413; uint8 ym2413;
int16 psg_preamp; int16 psg_preamp;

View File

@ -19,17 +19,21 @@
# -D16BPP_RENDERING - configure for 16-bit pixels (RGB565) # -D16BPP_RENDERING - configure for 16-bit pixels (RGB565)
# -D32BPP_RENDERING - configure for 32-bit pixels (RGB888) # -D32BPP_RENDERING - configure for 32-bit pixels (RGB888)
NAME = gen_sdl.exe NAME = gen_sdl
CC = gcc CC = gcc
CFLAGS = `sdl-config --cflags` -march=i686 -O6 -fomit-frame-pointer -Wall -Wno-strict-aliasing -ansi -std=c89 -pedantic-errors CFLAGS = `sdl-config --cflags` -march=i686 -O6 -fomit-frame-pointer -Wall -Wno-strict-aliasing -ansi -std=c99 -pedantic-errors
#-g -ggdb -pg #-g -ggdb -pg
#-fomit-frame-pointer #-fomit-frame-pointer
#LDFLAGS = -pg #LDFLAGS = -pg
DEFINES = -DLSB_FIRST -DUSE_16BPP_RENDERING -DUSE_LIBTREMOR DEFINES = -DLSB_FIRST -DUSE_16BPP_RENDERING -DUSE_LIBTREMOR -DMAXROMSIZE=33554432
ifneq ($(OS),Windows_NT)
DEFINES += -DHAVE_ALLOCA_H
endif
SRCDIR = ../core SRCDIR = ../core
INCLUDES = -I$(SRCDIR) -I$(SRCDIR)/z80 -I$(SRCDIR)/m68k -I$(SRCDIR)/sound -I$(SRCDIR)/input_hw -I$(SRCDIR)/cart_hw -I$(SRCDIR)/cart_hw/svp -I$(SRCDIR)/cd_hw -I$(SRCDIR)/ntsc -I$(SRCDIR)/tremor -I$(SRCDIR)/../sdl INCLUDES = -I$(SRCDIR) -I$(SRCDIR)/z80 -I$(SRCDIR)/m68k -I$(SRCDIR)/sound -I$(SRCDIR)/input_hw -I$(SRCDIR)/cart_hw -I$(SRCDIR)/cart_hw/svp -I$(SRCDIR)/cd_hw -I$(SRCDIR)/ntsc -I$(SRCDIR)/tremor -I$(SRCDIR)/../sdl -I$(SRCDIR)/../sdl/sdl1
LIBS = `sdl-config --libs` -lz -lm LIBS = `sdl-config --libs` -lz -lm
OBJDIR = ./build_sdl OBJDIR = ./build_sdl
@ -63,7 +67,7 @@ OBJECTS += $(OBJDIR)/input.o \
$(OBJDIR)/graphic_board.o $(OBJDIR)/graphic_board.o
OBJECTS += $(OBJDIR)/sound.o \ OBJECTS += $(OBJDIR)/sound.o \
$(OBJDIR)/sn76489.o \ $(OBJDIR)/psg.o \
$(OBJDIR)/ym2413.o \ $(OBJDIR)/ym2413.o \
$(OBJDIR)/ym2612.o $(OBJDIR)/ym2612.o
@ -114,7 +118,9 @@ OBJECTS += $(OBJDIR)/bitwise.o \
$(OBJDIR)/vorbisfile.o \ $(OBJDIR)/vorbisfile.o \
$(OBJDIR)/window.o $(OBJDIR)/window.o
ifeq ($(OS),Windows_NT)
OBJECTS += $(OBJDIR)/icon.o OBJECTS += $(OBJDIR)/icon.o
endif
all: $(NAME) all: $(NAME)
@ -163,8 +169,13 @@ $(OBJDIR)/%.o : $(SRCDIR)/tremor/%.c
$(OBJDIR)/%.o : $(SRCDIR)/../sdl/%.c $(SRCDIR)/../sdl/%.h $(OBJDIR)/%.o : $(SRCDIR)/../sdl/%.c $(SRCDIR)/../sdl/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@ $(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/../sdl/sdl1/%.c $(SRCDIR)/../sdl/sdl1/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
ifeq ($(OS),Windows_NT)
$(OBJDIR)/icon.o : $(OBJDIR)/icon.o :
windres $(SRCDIR)/../sdl/icon.rc $@ windres $(SRCDIR)/../sdl/icon.rc $@
endif
pack : pack :
strip $(NAME) strip $(NAME)

185
sdl/Makefile.sdl2 Normal file
View File

@ -0,0 +1,185 @@
# Makefile for genplus SDL2
#
# (c) 1999, 2000, 2001, 2002, 2003 Charles MacDonald
# modified by Eke-Eke <eke_eke31@yahoo.fr>
#
# Defines :
# -DLSB_FIRST : for little endian systems.
# -DLOGERROR : enable message logging
# -DLOGVDP : enable VDP debug messages
# -DLOGSOUND : enable AUDIO debug messages
# -DLOG_SCD : enable SCD debug messages
# -DLOG_CDD : enable CDD debug messages
# -DLOG_CDC : enable CDC debug messages
# -DLOG_PCM : enable PCM debug messages
# -DLOGSOUND : enable AUDIO debug messages
# -D8BPP_RENDERING - configure for 8-bit pixels (RGB332)
# -D15BPP_RENDERING - configure for 15-bit pixels (RGB555)
# -D16BPP_RENDERING - configure for 16-bit pixels (RGB565)
# -D32BPP_RENDERING - configure for 32-bit pixels (RGB888)
NAME = gen_sdl2
CC = gcc
CFLAGS = `sdl2-config --cflags` -march=i686 -O6 -fomit-frame-pointer -Wall -Wno-strict-aliasing -ansi -std=c99 -pedantic-errors
#-g -ggdb -pg
#-fomit-frame-pointer
#LDFLAGS = -pg
DEFINES = -DLSB_FIRST -DUSE_16BPP_RENDERING -DUSE_LIBTREMOR -DMAXROMSIZE=33554432
ifneq ($(OS),Windows_NT)
DEFINES += -DHAVE_ALLOCA_H
endif
SRCDIR = ../core
INCLUDES = -I$(SRCDIR) -I$(SRCDIR)/z80 -I$(SRCDIR)/m68k -I$(SRCDIR)/sound -I$(SRCDIR)/input_hw -I$(SRCDIR)/cart_hw -I$(SRCDIR)/cart_hw/svp -I$(SRCDIR)/cd_hw -I$(SRCDIR)/ntsc -I$(SRCDIR)/tremor -I$(SRCDIR)/../sdl -I$(SRCDIR)/../sdl/sdl2
LIBS = `sdl2-config --libs` -lz -lm
OBJDIR = ./build_sdl2
OBJECTS = $(OBJDIR)/z80.o
OBJECTS += $(OBJDIR)/m68kcpu.o \
$(OBJDIR)/s68kcpu.o
OBJECTS += $(OBJDIR)/genesis.o \
$(OBJDIR)/vdp_ctrl.o \
$(OBJDIR)/vdp_render.o \
$(OBJDIR)/system.o \
$(OBJDIR)/io_ctrl.o \
$(OBJDIR)/mem68k.o \
$(OBJDIR)/memz80.o \
$(OBJDIR)/membnk.o \
$(OBJDIR)/state.o \
$(OBJDIR)/loadrom.o
OBJECTS += $(OBJDIR)/input.o \
$(OBJDIR)/gamepad.o \
$(OBJDIR)/lightgun.o \
$(OBJDIR)/mouse.o \
$(OBJDIR)/activator.o \
$(OBJDIR)/xe_1ap.o \
$(OBJDIR)/teamplayer.o \
$(OBJDIR)/paddle.o \
$(OBJDIR)/sportspad.o \
$(OBJDIR)/terebi_oekaki.o \
$(OBJDIR)/graphic_board.o
OBJECTS += $(OBJDIR)/sound.o \
$(OBJDIR)/psg.o \
$(OBJDIR)/ym2413.o \
$(OBJDIR)/ym2612.o
OBJECTS += $(OBJDIR)/blip_buf.o
OBJECTS += $(OBJDIR)/eq.o
OBJECTS += $(OBJDIR)/sram.o \
$(OBJDIR)/svp.o \
$(OBJDIR)/ssp16.o \
$(OBJDIR)/ggenie.o \
$(OBJDIR)/areplay.o \
$(OBJDIR)/eeprom_93c.o \
$(OBJDIR)/eeprom_i2c.o \
$(OBJDIR)/eeprom_spi.o \
$(OBJDIR)/md_cart.o \
$(OBJDIR)/sms_cart.o
OBJECTS += $(OBJDIR)/scd.o \
$(OBJDIR)/cdd.o \
$(OBJDIR)/cdc.o \
$(OBJDIR)/gfx.o \
$(OBJDIR)/pcm.o \
$(OBJDIR)/cd_cart.o
OBJECTS += $(OBJDIR)/sms_ntsc.o \
$(OBJDIR)/md_ntsc.o
OBJECTS += $(OBJDIR)/main.o \
$(OBJDIR)/config.o \
$(OBJDIR)/error.o \
$(OBJDIR)/unzip.o \
$(OBJDIR)/fileio.o
OBJECTS += $(OBJDIR)/bitwise.o \
$(OBJDIR)/block.o \
$(OBJDIR)/codebook.o \
$(OBJDIR)/floor0.o \
$(OBJDIR)/floor1.o \
$(OBJDIR)/framing.o \
$(OBJDIR)/info.o \
$(OBJDIR)/mapping0.o \
$(OBJDIR)/mdct.o \
$(OBJDIR)/registry.o \
$(OBJDIR)/res012.o \
$(OBJDIR)/sharedbook.o \
$(OBJDIR)/synthesis.o \
$(OBJDIR)/vorbisfile.o \
$(OBJDIR)/window.o
ifeq ($(OS),Windows_NT)
OBJECTS += $(OBJDIR)/icon.o
endif
all: $(NAME)
$(NAME): $(OBJDIR) $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) $(LIBS) -o $@
$(OBJDIR) :
@[ -d $@ ] || mkdir -p $@
$(OBJDIR)/%.o : $(SRCDIR)/%.c $(SRCDIR)/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/sound/%.c $(SRCDIR)/sound/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/input_hw/%.c $(SRCDIR)/input_hw/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/cart_hw/%.c $(SRCDIR)/cart_hw/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/cart_hw/svp/%.c
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/cart_hw/svp/%.c $(SRCDIR)/cart_hw/svp/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/cd_hw/%.c $(SRCDIR)/cd_hw/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/z80/%.c $(SRCDIR)/z80/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/m68k/%.c
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/ntsc/%.c $(SRCDIR)/ntsc/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/tremor/%.c $(SRCDIR)/tremor/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/tremor/%.c
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/../sdl/%.c $(SRCDIR)/../sdl/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
$(OBJDIR)/%.o : $(SRCDIR)/../sdl/sdl2/%.c $(SRCDIR)/../sdl/sdl2/%.h
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@
ifeq ($(OS),Windows_NT)
$(OBJDIR)/icon.o :
windres $(SRCDIR)/../sdl/icon.rc $@
endif
pack :
strip $(NAME)
upx -9 $(NAME)
clean:
rm -f $(OBJECTS) $(NAME)

View File

@ -9,7 +9,7 @@ PLEASE DO NOT DISTRIBUTE WIN32 BINARIES WITHOUT THIS NOTICE.
END USERS SHOULD PREFERABLY USE LIBRETRO PORT WITH RETROARCH. END USERS SHOULD PREFERABLY USE LIBRETRO PORT WITH RETROARCH.
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
Genesis Plus (Windows Port) Genesis Plus (SDL Port)
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
based on the original version 1.3 based on the original version 1.3

View File

@ -12,7 +12,7 @@ void set_config_defaults(void)
config.psg_preamp = 150; config.psg_preamp = 150;
config.fm_preamp = 100; config.fm_preamp = 100;
config.hq_fm = 1; config.hq_fm = 1;
config.psgBoostNoise = 1; config.hq_psg = 1;
config.filter = 1; config.filter = 1;
config.low_freq = 200; config.low_freq = 200;
config.high_freq = 8000; config.high_freq = 8000;

View File

@ -15,7 +15,7 @@ typedef struct
{ {
uint8 hq_fm; uint8 hq_fm;
uint8 filter; uint8 filter;
uint8 psgBoostNoise; uint8 hq_psg;
uint8 dac_bits; uint8 dac_bits;
uint8 ym2413; uint8 ym2413;
int16 psg_preamp; int16 psg_preamp;

View File

@ -5,7 +5,9 @@
#include "osd.h" #include "osd.h"
#ifdef LOGERROR
static FILE *error_log; static FILE *error_log;
#endif
void error_init(void) void error_init(void)
{ {

View File

@ -14,7 +14,7 @@
#define SOUND_FREQUENCY 48000 #define SOUND_FREQUENCY 48000
#define SOUND_SAMPLES_SIZE 2048 #define SOUND_SAMPLES_SIZE 2048
#define VIDEO_WIDTH 320 #define VIDEO_WIDTH 320
#define VIDEO_HEIGHT 240 #define VIDEO_HEIGHT 240
int joynum = 0; int joynum = 0;
@ -67,7 +67,7 @@ static int sdl_sound_init()
{ {
int n; int n;
SDL_AudioSpec as_desired, as_obtained; SDL_AudioSpec as_desired, as_obtained;
if(SDL_Init(SDL_INIT_AUDIO) < 0) { if(SDL_Init(SDL_INIT_AUDIO) < 0) {
MessageBox(NULL, "SDL Audio initialization failed", "Error", 0); MessageBox(NULL, "SDL Audio initialization failed", "Error", 0);
return 0; return 0;
@ -101,10 +101,10 @@ static int sdl_sound_init()
return 1; return 1;
} }
static void sdl_sound_update(enabled) static void sdl_sound_update(int enabled)
{ {
int size = audio_update(soundframe) * 2; int size = audio_update(soundframe) * 2;
if (enabled) if (enabled)
{ {
int i; int i;
@ -126,7 +126,7 @@ static void sdl_sound_close()
{ {
SDL_PauseAudio(1); SDL_PauseAudio(1);
SDL_CloseAudio(); SDL_CloseAudio();
if (sdl_sound.buffer) if (sdl_sound.buffer)
free(sdl_sound.buffer); free(sdl_sound.buffer);
} }
@ -196,7 +196,7 @@ static void sdl_video_update()
sdl_video.drect.h = sdl_video.srect.h; sdl_video.drect.h = sdl_video.srect.h;
sdl_video.drect.x = (VIDEO_WIDTH - sdl_video.drect.w) / 2; sdl_video.drect.x = (VIDEO_WIDTH - sdl_video.drect.w) / 2;
sdl_video.drect.y = (VIDEO_HEIGHT - sdl_video.drect.h) / 2; sdl_video.drect.y = (VIDEO_HEIGHT - sdl_video.drect.h) / 2;
/* clear destination surface */ /* clear destination surface */
SDL_FillRect(sdl_video.surf_screen, 0, 0); SDL_FillRect(sdl_video.surf_screen, 0, 0);
@ -304,7 +304,7 @@ static void sdl_sync_close()
SDL_DestroySemaphore(sdl_sync.sem_sync); SDL_DestroySemaphore(sdl_sync.sem_sync);
} }
static const uint16 vc_table[4][2] = static const uint16 vc_table[4][2] =
{ {
/* NTSC, PAL */ /* NTSC, PAL */
{0xDA , 0xF2}, /* Mode 4 (192 lines) */ {0xDA , 0xF2}, /* Mode 4 (192 lines) */
@ -332,8 +332,7 @@ static int sdl_control_update(SDLKey keystate)
case SDLK_F2: case SDLK_F2:
{ {
if (fullscreen) fullscreen = 0; fullscreen = (fullscreen ? 0 : SDL_FULLSCREEN);
else fullscreen = SDL_FULLSCREEN;
sdl_video.surf_screen = SDL_SetVideoMode(VIDEO_WIDTH, VIDEO_HEIGHT, 16, SDL_SWSURFACE | fullscreen); sdl_video.surf_screen = SDL_SetVideoMode(VIDEO_WIDTH, VIDEO_HEIGHT, 16, SDL_SWSURFACE | fullscreen);
break; break;
} }
@ -497,7 +496,7 @@ int sdl_input_update(void)
/* reset input */ /* reset input */
input.pad[joynum] = 0; input.pad[joynum] = 0;
switch (input.dev[joynum]) switch (input.dev[joynum])
{ {
case DEVICE_LIGHTGUN: case DEVICE_LIGHTGUN:
@ -515,7 +514,7 @@ int sdl_input_update(void)
/* TRIGGER, B, C (Menacer only), START (Menacer & Justifier only) */ /* TRIGGER, B, C (Menacer only), START (Menacer & Justifier only) */
if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_A; if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_A;
if(state & SDL_BUTTON_RMASK) input.pad[joynum] |= INPUT_B; if(state & SDL_BUTTON_RMASK) input.pad[joynum] |= INPUT_B;
if(state & SDL_BUTTON_MMASK) input.pad[joynum] |= INPUT_C; if(state & SDL_BUTTON_MMASK) input.pad[joynum] |= INPUT_C;
if(keystate[SDLK_f]) input.pad[joynum] |= INPUT_START; if(keystate[SDLK_f]) input.pad[joynum] |= INPUT_START;
break; break;
} }
@ -586,7 +585,7 @@ int sdl_input_update(void)
if(keystate[SDLK_x]) input.pad[joynum] |= INPUT_X; if(keystate[SDLK_x]) input.pad[joynum] |= INPUT_X;
if(keystate[SDLK_c]) input.pad[joynum] |= INPUT_MODE; if(keystate[SDLK_c]) input.pad[joynum] |= INPUT_MODE;
if(keystate[SDLK_v]) input.pad[joynum] |= INPUT_Z; if(keystate[SDLK_v]) input.pad[joynum] |= INPUT_Z;
/* Left Analog Stick (bidirectional) */ /* Left Analog Stick (bidirectional) */
if(keystate[SDLK_UP]) input.analog[joynum][1]-=2; if(keystate[SDLK_UP]) input.analog[joynum][1]-=2;
else if(keystate[SDLK_DOWN]) input.analog[joynum][1]+=2; else if(keystate[SDLK_DOWN]) input.analog[joynum][1]+=2;
@ -624,7 +623,7 @@ int sdl_input_update(void)
/* Calculate X Y axis values */ /* Calculate X Y axis values */
input.analog[0][0] = 0x3c + (x * (0x17c-0x03c+1)) / VIDEO_WIDTH; input.analog[0][0] = 0x3c + (x * (0x17c-0x03c+1)) / VIDEO_WIDTH;
input.analog[0][1] = 0x1fc + (y * (0x2f7-0x1fc+1)) / VIDEO_HEIGHT; input.analog[0][1] = 0x1fc + (y * (0x2f7-0x1fc+1)) / VIDEO_HEIGHT;
/* Map mouse buttons to player #1 inputs */ /* Map mouse buttons to player #1 inputs */
if(state & SDL_BUTTON_MMASK) pico_current = (pico_current + 1) & 7; if(state & SDL_BUTTON_MMASK) pico_current = (pico_current + 1) & 7;
if(state & SDL_BUTTON_RMASK) input.pad[0] |= INPUT_PICO_RED; if(state & SDL_BUTTON_RMASK) input.pad[0] |= INPUT_PICO_RED;
@ -642,7 +641,7 @@ int sdl_input_update(void)
/* Calculate X Y axis values */ /* Calculate X Y axis values */
input.analog[0][0] = (x * 250) / VIDEO_WIDTH; input.analog[0][0] = (x * 250) / VIDEO_WIDTH;
input.analog[0][1] = (y * 250) / VIDEO_HEIGHT; input.analog[0][1] = (y * 250) / VIDEO_HEIGHT;
/* Map mouse buttons to player #1 inputs */ /* Map mouse buttons to player #1 inputs */
if(state & SDL_BUTTON_RMASK) input.pad[0] |= INPUT_B; if(state & SDL_BUTTON_RMASK) input.pad[0] |= INPUT_B;
@ -658,7 +657,7 @@ int sdl_input_update(void)
/* Calculate X Y axis values */ /* Calculate X Y axis values */
input.analog[0][0] = (x * 255) / VIDEO_WIDTH; input.analog[0][0] = (x * 255) / VIDEO_WIDTH;
input.analog[0][1] = (y * 255) / VIDEO_HEIGHT; input.analog[0][1] = (y * 255) / VIDEO_HEIGHT;
/* Map mouse buttons to player #1 inputs */ /* Map mouse buttons to player #1 inputs */
if(state & SDL_BUTTON_LMASK) input.pad[0] |= INPUT_GRAPHIC_PEN; if(state & SDL_BUTTON_LMASK) input.pad[0] |= INPUT_GRAPHIC_PEN;
if(state & SDL_BUTTON_RMASK) input.pad[0] |= INPUT_GRAPHIC_MENU; if(state & SDL_BUTTON_RMASK) input.pad[0] |= INPUT_GRAPHIC_MENU;
@ -711,7 +710,7 @@ int main (int argc, char **argv)
char caption[256]; char caption[256];
sprintf(caption, "Genesis Plus GX\\SDL\nusage: %s gamename\n", argv[0]); sprintf(caption, "Genesis Plus GX\\SDL\nusage: %s gamename\n", argv[0]);
MessageBox(NULL, caption, "Information", 0); MessageBox(NULL, caption, "Information", 0);
exit(1); return 1;
} }
/* set default config */ /* set default config */
@ -751,10 +750,8 @@ int main (int argc, char **argv)
/* initialize SDL */ /* initialize SDL */
if(SDL_Init(0) < 0) if(SDL_Init(0) < 0)
{ {
char caption[256]; MessageBox(NULL, "SDL initialization failed", "Error", 0);
sprintf(caption, "SDL initialization failed"); return 1;
MessageBox(NULL, caption, "Error", 0);
exit(1);
} }
sdl_video_init(); sdl_video_init();
if (use_sound) sdl_sound_init(); if (use_sound) sdl_sound_init();
@ -784,7 +781,7 @@ int main (int argc, char **argv)
char caption[256]; char caption[256];
sprintf(caption, "Error loading file `%s'.", argv[1]); sprintf(caption, "Error loading file `%s'.", argv[1]);
MessageBox(NULL, caption, "Error", 0); MessageBox(NULL, caption, "Error", 0);
exit(1); return 1;
} }
/* initialize system hardware */ /* initialize system hardware */
@ -866,14 +863,14 @@ int main (int argc, char **argv)
while(running) while(running)
{ {
SDL_Event event; SDL_Event event;
if (SDL_PollEvent(&event)) if (SDL_PollEvent(&event))
{ {
switch(event.type) switch(event.type)
{ {
case SDL_USEREVENT: case SDL_USEREVENT:
{ {
char caption[100]; char caption[100];
sprintf(caption,"Genesis Plus GX - %d fps - %s)", event.user.code, (rominfo.international[0] != 0x20) ? rominfo.international : rominfo.domestic); sprintf(caption,"Genesis Plus GX - %d fps - %s", event.user.code, (rominfo.international[0] != 0x20) ? rominfo.international : rominfo.domestic);
SDL_WM_SetCaption(caption, NULL); SDL_WM_SetCaption(caption, NULL);
break; break;
} }

945
sdl/sdl2/main.c Normal file
View File

@ -0,0 +1,945 @@
#include "SDL.h"
#include "SDL_thread.h"
#include "shared.h"
#include "sms_ntsc.h"
#include "md_ntsc.h"
#define SOUND_FREQUENCY 48000
#define SOUND_SAMPLES_SIZE 2048
#define VIDEO_WIDTH 320
#define VIDEO_HEIGHT 240
int joynum = 0;
int log_error = 0;
int debug_on = 0;
int turbo_mode = 0;
int use_sound = 1;
int fullscreen = 0; /* SDL_WINDOW_FULLSCREEN */
struct {
SDL_Window* window;
SDL_Surface* surf_screen;
SDL_Surface* surf_bitmap;
SDL_Rect srect;
SDL_Rect drect;
Uint32 frames_rendered;
} sdl_video;
/* sound */
struct {
char* current_pos;
char* buffer;
int current_emulated_samples;
} sdl_sound;
static uint8 brm_format[0x40] =
{
0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x00,0x00,0x00,0x00,0x40,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x53,0x45,0x47,0x41,0x5f,0x43,0x44,0x5f,0x52,0x4f,0x4d,0x00,0x01,0x00,0x00,0x00,
0x52,0x41,0x4d,0x5f,0x43,0x41,0x52,0x54,0x52,0x49,0x44,0x47,0x45,0x5f,0x5f,0x5f
};
static short soundframe[SOUND_SAMPLES_SIZE];
static void sdl_sound_callback(void *userdata, Uint8 *stream, int len)
{
if(sdl_sound.current_emulated_samples < len) {
memset(stream, 0, len);
}
else {
memcpy(stream, sdl_sound.buffer, len);
/* loop to compensate desync */
do {
sdl_sound.current_emulated_samples -= len;
} while(sdl_sound.current_emulated_samples > 2 * len);
memcpy(sdl_sound.buffer,
sdl_sound.current_pos - sdl_sound.current_emulated_samples,
sdl_sound.current_emulated_samples);
sdl_sound.current_pos = sdl_sound.buffer + sdl_sound.current_emulated_samples;
}
}
static int sdl_sound_init()
{
int n;
SDL_AudioSpec as_desired, as_obtained;
if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "SDL Audio initialization failed", sdl_video.window);
return 0;
}
as_desired.freq = SOUND_FREQUENCY;
as_desired.format = AUDIO_S16LSB;
as_desired.channels = 2;
as_desired.samples = SOUND_SAMPLES_SIZE;
as_desired.callback = sdl_sound_callback;
if(SDL_OpenAudio(&as_desired, &as_obtained) == -1) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "SDL Audio open failed", sdl_video.window);
return 0;
}
if(as_desired.samples != as_obtained.samples) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "SDL Audio wrong setup", sdl_video.window);
return 0;
}
sdl_sound.current_emulated_samples = 0;
n = SOUND_SAMPLES_SIZE * 2 * sizeof(short) * 20;
sdl_sound.buffer = (char*)malloc(n);
if(!sdl_sound.buffer) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Can't allocate audio buffer", sdl_video.window);
return 0;
}
memset(sdl_sound.buffer, 0, n);
sdl_sound.current_pos = sdl_sound.buffer;
return 1;
}
static void sdl_sound_update(int enabled)
{
int size = audio_update(soundframe) * 2;
if (enabled)
{
int i;
short *out;
SDL_LockAudio();
out = (short*)sdl_sound.current_pos;
for(i = 0; i < size; i++)
{
*out++ = soundframe[i];
}
sdl_sound.current_pos = (char*)out;
sdl_sound.current_emulated_samples += size * sizeof(short);
SDL_UnlockAudio();
}
}
static void sdl_sound_close()
{
SDL_PauseAudio(1);
SDL_CloseAudio();
if (sdl_sound.buffer)
free(sdl_sound.buffer);
}
/* video */
md_ntsc_t *md_ntsc;
sms_ntsc_t *sms_ntsc;
static int sdl_video_init()
{
if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "SDL Video initialization failed", sdl_video.window);
return 0;
}
sdl_video.window = SDL_CreateWindow("Genesis Plus GX", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, VIDEO_WIDTH, VIDEO_HEIGHT, 0);
sdl_video.surf_screen = SDL_GetWindowSurface(sdl_video.window);
sdl_video.surf_bitmap = SDL_CreateRGBSurface(SDL_SWSURFACE, 720, 576, 16, 0, 0, 0, 0);
sdl_video.frames_rendered = 0;
SDL_ShowCursor(0);
return 1;
}
static void sdl_video_update()
{
if (system_hw == SYSTEM_MCD)
{
system_frame_scd(0);
}
else if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
system_frame_gen(0);
}
else
{
system_frame_sms(0);
}
/* viewport size changed */
if(bitmap.viewport.changed & 1)
{
bitmap.viewport.changed &= ~1;
/* source bitmap */
sdl_video.srect.w = bitmap.viewport.w+2*bitmap.viewport.x;
sdl_video.srect.h = bitmap.viewport.h+2*bitmap.viewport.y;
sdl_video.srect.x = 0;
sdl_video.srect.y = 0;
if (sdl_video.srect.w > VIDEO_WIDTH)
{
sdl_video.srect.x = (sdl_video.srect.w - VIDEO_WIDTH) / 2;
sdl_video.srect.w = VIDEO_WIDTH;
}
if (sdl_video.srect.h > VIDEO_HEIGHT)
{
sdl_video.srect.y = (sdl_video.srect.h - VIDEO_HEIGHT) / 2;
sdl_video.srect.h = VIDEO_HEIGHT;
}
/* destination bitmap */
sdl_video.drect.w = sdl_video.srect.w;
sdl_video.drect.h = sdl_video.srect.h;
sdl_video.drect.x = (VIDEO_WIDTH - sdl_video.drect.w) / 2;
sdl_video.drect.y = (VIDEO_HEIGHT - sdl_video.drect.h) / 2;
/* clear destination surface */
SDL_FillRect(sdl_video.surf_screen, 0, 0);
#if 0
if (config.render && (interlaced || config.ntsc)) rect.h *= 2;
if (config.ntsc) rect.w = (reg[12]&1) ? MD_NTSC_OUT_WIDTH(rect.w) : SMS_NTSC_OUT_WIDTH(rect.w);
if (config.ntsc)
{
sms_ntsc = (sms_ntsc_t *)malloc(sizeof(sms_ntsc_t));
md_ntsc = (md_ntsc_t *)malloc(sizeof(md_ntsc_t));
switch (config.ntsc)
{
case 1:
sms_ntsc_init(sms_ntsc, &sms_ntsc_composite);
md_ntsc_init(md_ntsc, &md_ntsc_composite);
break;
case 2:
sms_ntsc_init(sms_ntsc, &sms_ntsc_svideo);
md_ntsc_init(md_ntsc, &md_ntsc_svideo);
break;
case 3:
sms_ntsc_init(sms_ntsc, &sms_ntsc_rgb);
md_ntsc_init(md_ntsc, &md_ntsc_rgb);
break;
}
}
else
{
if (sms_ntsc)
{
free(sms_ntsc);
sms_ntsc = NULL;
}
if (md_ntsc)
{
free(md_ntsc);
md_ntsc = NULL;
}
}
#endif
}
SDL_BlitSurface(sdl_video.surf_bitmap, &sdl_video.srect, sdl_video.surf_screen, &sdl_video.drect);
SDL_UpdateWindowSurface(sdl_video.window);
++sdl_video.frames_rendered;
}
static void sdl_video_close()
{
if (sdl_video.surf_bitmap)
SDL_FreeSurface(sdl_video.surf_bitmap);
if (sdl_video.surf_screen)
SDL_FreeSurface(sdl_video.surf_screen);
}
/* Timer Sync */
struct {
SDL_sem* sem_sync;
unsigned ticks;
} sdl_sync;
static Uint32 sdl_sync_timer_callback(Uint32 interval, void *param)
{
SDL_SemPost(sdl_sync.sem_sync);
sdl_sync.ticks++;
if (sdl_sync.ticks == (vdp_pal ? 50 : 20))
{
SDL_Event event;
SDL_UserEvent userevent;
userevent.type = SDL_USEREVENT;
userevent.code = vdp_pal ? (sdl_video.frames_rendered / 3) : sdl_video.frames_rendered;
userevent.data1 = NULL;
userevent.data2 = NULL;
sdl_sync.ticks = sdl_video.frames_rendered = 0;
event.type = SDL_USEREVENT;
event.user = userevent;
SDL_PushEvent(&event);
}
return interval;
}
static int sdl_sync_init()
{
if(SDL_InitSubSystem(SDL_INIT_TIMER|SDL_INIT_EVENTS) < 0)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "SDL Timer initialization failed", sdl_video.window);
return 0;
}
sdl_sync.sem_sync = SDL_CreateSemaphore(0);
sdl_sync.ticks = 0;
return 1;
}
static void sdl_sync_close()
{
if(sdl_sync.sem_sync)
SDL_DestroySemaphore(sdl_sync.sem_sync);
}
static const uint16 vc_table[4][2] =
{
/* NTSC, PAL */
{0xDA , 0xF2}, /* Mode 4 (192 lines) */
{0xEA , 0x102}, /* Mode 5 (224 lines) */
{0xDA , 0xF2}, /* Mode 4 (192 lines) */
{0x106, 0x10A} /* Mode 5 (240 lines) */
};
static int sdl_control_update(SDL_Keycode keystate)
{
switch (keystate)
{
case SDLK_TAB:
{
system_reset();
break;
}
case SDLK_F1:
{
if (SDL_ShowCursor(-1)) SDL_ShowCursor(0);
else SDL_ShowCursor(1);
break;
}
case SDLK_F2:
{
fullscreen = (fullscreen ? 0 : SDL_WINDOW_FULLSCREEN);
SDL_SetWindowFullscreen(sdl_video.window, fullscreen);
break;
}
case SDLK_F3:
{
if (config.bios == 0) config.bios = 3;
else if (config.bios == 3) config.bios = 1;
break;
}
case SDLK_F4:
{
if (!turbo_mode) use_sound ^= 1;
break;
}
case SDLK_F5:
{
log_error ^= 1;
break;
}
case SDLK_F6:
{
if (!use_sound)
{
turbo_mode ^=1;
sdl_sync.ticks = 0;
}
break;
}
case SDLK_F7:
{
FILE *f = fopen("game.gp0","rb");
if (f)
{
uint8 buf[STATE_SIZE];
fread(&buf, STATE_SIZE, 1, f);
state_load(buf);
fclose(f);
}
break;
}
case SDLK_F8:
{
FILE *f = fopen("game.gp0","wb");
if (f)
{
uint8 buf[STATE_SIZE];
int len = state_save(buf);
fwrite(&buf, len, 1, f);
fclose(f);
}
break;
}
case SDLK_F9:
{
config.region_detect = (config.region_detect + 1) % 5;
get_region(0);
/* framerate has changed, reinitialize audio timings */
audio_init(snd.sample_rate, 0);
/* system with region BIOS should be reinitialized */
if ((system_hw == SYSTEM_MCD) || ((system_hw & SYSTEM_SMS) && (config.bios & 1)))
{
system_init();
system_reset();
}
else
{
/* reinitialize I/O region register */
if (system_hw == SYSTEM_MD)
{
io_reg[0x00] = 0x20 | region_code | (config.bios & 1);
}
else
{
io_reg[0x00] = 0x80 | (region_code >> 1);
}
/* reinitialize VDP */
if (vdp_pal)
{
status |= 1;
lines_per_frame = 313;
}
else
{
status &= ~1;
lines_per_frame = 262;
}
/* reinitialize VC max value */
switch (bitmap.viewport.h)
{
case 192:
vc_max = vc_table[0][vdp_pal];
break;
case 224:
vc_max = vc_table[1][vdp_pal];
break;
case 240:
vc_max = vc_table[3][vdp_pal];
break;
}
}
break;
}
case SDLK_F10:
{
gen_reset(0);
break;
}
case SDLK_F11:
{
config.overscan = (config.overscan + 1) & 3;
if ((system_hw == SYSTEM_GG) && !config.gg_extra)
{
bitmap.viewport.x = (config.overscan & 2) ? 14 : -48;
}
else
{
bitmap.viewport.x = (config.overscan & 2) * 7;
}
bitmap.viewport.changed = 3;
break;
}
case SDLK_F12:
{
joynum = (joynum + 1) % MAX_DEVICES;
while (input.dev[joynum] == NO_DEVICE)
{
joynum = (joynum + 1) % MAX_DEVICES;
}
break;
}
case SDLK_ESCAPE:
{
return 0;
}
default:
break;
}
return 1;
}
int sdl_input_update(void)
{
const uint8 *keystate = SDL_GetKeyboardState(NULL);
/* reset input */
input.pad[joynum] = 0;
switch (input.dev[joynum])
{
case DEVICE_LIGHTGUN:
{
/* get mouse coordinates (absolute values) */
int x,y;
int state = SDL_GetMouseState(&x,&y);
/* X axis */
input.analog[joynum][0] = x - (VIDEO_WIDTH-bitmap.viewport.w)/2;
/* Y axis */
input.analog[joynum][1] = y - (VIDEO_HEIGHT-bitmap.viewport.h)/2;
/* TRIGGER, B, C (Menacer only), START (Menacer & Justifier only) */
if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_A;
if(state & SDL_BUTTON_RMASK) input.pad[joynum] |= INPUT_B;
if(state & SDL_BUTTON_MMASK) input.pad[joynum] |= INPUT_C;
if(keystate[SDL_SCANCODE_F]) input.pad[joynum] |= INPUT_START;
break;
}
case DEVICE_PADDLE:
{
/* get mouse (absolute values) */
int x;
int state = SDL_GetMouseState(&x, NULL);
/* Range is [0;256], 128 being middle position */
input.analog[joynum][0] = x * 256 /VIDEO_WIDTH;
/* Button I -> 0 0 0 0 0 0 0 I*/
if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_B;
break;
}
case DEVICE_SPORTSPAD:
{
/* get mouse (relative values) */
int x,y;
int state = SDL_GetRelativeMouseState(&x,&y);
/* Range is [0;256] */
input.analog[joynum][0] = (unsigned char)(-x & 0xFF);
input.analog[joynum][1] = (unsigned char)(-y & 0xFF);
/* Buttons I & II -> 0 0 0 0 0 0 II I*/
if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_B;
if(state & SDL_BUTTON_RMASK) input.pad[joynum] |= INPUT_C;
break;
}
case DEVICE_MOUSE:
{
/* get mouse (relative values) */
int x,y;
int state = SDL_GetRelativeMouseState(&x,&y);
/* Sega Mouse range is [-256;+256] */
input.analog[joynum][0] = x * 2;
input.analog[joynum][1] = y * 2;
/* Vertical movement is upsidedown */
if (!config.invert_mouse)
input.analog[joynum][1] = 0 - input.analog[joynum][1];
/* Start,Left,Right,Middle buttons -> 0 0 0 0 START MIDDLE RIGHT LEFT */
if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_B;
if(state & SDL_BUTTON_RMASK) input.pad[joynum] |= INPUT_C;
if(state & SDL_BUTTON_MMASK) input.pad[joynum] |= INPUT_A;
if(keystate[SDL_SCANCODE_F]) input.pad[joynum] |= INPUT_START;
break;
}
case DEVICE_XE_1AP:
{
/* A,B,C,D,Select,START,E1,E2 buttons -> E1(?) E2(?) START SELECT(?) A B C D */
if(keystate[SDL_SCANCODE_A]) input.pad[joynum] |= INPUT_START;
if(keystate[SDL_SCANCODE_S]) input.pad[joynum] |= INPUT_A;
if(keystate[SDL_SCANCODE_D]) input.pad[joynum] |= INPUT_C;
if(keystate[SDL_SCANCODE_F]) input.pad[joynum] |= INPUT_Y;
if(keystate[SDL_SCANCODE_Z]) input.pad[joynum] |= INPUT_B;
if(keystate[SDL_SCANCODE_X]) input.pad[joynum] |= INPUT_X;
if(keystate[SDL_SCANCODE_C]) input.pad[joynum] |= INPUT_MODE;
if(keystate[SDL_SCANCODE_V]) input.pad[joynum] |= INPUT_Z;
/* Left Analog Stick (bidirectional) */
if(keystate[SDL_SCANCODE_UP]) input.analog[joynum][1]-=2;
else if(keystate[SDL_SCANCODE_DOWN]) input.analog[joynum][1]+=2;
else input.analog[joynum][1] = 128;
if(keystate[SDL_SCANCODE_LEFT]) input.analog[joynum][0]-=2;
else if(keystate[SDL_SCANCODE_RIGHT]) input.analog[joynum][0]+=2;
else input.analog[joynum][0] = 128;
/* Right Analog Stick (unidirectional) */
if(keystate[SDL_SCANCODE_KP_8]) input.analog[joynum+1][0]-=2;
else if(keystate[SDL_SCANCODE_KP_2]) input.analog[joynum+1][0]+=2;
else if(keystate[SDL_SCANCODE_KP_4]) input.analog[joynum+1][0]-=2;
else if(keystate[SDL_SCANCODE_KP_6]) input.analog[joynum+1][0]+=2;
else input.analog[joynum+1][0] = 128;
/* Limiters */
if (input.analog[joynum][0] > 0xFF) input.analog[joynum][0] = 0xFF;
else if (input.analog[joynum][0] < 0) input.analog[joynum][0] = 0;
if (input.analog[joynum][1] > 0xFF) input.analog[joynum][1] = 0xFF;
else if (input.analog[joynum][1] < 0) input.analog[joynum][1] = 0;
if (input.analog[joynum+1][0] > 0xFF) input.analog[joynum+1][0] = 0xFF;
else if (input.analog[joynum+1][0] < 0) input.analog[joynum+1][0] = 0;
if (input.analog[joynum+1][1] > 0xFF) input.analog[joynum+1][1] = 0xFF;
else if (input.analog[joynum+1][1] < 0) input.analog[joynum+1][1] = 0;
break;
}
case DEVICE_PICO:
{
/* get mouse (absolute values) */
int x,y;
int state = SDL_GetMouseState(&x,&y);
/* Calculate X Y axis values */
input.analog[0][0] = 0x3c + (x * (0x17c-0x03c+1)) / VIDEO_WIDTH;
input.analog[0][1] = 0x1fc + (y * (0x2f7-0x1fc+1)) / VIDEO_HEIGHT;
/* Map mouse buttons to player #1 inputs */
if(state & SDL_BUTTON_MMASK) pico_current = (pico_current + 1) & 7;
if(state & SDL_BUTTON_RMASK) input.pad[0] |= INPUT_PICO_RED;
if(state & SDL_BUTTON_LMASK) input.pad[0] |= INPUT_PICO_PEN;
break;
}
case DEVICE_TEREBI:
{
/* get mouse (absolute values) */
int x,y;
int state = SDL_GetMouseState(&x,&y);
/* Calculate X Y axis values */
input.analog[0][0] = (x * 250) / VIDEO_WIDTH;
input.analog[0][1] = (y * 250) / VIDEO_HEIGHT;
/* Map mouse buttons to player #1 inputs */
if(state & SDL_BUTTON_RMASK) input.pad[0] |= INPUT_B;
break;
}
case DEVICE_GRAPHIC_BOARD:
{
/* get mouse (absolute values) */
int x,y;
int state = SDL_GetMouseState(&x,&y);
/* Calculate X Y axis values */
input.analog[0][0] = (x * 255) / VIDEO_WIDTH;
input.analog[0][1] = (y * 255) / VIDEO_HEIGHT;
/* Map mouse buttons to player #1 inputs */
if(state & SDL_BUTTON_LMASK) input.pad[0] |= INPUT_GRAPHIC_PEN;
if(state & SDL_BUTTON_RMASK) input.pad[0] |= INPUT_GRAPHIC_MENU;
if(state & SDL_BUTTON_MMASK) input.pad[0] |= INPUT_GRAPHIC_DO;
break;
}
case DEVICE_ACTIVATOR:
{
if(keystate[SDL_SCANCODE_G]) input.pad[joynum] |= INPUT_ACTIVATOR_7L;
if(keystate[SDL_SCANCODE_H]) input.pad[joynum] |= INPUT_ACTIVATOR_7U;
if(keystate[SDL_SCANCODE_J]) input.pad[joynum] |= INPUT_ACTIVATOR_8L;
if(keystate[SDL_SCANCODE_K]) input.pad[joynum] |= INPUT_ACTIVATOR_8U;
}
default:
{
if(keystate[SDL_SCANCODE_A]) input.pad[joynum] |= INPUT_A;
if(keystate[SDL_SCANCODE_S]) input.pad[joynum] |= INPUT_B;
if(keystate[SDL_SCANCODE_D]) input.pad[joynum] |= INPUT_C;
if(keystate[SDL_SCANCODE_F]) input.pad[joynum] |= INPUT_START;
if(keystate[SDL_SCANCODE_Z]) input.pad[joynum] |= INPUT_X;
if(keystate[SDL_SCANCODE_X]) input.pad[joynum] |= INPUT_Y;
if(keystate[SDL_SCANCODE_C]) input.pad[joynum] |= INPUT_Z;
if(keystate[SDL_SCANCODE_V]) input.pad[joynum] |= INPUT_MODE;
if(keystate[SDL_SCANCODE_UP]) input.pad[joynum] |= INPUT_UP;
else
if(keystate[SDL_SCANCODE_DOWN]) input.pad[joynum] |= INPUT_DOWN;
if(keystate[SDL_SCANCODE_LEFT]) input.pad[joynum] |= INPUT_LEFT;
else
if(keystate[SDL_SCANCODE_RIGHT]) input.pad[joynum] |= INPUT_RIGHT;
break;
}
}
return 1;
}
int main (int argc, char **argv)
{
FILE *fp;
int running = 1;
/* Print help if no game specified */
if(argc < 2)
{
char caption[256];
sprintf(caption, "Genesis Plus GX\\SDL\nusage: %s gamename\n", argv[0]);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Information", caption, sdl_video.window);
return 1;
}
/* set default config */
error_init();
set_config_defaults();
/* mark all BIOS as unloaded */
system_bios = 0;
/* Genesis BOOT ROM support (2KB max) */
memset(boot_rom, 0xFF, 0x800);
fp = fopen(MD_BIOS, "rb");
if (fp != NULL)
{
int i;
/* read BOOT ROM */
fread(boot_rom, 1, 0x800, fp);
fclose(fp);
/* check BOOT ROM */
if (!memcmp((char *)(boot_rom + 0x120),"GENESIS OS", 10))
{
/* mark Genesis BIOS as loaded */
system_bios = SYSTEM_MD;
}
/* Byteswap ROM */
for (i=0; i<0x800; i+=2)
{
uint8 temp = boot_rom[i];
boot_rom[i] = boot_rom[i+1];
boot_rom[i+1] = temp;
}
}
/* initialize SDL */
if(SDL_Init(0) < 0)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "SDL initialization failed", sdl_video.window);
return 1;
}
sdl_video_init();
if (use_sound) sdl_sound_init();
sdl_sync_init();
/* initialize Genesis virtual system */
SDL_LockSurface(sdl_video.surf_bitmap);
memset(&bitmap, 0, sizeof(t_bitmap));
bitmap.width = 720;
bitmap.height = 576;
#if defined(USE_8BPP_RENDERING)
bitmap.pitch = (bitmap.width * 1);
#elif defined(USE_15BPP_RENDERING)
bitmap.pitch = (bitmap.width * 2);
#elif defined(USE_16BPP_RENDERING)
bitmap.pitch = (bitmap.width * 2);
#elif defined(USE_32BPP_RENDERING)
bitmap.pitch = (bitmap.width * 4);
#endif
bitmap.data = sdl_video.surf_bitmap->pixels;
SDL_UnlockSurface(sdl_video.surf_bitmap);
bitmap.viewport.changed = 3;
/* Load game file */
if(!load_rom(argv[1]))
{
char caption[256];
sprintf(caption, "Error loading file `%s'.", argv[1]);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", caption, sdl_video.window);
return 1;
}
/* initialize system hardware */
audio_init(SOUND_FREQUENCY, 0);
system_init();
/* Mega CD specific */
if (system_hw == SYSTEM_MCD)
{
/* load internal backup RAM */
fp = fopen("./scd.brm", "rb");
if (fp!=NULL)
{
fread(scd.bram, 0x2000, 1, fp);
fclose(fp);
}
/* check if internal backup RAM is formatted */
if (memcmp(scd.bram + 0x2000 - 0x20, brm_format + 0x20, 0x20))
{
/* clear internal backup RAM */
memset(scd.bram, 0x00, 0x200);
/* Internal Backup RAM size fields */
brm_format[0x10] = brm_format[0x12] = brm_format[0x14] = brm_format[0x16] = 0x00;
brm_format[0x11] = brm_format[0x13] = brm_format[0x15] = brm_format[0x17] = (sizeof(scd.bram) / 64) - 3;
/* format internal backup RAM */
memcpy(scd.bram + 0x2000 - 0x40, brm_format, 0x40);
}
/* load cartridge backup RAM */
if (scd.cartridge.id)
{
fp = fopen("./cart.brm", "rb");
if (fp!=NULL)
{
fread(scd.cartridge.area, scd.cartridge.mask + 1, 1, fp);
fclose(fp);
}
/* check if cartridge backup RAM is formatted */
if (memcmp(scd.cartridge.area + scd.cartridge.mask + 1 - 0x20, brm_format + 0x20, 0x20))
{
/* clear cartridge backup RAM */
memset(scd.cartridge.area, 0x00, scd.cartridge.mask + 1);
/* Cartridge Backup RAM size fields */
brm_format[0x10] = brm_format[0x12] = brm_format[0x14] = brm_format[0x16] = (((scd.cartridge.mask + 1) / 64) - 3) >> 8;
brm_format[0x11] = brm_format[0x13] = brm_format[0x15] = brm_format[0x17] = (((scd.cartridge.mask + 1) / 64) - 3) & 0xff;
/* format cartridge backup RAM */
memcpy(scd.cartridge.area + scd.cartridge.mask + 1 - sizeof(brm_format), brm_format, sizeof(brm_format));
}
}
}
if (sram.on)
{
/* load SRAM */
fp = fopen("./game.srm", "rb");
if (fp!=NULL)
{
fread(sram.sram,0x10000,1, fp);
fclose(fp);
}
}
/* reset system hardware */
system_reset();
if(use_sound) SDL_PauseAudio(0);
/* 3 frames = 50 ms (60hz) or 60 ms (50hz) */
if(sdl_sync.sem_sync)
SDL_AddTimer(vdp_pal ? 60 : 50, sdl_sync_timer_callback, NULL);
/* emulation loop */
while(running)
{
SDL_Event event;
if (SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_USEREVENT:
{
char caption[100];
sprintf(caption,"Genesis Plus GX - %d fps - %s", event.user.code, (rominfo.international[0] != 0x20) ? rominfo.international : rominfo.domestic);
SDL_SetWindowTitle(sdl_video.window, caption);
break;
}
case SDL_QUIT:
{
running = 0;
break;
}
case SDL_KEYDOWN:
{
running = sdl_control_update(event.key.keysym.sym);
break;
}
}
}
sdl_video_update();
sdl_sound_update(use_sound);
if(!turbo_mode && sdl_sync.sem_sync && sdl_video.frames_rendered % 3 == 0)
{
SDL_SemWait(sdl_sync.sem_sync);
}
}
if (system_hw == SYSTEM_MCD)
{
/* save internal backup RAM (if formatted) */
if (!memcmp(scd.bram + 0x2000 - 0x20, brm_format + 0x20, 0x20))
{
fp = fopen("./scd.brm", "wb");
if (fp!=NULL)
{
fwrite(scd.bram, 0x2000, 1, fp);
fclose(fp);
}
}
/* save cartridge backup RAM (if formatted) */
if (scd.cartridge.id)
{
if (!memcmp(scd.cartridge.area + scd.cartridge.mask + 1 - 0x20, brm_format + 0x20, 0x20))
{
fp = fopen("./cart.brm", "wb");
if (fp!=NULL)
{
fwrite(scd.cartridge.area, scd.cartridge.mask + 1, 1, fp);
fclose(fp);
}
}
}
}
if (sram.on)
{
/* save SRAM */
fp = fopen("./game.srm", "wb");
if (fp!=NULL)
{
fwrite(sram.sram,0x10000,1, fp);
fclose(fp);
}
}
audio_shutdown();
error_shutdown();
sdl_video_close();
sdl_sound_close();
sdl_sync_close();
SDL_Quit();
return 0;
}

11
sdl/sdl2/main.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _MAIN_H_
#define _MAIN_H_
#define MAX_INPUTS 8
extern int debug_on;
extern int log_error;
extern int sdl_input_update(void);
#endif /* _MAIN_H_ */