mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-11-13 06:15:07 +01:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
0247ea6202
5
.gitignore
vendored
5
.gitignore
vendored
@ -3,3 +3,8 @@ psp2/*.elf
|
||||
psp2/*.velf
|
||||
psp2/build_vita/*
|
||||
psp2/*.a
|
||||
sdl/gen_sdl
|
||||
sdl/gen_sdl2
|
||||
sdl/build_sdl
|
||||
sdl/build_sdl2
|
||||
|
||||
|
@ -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 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 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 state loading bug when SUB-CPU interrupt is pending
|
||||
* 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)
|
||||
* 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]
|
||||
---------------
|
||||
* added configurable BIOS & Lock-on ROM files
|
||||
|
@ -27,7 +27,7 @@ INCLUDES := core core/m68k core/z80 core/sound core/tremor core/ntsc core/input_
|
||||
# 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)
|
||||
|
||||
LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map
|
||||
|
@ -27,7 +27,7 @@ INCLUDES := core core/m68k core/z80 core/sound core/tremor core/ntsc core/input_
|
||||
# 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)
|
||||
|
||||
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 |
@ -2,7 +2,7 @@
|
||||
* Genesis Plus
|
||||
* 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
|
||||
* provided that the following conditions are met:
|
||||
@ -68,7 +68,17 @@ void sram_init()
|
||||
sram.sram = cart.rom + 0x800000;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* retrieve informations from header */
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Genesis Plus
|
||||
* 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
|
||||
* provided that the following conditions are met:
|
||||
|
144
core/cd_hw/cdd.c
144
core/cd_hw/cdd.c
@ -123,10 +123,10 @@ static const uint32 toc_ffightj[29] =
|
||||
};
|
||||
|
||||
/* 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,
|
||||
0x44,0xac,0x00,0x00,0x10,0xb1,0x02,0x00,0x04,0x00,0x10,0x00,0x64,0x61,0x74,0x61
|
||||
0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,0x10,0x00,0x00,0x00,0x01,0x00,
|
||||
0x02,0x00,0x44,0xac,0x00,0x00,0x10,0xb1,0x02,0x00,0x04,0x00,0x10,0x00
|
||||
};
|
||||
|
||||
/* supported WAVE file extensions */
|
||||
@ -179,8 +179,7 @@ void cdd_init(int samplerate)
|
||||
{
|
||||
/* CD-DA is running by default at 44100 Hz */
|
||||
/* 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][1], 44100, samplerate);
|
||||
blip_set_rates(snd.blips[2], 44100, samplerate);
|
||||
}
|
||||
|
||||
void cdd_reset(void)
|
||||
@ -449,16 +448,39 @@ int cdd_load(char *filename, char *header)
|
||||
if (!strstr(lptr,"BINARY") && !strstr(lptr,"MOTOROLA"))
|
||||
{
|
||||
/* read file header */
|
||||
unsigned char head[32];
|
||||
unsigned char head[28];
|
||||
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);
|
||||
|
||||
/* 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 */
|
||||
cdd.toc.tracks[cdd.toc.last].offset -= 44;
|
||||
cdd.toc.tracks[cdd.toc.last].offset -= dataOffset;
|
||||
}
|
||||
#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))
|
||||
@ -699,14 +721,35 @@ int cdd_load(char *filename, char *header)
|
||||
while (fd)
|
||||
{
|
||||
/* read file HEADER */
|
||||
unsigned char head[32];
|
||||
unsigned char head[28];
|
||||
fseek(fd, 8, SEEK_SET);
|
||||
fread(head, 32, 1, fd);
|
||||
fread(head, 28, 1, fd);
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
|
||||
/* 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 */
|
||||
cdd.toc.tracks[cdd.toc.last].fd = fd;
|
||||
|
||||
@ -718,7 +761,7 @@ int cdd_load(char *filename, char *header)
|
||||
|
||||
/* current track end time */
|
||||
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 */
|
||||
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;
|
||||
|
||||
/* 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 */
|
||||
cdd.toc.last++;
|
||||
@ -1015,16 +1058,16 @@ void cdd_read_data(uint8 *dst)
|
||||
void cdd_read_audio(unsigned int samples)
|
||||
{
|
||||
/* previous audio outputs */
|
||||
int16 l = cdd.audio[0];
|
||||
int16 r = cdd.audio[1];
|
||||
int prev_l = cdd.audio[0];
|
||||
int prev_r = cdd.audio[1];
|
||||
|
||||
/* 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 ? */
|
||||
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 */
|
||||
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) */
|
||||
mul = (curVol & 0x7fc) ? (curVol & 0x7fc) : (curVol & 0x03);
|
||||
|
||||
/* left channel */
|
||||
delta = ((ptr[0] * mul) / 1024) - l;
|
||||
ptr++;
|
||||
l += delta;
|
||||
blip_add_delta_fast(snd.blips[2][0], i, delta);
|
||||
|
||||
/* right channel */
|
||||
delta = ((ptr[0] * mul) / 1024) - r;
|
||||
ptr++;
|
||||
r += delta;
|
||||
blip_add_delta_fast(snd.blips[2][1], i, delta);
|
||||
/* left & right channels */
|
||||
l = ((ptr[0] * mul) / 1024);
|
||||
r = ((ptr[1] * mul) / 1024);
|
||||
blip_add_delta_fast(snd.blips[2], i, l-prev_l, r-prev_r);
|
||||
prev_l = l;
|
||||
prev_r = r;
|
||||
ptr+=2;
|
||||
|
||||
/* update CD-DA fader volume (one step/sample) */
|
||||
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) */
|
||||
mul = (curVol & 0x7fc) ? (curVol & 0x7fc) : (curVol & 0x03);
|
||||
|
||||
/* left channel */
|
||||
/* left & right channels */
|
||||
#ifdef LSB_FIRST
|
||||
delta = ((ptr[0] * mul) / 1024) - l;
|
||||
ptr++;
|
||||
l = ((ptr[0] * mul) / 1024);
|
||||
r = ((ptr[1] * mul) / 1024);
|
||||
ptr+=2;
|
||||
#else
|
||||
delta = (((int16)((ptr[0] + ptr[1]*256)) * mul) / 1024) - l;
|
||||
ptr += 2;
|
||||
l = (((int16)((ptr[0] + ptr[1]*256)) * mul) / 1024);
|
||||
r = (((int16)((ptr[2] + ptr[3]*256)) * mul) / 1024);
|
||||
ptr+=4;
|
||||
#endif
|
||||
l += delta;
|
||||
blip_add_delta_fast(snd.blips[2][0], i, delta);
|
||||
|
||||
/* 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);
|
||||
blip_add_delta_fast(snd.blips[2], i, l-prev_l, r-prev_r);
|
||||
prev_l = l;
|
||||
prev_r = r;
|
||||
|
||||
/* update CD-DA fader volume (one step/sample) */
|
||||
if (curVol < endVol)
|
||||
@ -1154,23 +1185,24 @@ void cdd_read_audio(unsigned int samples)
|
||||
cdd.volume = curVol;
|
||||
|
||||
/* save last audio output for next frame */
|
||||
cdd.audio[0] = l;
|
||||
cdd.audio[1] = r;
|
||||
cdd.audio[0] = prev_l;
|
||||
cdd.audio[1] = prev_r;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no audio output */
|
||||
if (l) blip_add_delta_fast(snd.blips[2][0], 0, -l);
|
||||
if (r) blip_add_delta_fast(snd.blips[2][1], 0, -r);
|
||||
if (prev_l | prev_r)
|
||||
{
|
||||
blip_add_delta_fast(snd.blips[2], 0, -prev_l, -prev_r);
|
||||
|
||||
/* save audio output for next frame */
|
||||
cdd.audio[0] = 0;
|
||||
cdd.audio[1] = 0;
|
||||
/* save audio output for next frame */
|
||||
cdd.audio[0] = 0;
|
||||
cdd.audio[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* end of Blip Buffer timeframe */
|
||||
blip_end_frame(snd.blips[2][0], samples);
|
||||
blip_end_frame(snd.blips[2][1], samples);
|
||||
blip_end_frame(snd.blips[2], samples);
|
||||
}
|
||||
|
||||
static void cdd_read_subcode(void)
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Genesis Plus
|
||||
* 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
|
||||
* 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 */
|
||||
/* 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][1], clock / PCM_SCYCLES_RATIO, samplerate);
|
||||
blip_set_rates(snd.blips[1], clock / PCM_SCYCLES_RATIO, samplerate);
|
||||
}
|
||||
|
||||
void pcm_reset(void)
|
||||
@ -71,8 +70,7 @@ void pcm_reset(void)
|
||||
pcm.cycles = 0;
|
||||
|
||||
/* clear blip buffers */
|
||||
blip_clear(snd.blips[1][0]);
|
||||
blip_clear(snd.blips[1][1]);
|
||||
blip_clear(snd.blips[1]);
|
||||
}
|
||||
|
||||
int pcm_context_save(uint8 *state)
|
||||
@ -117,6 +115,11 @@ void pcm_run(unsigned int length)
|
||||
#ifdef LOG_PCM
|
||||
error("[%d][%d]run %d PCM samples (from %d)\n", v_counter, s68k.cycles, length, pcm.cycles);
|
||||
#endif
|
||||
|
||||
/* previous audio outputs */
|
||||
int prev_l = pcm.out[0];
|
||||
int prev_r = pcm.out[1];
|
||||
|
||||
/* check if PCM chip is running */
|
||||
if (pcm.enabled)
|
||||
{
|
||||
@ -180,41 +183,29 @@ void pcm_run(unsigned int length)
|
||||
if (r < -32768) r = -32768;
|
||||
else if (r > 32767) r = 32767;
|
||||
|
||||
/* check if PCM left output changed */
|
||||
if (pcm.out[0] != l)
|
||||
{
|
||||
blip_add_delta_fast(snd.blips[1][0], i, l-pcm.out[0]);
|
||||
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;
|
||||
}
|
||||
/* update Blip Buffer */
|
||||
blip_add_delta_fast(snd.blips[1], i, l-prev_l, r-prev_r);
|
||||
prev_l = l;
|
||||
prev_r = r;
|
||||
}
|
||||
|
||||
/* save last audio outputs */
|
||||
pcm.out[0] = prev_l;
|
||||
pcm.out[1] = prev_r;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if PCM left output changed */
|
||||
if (pcm.out[0])
|
||||
/* check if PCM output was not muted */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* end of blip buffer frame */
|
||||
blip_end_frame(snd.blips[1][0], length);
|
||||
blip_end_frame(snd.blips[1][1], length);
|
||||
blip_end_frame(snd.blips[1], length);
|
||||
|
||||
/* update PCM master clock counter */
|
||||
pcm.cycles += length * PCM_SCYCLES_RATIO;
|
||||
@ -223,7 +214,7 @@ void pcm_run(unsigned int length)
|
||||
void pcm_update(unsigned int samples)
|
||||
{
|
||||
/* 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 */
|
||||
if (clocks > 0)
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Genesis Plus
|
||||
* 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
|
||||
* provided that the following conditions are met:
|
||||
|
@ -4,8 +4,8 @@
|
||||
*
|
||||
* 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) 2007-2014 Eke-Eke (Genesis Plus GX)
|
||||
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
|
||||
* Copyright (C) 2007-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:
|
||||
@ -234,24 +234,39 @@ void gen_reset(int hard_reset)
|
||||
/* System 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(zram, 0x00, sizeof (zram));
|
||||
}
|
||||
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) */
|
||||
fm_reset(0);
|
||||
}
|
||||
|
||||
/* 68k & Z80 could be anywhere in VDP frame (Bonkers, Eternal Champions, X-Men 2) */
|
||||
m68k.cycles = Z80.cycles = (uint32)((MCYCLES_PER_LINE * lines_per_frame) * ((double)rand() / (double)RAND_MAX));
|
||||
|
||||
/* 68k cycles should be a multiple of 7 */
|
||||
/* 68k M-cycles should be a multiple of 7 */
|
||||
m68k.cycles = (m68k.cycles / 7) * 7;
|
||||
|
||||
/* Z80 cycles should be a multiple of 15 */
|
||||
Z80.cycles = (Z80.cycles / 15) * 15;
|
||||
/* Z80 M-cycles should be a multiple of 15 */
|
||||
Z80.cycles = (m68k.cycles / 15) * 15;
|
||||
|
||||
/* 8-bit / 16-bit modes */
|
||||
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
|
||||
|
@ -4,8 +4,8 @@
|
||||
*
|
||||
* 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) 2007-2014 Eke-Eke (Genesis Plus GX)
|
||||
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
|
||||
* Copyright (C) 2007-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:
|
||||
|
@ -570,7 +570,7 @@ void io_gg_write(unsigned int offset, unsigned int data)
|
||||
|
||||
case 6: /* PSG Stereo output control */
|
||||
io_reg[6] = data;
|
||||
SN76489_Config(Z80.cycles, config.psg_preamp, config.psgBoostNoise, data);
|
||||
psg_config(Z80.cycles, config.psg_preamp, data);
|
||||
return;
|
||||
|
||||
default: /* Read-only */
|
||||
|
@ -207,7 +207,7 @@ void z80_write_byte(unsigned int address, unsigned int data)
|
||||
default: /* ZRAM */
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1218,7 +1218,7 @@ void vdp_write_byte(unsigned int address, unsigned int data)
|
||||
{
|
||||
if (address & 1)
|
||||
{
|
||||
SN76489_Write(m68k.cycles, data);
|
||||
psg_write(m68k.cycles, data);
|
||||
return;
|
||||
}
|
||||
m68k_unused_8_w(address, data);
|
||||
@ -1264,7 +1264,7 @@ void vdp_write_word(unsigned int address, unsigned int data)
|
||||
case 0x10: /* PSG */
|
||||
case 0x14:
|
||||
{
|
||||
SN76489_Write(m68k.cycles, data & 0xFF);
|
||||
psg_write(m68k.cycles, data & 0xFF);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
* Genesis Plus
|
||||
* Z80 bank access to 68k bus
|
||||
*
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
|
||||
* Copyright (C) 2007-2011 Eke-Eke (Genesis Plus GX)
|
||||
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
|
||||
* Copyright (C) 2007-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:
|
||||
@ -294,7 +294,7 @@ void zbank_write_vdp(unsigned int address, unsigned int data)
|
||||
{
|
||||
if (address & 1)
|
||||
{
|
||||
SN76489_Write(Z80.cycles, data);
|
||||
psg_write(Z80.cycles, data);
|
||||
return;
|
||||
}
|
||||
zbank_unused_w(address, data);
|
||||
|
@ -2,8 +2,8 @@
|
||||
* Genesis Plus
|
||||
* Z80 bank access to 68k bus
|
||||
*
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
|
||||
* Copyright (C) 2007-2012 Eke-Eke (Genesis Plus GX)
|
||||
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
|
||||
* Copyright (C) 2007-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:
|
||||
|
@ -231,7 +231,7 @@ void z80_md_port_w(unsigned int port, unsigned char data)
|
||||
case 0x40:
|
||||
case 0x41:
|
||||
{
|
||||
SN76489_Write(Z80.cycles, data);
|
||||
psg_write(Z80.cycles, data);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -345,7 +345,7 @@ void z80_gg_port_w(unsigned int port, unsigned char data)
|
||||
case 0x40:
|
||||
case 0x41:
|
||||
{
|
||||
SN76489_Write(Z80.cycles, data);
|
||||
psg_write(Z80.cycles, data);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -450,7 +450,7 @@ void z80_ms_port_w(unsigned int port, unsigned char data)
|
||||
case 0x40:
|
||||
case 0x41:
|
||||
{
|
||||
SN76489_Write(Z80.cycles, data);
|
||||
psg_write(Z80.cycles, data);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -493,7 +493,7 @@ void z80_ms_port_w(unsigned int port, unsigned char data)
|
||||
1 0 : disable both PSG & 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);
|
||||
io_reg[6] = data;
|
||||
return;
|
||||
@ -611,7 +611,7 @@ void z80_m3_port_w(unsigned int port, unsigned char data)
|
||||
case 0x40:
|
||||
case 0x41:
|
||||
{
|
||||
SN76489_Write(Z80.cycles, data);
|
||||
psg_write(Z80.cycles, data);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -699,7 +699,7 @@ void z80_sg_port_w(unsigned int port, unsigned char data)
|
||||
case 0x40:
|
||||
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.cycles += (32 * 15);
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "io_ctrl.h"
|
||||
#include "input.h"
|
||||
#include "sound.h"
|
||||
#include "sn76489.h"
|
||||
#include "psg.h"
|
||||
#include "ym2413.h"
|
||||
#include "ym2612.h"
|
||||
#include "sram.h"
|
||||
|
@ -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) */
|
||||
/* - disabled assertions checks (define #BLIP_ASSERT to re-enable) */
|
||||
/* - 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) */
|
||||
/* Modified for Genesis Plus GX by EkeEke */
|
||||
/* - disabled assertions checks (define #BLIP_ASSERT to re-enable) */
|
||||
/* - fixed multiple time-frames support & removed m->avail */
|
||||
/* - 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"
|
||||
|
||||
@ -61,24 +62,32 @@ enum { phase_count = 1 << phase_bits };
|
||||
enum { delta_bits = 15 };
|
||||
enum { delta_unit = 1 << delta_bits };
|
||||
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
|
||||
limit the total buffered samples to blip_max_frame. That could only be
|
||||
increased by decreasing time_bits, which would reduce resample ratio accuracy.
|
||||
*/
|
||||
|
||||
typedef int buf_t;
|
||||
|
||||
struct blip_t
|
||||
{
|
||||
fixed_t factor;
|
||||
fixed_t offset;
|
||||
int size;
|
||||
#ifdef BLIP_MONO
|
||||
int integrator;
|
||||
#else
|
||||
int integrator[2];
|
||||
buf_t* buffer[2];
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef int buf_t;
|
||||
|
||||
#ifdef BLIP_MONO
|
||||
/* probably not totally portable */
|
||||
#define SAMPLES( buf ) ((buf_t*) ((buf) + 1))
|
||||
#define SAMPLES( blip ) ((buf_t*) ((blip) + 1))
|
||||
#endif
|
||||
|
||||
/* Arithmetic (sign-preserving) right shift */
|
||||
#define ARITH_SHIFT( n, shift ) \
|
||||
@ -124,9 +133,23 @@ blip_t* blip_new( int size )
|
||||
assert( size >= 0 );
|
||||
#endif
|
||||
|
||||
#ifdef BLIP_MONO
|
||||
m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) );
|
||||
#else
|
||||
m = (blip_t*) malloc( sizeof *m );
|
||||
#endif
|
||||
|
||||
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->size = size;
|
||||
blip_clear( m );
|
||||
@ -141,7 +164,13 @@ void blip_delete( blip_t* m )
|
||||
{
|
||||
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 );
|
||||
free( m );
|
||||
}
|
||||
@ -173,16 +202,23 @@ void blip_clear( blip_t* m )
|
||||
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
|
||||
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;
|
||||
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 )
|
||||
{
|
||||
fixed_t needed;
|
||||
|
||||
|
||||
#ifdef BLIP_ASSERT
|
||||
/* Fails if buffer can't hold that many more samples */
|
||||
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;
|
||||
if ( needed < m->offset )
|
||||
return 0;
|
||||
|
||||
|
||||
return (needed - m->offset + m->factor - 1) / m->factor;
|
||||
}
|
||||
|
||||
void blip_end_frame( blip_t* m, unsigned t )
|
||||
{
|
||||
m->offset += t * m->factor;
|
||||
|
||||
|
||||
#ifdef BLIP_ASSERT
|
||||
/* Fails if buffer size was exceeded */
|
||||
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 )
|
||||
{
|
||||
#ifdef BLIP_MONO
|
||||
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;
|
||||
|
||||
memmove( &buf [0], &buf [count], remain * sizeof buf [0] );
|
||||
memset( &buf [remain], 0, count * sizeof buf [0] );
|
||||
|
||||
memmove( &buf [0], &buf [count], remain * sizeof (buf_t) );
|
||||
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)
|
||||
{
|
||||
#ifdef BLIP_ASSERT
|
||||
assert( count >= 0 );
|
||||
|
||||
|
||||
if ( count > (m->offset >> time_bits) )
|
||||
count = m->offset >> time_bits;
|
||||
|
||||
|
||||
if ( count )
|
||||
#endif
|
||||
{
|
||||
buf_t const* in = SAMPLES( m );
|
||||
buf_t const* end = in + count;
|
||||
#ifdef BLIP_MONO
|
||||
buf_t const* in = SAMPLES( m );
|
||||
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
|
||||
{
|
||||
/* Eliminate fraction */
|
||||
int s = ARITH_SHIFT( sum, delta_bits );
|
||||
|
||||
|
||||
sum += *in++;
|
||||
|
||||
|
||||
CLAMP( s );
|
||||
|
||||
*out = s;
|
||||
out += 2;
|
||||
|
||||
|
||||
*out++ = s;
|
||||
|
||||
/* High-pass filter */
|
||||
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 );
|
||||
|
||||
#ifdef BLIP_MONO
|
||||
m->integrator = sum;
|
||||
|
||||
#else
|
||||
m->integrator[0] = sum;
|
||||
m->integrator[1] = sum2;
|
||||
#endif
|
||||
remove_samples( m, 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
|
||||
assert( count >= 0 );
|
||||
|
||||
if ( count > (m->offset >> time_bits) )
|
||||
count = m->offset >> time_bits;
|
||||
|
||||
if ( count )
|
||||
assert( count >= 0 );
|
||||
|
||||
if ( count > (m1->offset >> time_bits) )
|
||||
count = m1->offset >> time_bits;
|
||||
if ( count > (m2->offset >> time_bits) )
|
||||
count = m2->offset >> time_bits;
|
||||
if ( count > (m3->offset >> time_bits) )
|
||||
count = m3->offset >> time_bits;
|
||||
|
||||
if ( count )
|
||||
#endif
|
||||
{
|
||||
buf_t const* in = SAMPLES( m );
|
||||
buf_t const* end = in + count;
|
||||
int sum = m->integrator;
|
||||
do
|
||||
{
|
||||
/* Eliminate fraction */
|
||||
int s = ARITH_SHIFT( sum, delta_bits );
|
||||
|
||||
sum += *in++;
|
||||
|
||||
/* High-pass filter */
|
||||
sum -= s << (delta_bits - bass_shift);
|
||||
buf_t const* end;
|
||||
buf_t const* in[3];
|
||||
#ifdef BLIP_MONO
|
||||
int sum = m1->integrator;
|
||||
in[0] = SAMPLES( m1 );
|
||||
in[1] = SAMPLES( m2 );
|
||||
in[2] = SAMPLES( m3 );
|
||||
#else
|
||||
int sum = m1->integrator[0];
|
||||
int sum2 = m1->integrator[1];
|
||||
buf_t const* in2[3];
|
||||
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 */
|
||||
s += *out;
|
||||
|
||||
CLAMP( s );
|
||||
|
||||
*out = s;
|
||||
out += 2;
|
||||
}
|
||||
while ( in != end );
|
||||
m->integrator = sum;
|
||||
|
||||
remove_samples( m, count );
|
||||
}
|
||||
|
||||
return count;
|
||||
end = in[0] + count;
|
||||
do
|
||||
{
|
||||
/* Eliminate fraction */
|
||||
int s = ARITH_SHIFT( sum, delta_bits );
|
||||
|
||||
sum += *in[0]++;
|
||||
sum += *in[1]++;
|
||||
sum += *in[2]++;
|
||||
|
||||
CLAMP( s );
|
||||
|
||||
*out++ = s;
|
||||
|
||||
/* 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:
|
||||
@ -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
|
||||
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 )
|
||||
{
|
||||
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
|
||||
buf_t* out = SAMPLES( m ) + (fixed >> frac_bits);
|
||||
|
||||
int const phase_shift = frac_bits - phase_bits;
|
||||
int phase = fixed >> phase_shift & (phase_count - 1);
|
||||
short const* in = bl_step [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 [8] += delta2;
|
||||
}
|
||||
#endif
|
||||
|
@ -28,12 +28,24 @@ blip_max_ratio = 1 << 20 };
|
||||
/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
|
||||
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. */
|
||||
void blip_add_delta( blip_t*, unsigned int clock_time, int delta );
|
||||
|
||||
/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
|
||||
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
|
||||
samples available. */
|
||||
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. */
|
||||
int blip_read_samples( blip_t*, short out [], int count);
|
||||
|
||||
/* Same as above function except sample is added to output buffer previous value */
|
||||
/* This allows easy mixing of different blip buffers into a single output stream */
|
||||
int blip_mix_samples( blip_t* m, short out [], int count);
|
||||
/* Same as above function except sample is mixed from three blip buffers source */
|
||||
int blip_mix_samples( blip_t* m1, blip_t* m2, blip_t* m3, short out [], int count);
|
||||
|
||||
/** Frees buffer. No effect if NULL is passed. */
|
||||
void blip_delete( blip_t* );
|
||||
|
591
core/sound/psg.c
Normal file
591
core/sound/psg.c
Normal 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
60
core/sound/psg.h
Normal 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_ */
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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_ */
|
@ -2,8 +2,8 @@
|
||||
* Genesis Plus
|
||||
* Sound Hardware
|
||||
*
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
|
||||
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
|
||||
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
|
||||
* Copyright (C) 2007-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:
|
||||
@ -86,7 +86,7 @@ void sound_init( void )
|
||||
YM_Update = YM2612Update;
|
||||
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;
|
||||
}
|
||||
else
|
||||
@ -97,21 +97,20 @@ void sound_init( void )
|
||||
YM_Update = YM2413Update;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Initialize PSG chip */
|
||||
SN76489_Init((system_hw == SYSTEM_SG) ? SN_DISCRETE : SN_INTEGRATED);
|
||||
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff);
|
||||
psg_init((system_hw == SYSTEM_SG) ? PSG_DISCRETE : PSG_INTEGRATED);
|
||||
}
|
||||
|
||||
void sound_reset(void)
|
||||
{
|
||||
/* reset sound chips */
|
||||
YM_Reset();
|
||||
SN76489_Reset();
|
||||
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff);
|
||||
psg_reset();
|
||||
psg_config(0, config.psg_preamp, 0xff);
|
||||
|
||||
/* reset FM buffer ouput */
|
||||
fm_last[0] = fm_last[1] = 0;
|
||||
@ -125,21 +124,23 @@ void sound_reset(void)
|
||||
|
||||
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 */
|
||||
SN76489_Update(cycles);
|
||||
/* Run PSG chip until end of frame */
|
||||
psg_end_frame(cycles);
|
||||
|
||||
/* Run FM chip until end of frame */
|
||||
fm_update(cycles);
|
||||
|
||||
/* FM output pre-amplification */
|
||||
/* FM output pre-amplification */
|
||||
preamp = config.fm_preamp;
|
||||
|
||||
/* FM frame initial timestamp */
|
||||
time = fm_cycles_start;
|
||||
|
||||
/* Restore last FM outputs from previous frame */
|
||||
l = fm_last[0];
|
||||
r = fm_last[1];
|
||||
prev_l = fm_last[0];
|
||||
prev_r = fm_last[1];
|
||||
|
||||
/* FM buffer start pointer */
|
||||
ptr = fm_buffer;
|
||||
@ -150,15 +151,12 @@ int sound_update(unsigned int cycles)
|
||||
/* high-quality Band-Limited synthesis */
|
||||
do
|
||||
{
|
||||
/* left channel */
|
||||
delta = ((*ptr++ * preamp) / 100) - l;
|
||||
l += delta;
|
||||
blip_add_delta(snd.blips[0][0], time, delta);
|
||||
|
||||
/* right channel */
|
||||
delta = ((*ptr++ * preamp) / 100) - r;
|
||||
r += delta;
|
||||
blip_add_delta(snd.blips[0][1], time, delta);
|
||||
/* left & right channels */
|
||||
l = ((*ptr++ * preamp) / 100);
|
||||
r = ((*ptr++ * preamp) / 100);
|
||||
blip_add_delta(snd.blips[0], time, l-prev_l, r-prev_r);
|
||||
prev_l = l;
|
||||
prev_r = r;
|
||||
|
||||
/* increment time counter */
|
||||
time += fm_cycles_ratio;
|
||||
@ -170,15 +168,12 @@ int sound_update(unsigned int cycles)
|
||||
/* faster Linear Interpolation */
|
||||
do
|
||||
{
|
||||
/* left channel */
|
||||
delta = ((*ptr++ * preamp) / 100) - l;
|
||||
l += delta;
|
||||
blip_add_delta_fast(snd.blips[0][0], time, delta);
|
||||
|
||||
/* right channel */
|
||||
delta = ((*ptr++ * preamp) / 100) - r;
|
||||
r += delta;
|
||||
blip_add_delta_fast(snd.blips[0][1], time, delta);
|
||||
/* left & right channels */
|
||||
l = ((*ptr++ * preamp) / 100);
|
||||
r = ((*ptr++ * preamp) / 100);
|
||||
blip_add_delta_fast(snd.blips[0], time, l-prev_l, r-prev_r);
|
||||
prev_l = l;
|
||||
prev_r = r;
|
||||
|
||||
/* increment time counter */
|
||||
time += fm_cycles_ratio;
|
||||
@ -190,18 +185,17 @@ int sound_update(unsigned int cycles)
|
||||
fm_ptr = fm_buffer;
|
||||
|
||||
/* save last FM output for next frame */
|
||||
fm_last[0] = l;
|
||||
fm_last[1] = r;
|
||||
fm_last[0] = prev_l;
|
||||
fm_last[1] = prev_r;
|
||||
|
||||
/* adjust FM cycle counters for next frame */
|
||||
fm_cycles_count = fm_cycles_start = time - cycles;
|
||||
|
||||
/* end of blip buffers time frame */
|
||||
blip_end_frame(snd.blips[0][0], cycles);
|
||||
blip_end_frame(snd.blips[0][1], cycles);
|
||||
|
||||
/* end of blip buffer time frame */
|
||||
blip_end_frame(snd.blips[0], cycles);
|
||||
|
||||
/* 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)
|
||||
@ -217,7 +211,7 @@ int sound_context_save(uint8 *state)
|
||||
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));
|
||||
|
||||
@ -238,7 +232,7 @@ int sound_context_load(uint8 *state)
|
||||
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));
|
||||
fm_cycles_count = fm_cycles_start;
|
||||
|
@ -2,8 +2,8 @@
|
||||
* Genesis Plus
|
||||
* Sound Hardware
|
||||
*
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
|
||||
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
|
||||
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
|
||||
* Copyright (C) 2007-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:
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Genesis Plus
|
||||
* 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
|
||||
* provided that the following conditions are met:
|
||||
@ -116,11 +116,11 @@ int state_load(unsigned char *state)
|
||||
bufferptr += sound_context_load(&state[bufferptr]);
|
||||
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
|
||||
{
|
||||
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff);
|
||||
psg_config(0, config.psg_preamp, 0xff);
|
||||
}
|
||||
else
|
||||
{
|
||||
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, io_reg[6]);
|
||||
psg_config(0, config.psg_preamp, io_reg[6]);
|
||||
}
|
||||
|
||||
/* 68000 */
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Genesis Plus
|
||||
* 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
|
||||
* provided that the following conditions are met:
|
||||
|
176
core/system.c
176
core/system.c
@ -68,11 +68,9 @@ int audio_init(int samplerate, double framerate)
|
||||
memset(&snd, 0, sizeof (snd));
|
||||
|
||||
/* Initialize Blip Buffers */
|
||||
snd.blips[0][0] = blip_new(samplerate / 10);
|
||||
snd.blips[0][1] = blip_new(samplerate / 10);
|
||||
if (!snd.blips[0][0] || !snd.blips[0][1])
|
||||
snd.blips[0] = blip_new(samplerate / 10);
|
||||
if (!snd.blips[0])
|
||||
{
|
||||
audio_shutdown();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -80,11 +78,9 @@ int audio_init(int samplerate, double framerate)
|
||||
if (system_hw == SYSTEM_MCD)
|
||||
{
|
||||
/* allocate blip buffers */
|
||||
snd.blips[1][0] = blip_new(samplerate / 10);
|
||||
snd.blips[1][1] = blip_new(samplerate / 10);
|
||||
snd.blips[2][0] = blip_new(samplerate / 10);
|
||||
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])
|
||||
snd.blips[1] = blip_new(samplerate / 10);
|
||||
snd.blips[2] = blip_new(samplerate / 10);
|
||||
if (!snd.blips[1] || !snd.blips[2])
|
||||
{
|
||||
audio_shutdown();
|
||||
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 */
|
||||
/* 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. */
|
||||
blip_set_rates(snd.blips[0][0], mclk, samplerate);
|
||||
blip_set_rates(snd.blips[0][1], mclk, samplerate);
|
||||
blip_set_rates(snd.blips[0], mclk, samplerate);
|
||||
|
||||
/* Mega CD sound hardware */
|
||||
if (system_hw == SYSTEM_MCD)
|
||||
@ -155,17 +150,14 @@ void audio_set_rate(int samplerate, double framerate)
|
||||
|
||||
void audio_reset(void)
|
||||
{
|
||||
int i,j;
|
||||
int i;
|
||||
|
||||
/* Clear blip buffers */
|
||||
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][j]);
|
||||
}
|
||||
blip_clear(snd.blips[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,16 +179,13 @@ void audio_set_equalizer(void)
|
||||
|
||||
void audio_shutdown(void)
|
||||
{
|
||||
int i,j;
|
||||
int i;
|
||||
|
||||
/* Delete blip buffers */
|
||||
for (i=0; i<3; i++)
|
||||
{
|
||||
for (j=0; j<2; j++)
|
||||
{
|
||||
blip_delete(snd.blips[i][j]);
|
||||
snd.blips[i][j] = 0;
|
||||
}
|
||||
blip_delete(snd.blips[i]);
|
||||
snd.blips[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,37 +202,24 @@ int audio_update(int16 *buffer)
|
||||
|
||||
/* read CDDA samples */
|
||||
cdd_read_audio(size);
|
||||
}
|
||||
|
||||
#ifdef ALIGN_SND
|
||||
/* return an aligned number of samples if required */
|
||||
size &= ALIGN_SND;
|
||||
/* return an aligned number of samples if required */
|
||||
size &= ALIGN_SND;
|
||||
#endif
|
||||
|
||||
/* resample FM & PSG mixed stream to output buffer */
|
||||
#ifdef LSB_FIRST
|
||||
blip_read_samples(snd.blips[0][0], buffer, size);
|
||||
blip_read_samples(snd.blips[0][1], buffer + 1, size);
|
||||
#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 & mix FM/PSG, PCM & CD-DA streams to output buffer */
|
||||
blip_mix_samples(snd.blips[0], snd.blips[1], snd.blips[2], buffer, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* resample PCM & CD-DA streams to output buffer */
|
||||
#ifdef LSB_FIRST
|
||||
blip_mix_samples(snd.blips[1][0], buffer, size);
|
||||
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);
|
||||
#ifdef ALIGN_SND
|
||||
/* return an aligned number of samples if required */
|
||||
size &= ALIGN_SND;
|
||||
#endif
|
||||
|
||||
/* resample FM/PSG mixed stream to output buffer */
|
||||
blip_read_samples(snd.blips[0], buffer, size);
|
||||
}
|
||||
|
||||
/* Audio filtering */
|
||||
@ -434,13 +410,10 @@ void system_frame_gen(int do_skip)
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize VCounter */
|
||||
v_counter = bitmap.viewport.h;
|
||||
|
||||
/* first line of overscan */
|
||||
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 */
|
||||
@ -481,31 +454,38 @@ void system_frame_gen(int do_skip)
|
||||
/* refresh inputs just before VINT (Warriors of Eternal Sun) */
|
||||
osd_input_update();
|
||||
|
||||
/* delay between VBLANK flag & Vertical Interrupt (Dracula, OutRunners, VR Troopers) */
|
||||
m68k_run(788);
|
||||
if (zstate == 1)
|
||||
/* VDP always starts after VBLANK so VINT cannot occur on first frame after a VDP reset (verified on real hardware) */
|
||||
if (v_counter != bitmap.viewport.h)
|
||||
{
|
||||
z80_run(788);
|
||||
}
|
||||
else
|
||||
{
|
||||
Z80.cycles = 788;
|
||||
}
|
||||
/* reinitialize VCounter */
|
||||
v_counter = bitmap.viewport.h;
|
||||
|
||||
/* set VINT flag */
|
||||
status |= 0x80;
|
||||
/* delay between VBLANK flag & Vertical Interrupt (Dracula, OutRunners, VR Troopers) */
|
||||
m68k_run(788);
|
||||
if (zstate == 1)
|
||||
{
|
||||
z80_run(788);
|
||||
}
|
||||
else
|
||||
{
|
||||
Z80.cycles = 788;
|
||||
}
|
||||
|
||||
/* Vertical Interrupt */
|
||||
vint_pending = 0x20;
|
||||
if (reg[1] & 0x20)
|
||||
{
|
||||
/* level 6 interrupt */
|
||||
m68k_set_irq(6);
|
||||
/* set VINT flag */
|
||||
status |= 0x80;
|
||||
|
||||
/* Vertical Interrupt */
|
||||
vint_pending = 0x20;
|
||||
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 */
|
||||
m68k_run(MCYCLES_PER_LINE);
|
||||
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 */
|
||||
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 */
|
||||
@ -836,31 +813,38 @@ void system_frame_scd(int do_skip)
|
||||
/* refresh inputs just before VINT */
|
||||
osd_input_update();
|
||||
|
||||
/* delay between VBLANK flag & Vertical Interrupt (Dracula, OutRunners, VR Troopers) */
|
||||
m68k_run(788);
|
||||
if (zstate == 1)
|
||||
/* VDP always starts after VBLANK so VINT cannot occur on first frame after a VDP reset (verified on real hardware) */
|
||||
if (v_counter != bitmap.viewport.h)
|
||||
{
|
||||
z80_run(788);
|
||||
}
|
||||
else
|
||||
{
|
||||
Z80.cycles = 788;
|
||||
}
|
||||
/* reinitialize VCounter */
|
||||
v_counter = bitmap.viewport.h;
|
||||
|
||||
/* set VINT flag */
|
||||
status |= 0x80;
|
||||
/* delay between VBLANK flag & Vertical Interrupt (Dracula, OutRunners, VR Troopers) */
|
||||
m68k_run(788);
|
||||
if (zstate == 1)
|
||||
{
|
||||
z80_run(788);
|
||||
}
|
||||
else
|
||||
{
|
||||
Z80.cycles = 788;
|
||||
}
|
||||
|
||||
/* Vertical Interrupt */
|
||||
vint_pending = 0x20;
|
||||
if (reg[1] & 0x20)
|
||||
{
|
||||
/* level 6 interrupt */
|
||||
m68k_set_irq(6);
|
||||
/* set VINT flag */
|
||||
status |= 0x80;
|
||||
|
||||
/* Vertical Interrupt */
|
||||
vint_pending = 0x20;
|
||||
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 */
|
||||
scd_update(MCYCLES_PER_LINE);
|
||||
|
||||
|
@ -91,7 +91,7 @@ typedef struct
|
||||
int sample_rate; /* Output Sample rate (8000-48000) */
|
||||
double frame_rate; /* Output Frame rate (usually 50 or 60 frames per second) */
|
||||
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;
|
||||
|
||||
|
||||
|
@ -71,7 +71,7 @@ OBJECTS += $(OBJDIR)/input.o \
|
||||
$(OBJDIR)/graphic_board.o
|
||||
|
||||
OBJECTS += $(OBJDIR)/sound.o \
|
||||
$(OBJDIR)/sn76489.o \
|
||||
$(OBJDIR)/psg.o \
|
||||
$(OBJDIR)/ym2413.o \
|
||||
$(OBJDIR)/ym2612.o
|
||||
|
||||
|
@ -42,7 +42,7 @@ void set_config_defaults(void)
|
||||
config.psg_preamp = 150;
|
||||
config.fm_preamp = 100;
|
||||
config.hq_fm = 1;
|
||||
config.psgBoostNoise = 1;
|
||||
config.hq_psg = 1;
|
||||
config.filter = 1;
|
||||
config.low_freq = 200;
|
||||
config.high_freq = 8000;
|
||||
|
@ -20,7 +20,7 @@ typedef struct
|
||||
{
|
||||
uint8 hq_fm;
|
||||
uint8 filter;
|
||||
uint8 psgBoostNoise;
|
||||
uint8 hq_psg;
|
||||
uint8 dac_bits;
|
||||
uint8 ym2413;
|
||||
int16 psg_preamp;
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* 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
|
||||
* provided that the following conditions are met:
|
||||
@ -101,7 +101,7 @@ void config_default(void)
|
||||
config.psg_preamp = 150;
|
||||
config.fm_preamp = 100;
|
||||
config.hq_fm = 1;
|
||||
config.psgBoostNoise = 1;
|
||||
config.hq_psg = 1;
|
||||
config.filter = 1;
|
||||
config.lp_range = 0x9999; /* 0.6 in 16.16 fixed point */
|
||||
config.low_freq = 880;
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* 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
|
||||
* provided that the following conditions are met:
|
||||
@ -51,7 +51,7 @@ typedef struct
|
||||
char version[16];
|
||||
uint8 hq_fm;
|
||||
uint8 filter;
|
||||
uint8 psgBoostNoise;
|
||||
uint8 hq_psg;
|
||||
uint8 dac_bits;
|
||||
uint8 ym2413;
|
||||
uint8 mono;
|
||||
|
104
gx/gui/menu.c
104
gx/gui/menu.c
@ -341,19 +341,19 @@ static gui_item items_options[] =
|
||||
/* Audio options */
|
||||
static gui_item items_audio[] =
|
||||
{
|
||||
{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,"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,"PSG Volume: 2.50", "Adjust SN76489 output level", 56,132,276,48},
|
||||
{NULL,NULL,"PSG Noise Boost: OFF", "Boost SN76489 Noise Channel", 56,132,276,48},
|
||||
{NULL,NULL,"Audio Out: 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,"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,"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,"High Freq: 20000 Hz", "Adjust EQ Highest Frequency", 56,132,276,48}
|
||||
{NULL,NULL,"Master System FM: AUTO", "Enable/Disable YM2413 chip", 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 Volume: 1.00", "Adjust YM2612/YM2413 audio balance", 56,132,276,48},
|
||||
{NULL,NULL,"PSG Volume: 2.50", "Adjust SN76489 audio balance", 56,132,276,48},
|
||||
{NULL,NULL,"High-Quality PSG: ON", "Enable/Disable SN76489 high-quality resampling", 56,132,276,48},
|
||||
{NULL,NULL,"Audio Output: STEREO", "Select audio mixing output type", 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,"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,"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}
|
||||
};
|
||||
|
||||
/* 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,"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 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,"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,"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,"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,"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,"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,"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,"Deflickering Filter: AUTO", "Enable/Disable GX hardware framebuffer filtering", 56,132,276,48},
|
||||
#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},
|
||||
#endif
|
||||
{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,"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 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 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 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,"GG screen: ORIGINAL", "Enable/disable Game Gear extended screen", 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,"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 Scaling (+0,+0)", "Adjust display scaling", 56,132,276,48}
|
||||
@ -418,19 +418,19 @@ static gui_item items_video[] =
|
||||
/* Menu options */
|
||||
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 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 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 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,"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,"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,"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 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},
|
||||
#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},
|
||||
#endif
|
||||
};
|
||||
@ -899,16 +899,15 @@ static void soundmenu ()
|
||||
else if (config.ym2413 == 1) sprintf (items[0].text, "Master System FM: ON");
|
||||
else sprintf (items[0].text, "Master System FM: AUTO");
|
||||
|
||||
if (config.hq_fm) sprintf (items[1].text, "High-Quality FM: ON");
|
||||
else sprintf (items[1].text, "High-Quality FM: OFF");
|
||||
sprintf (items[1].text, "High-Quality FM: %s", config.hq_fm ? "ON":"OFF");
|
||||
|
||||
if (config.dac_bits < 14) sprintf (items[2].text, "FM Resolution: %d bits", config.dac_bits);
|
||||
else sprintf (items[2].text, "FM Resolution: MAX");
|
||||
|
||||
sprintf (items[3].text, "FM Volume: %1.2f", fm_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[6].text, "Audio Out: %s", config.mono ? "MONO":"STEREO");
|
||||
sprintf (items[5].text, "High-Quality PSG: %s", config.hq_psg? "ON":"OFF");
|
||||
sprintf (items[6].text, "Audio Output: %s", config.mono ? "MONO":"STEREO");
|
||||
|
||||
if (config.filter == 2)
|
||||
{
|
||||
@ -971,8 +970,7 @@ static void soundmenu ()
|
||||
case 1:
|
||||
{
|
||||
config.hq_fm ^= 1;
|
||||
if (config.hq_fm) sprintf (items[1].text, "High-Quality FM: ON");
|
||||
else sprintf (items[1].text, "High-Quality FM: OFF");
|
||||
sprintf (items[1].text, "High-Quality FM: %s", config.hq_fm ? "ON":"OFF");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1001,27 +999,19 @@ static void soundmenu ()
|
||||
config.psg_preamp = (int)(psg_volume * 100.0 + 0.5);
|
||||
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
|
||||
{
|
||||
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff);
|
||||
psg_config(0, config.psg_preamp, 0xff);
|
||||
}
|
||||
else
|
||||
{
|
||||
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, io_reg[6]);
|
||||
psg_config(0, config.psg_preamp, io_reg[6]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 5:
|
||||
{
|
||||
config.psgBoostNoise ^= 1;
|
||||
sprintf (items[5].text, "PSG Noise Boost: %s", config.psgBoostNoise ? "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]);
|
||||
}
|
||||
config.hq_psg ^= 1;
|
||||
sprintf (items[5].text, "High-Quality PSG: %s", config.hq_psg ? "ON":"OFF");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1745,8 +1735,8 @@ static void videomenu ()
|
||||
else
|
||||
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+3+ntsc_offset].comment, "Enable/disable Game Gear extended screen");
|
||||
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+4+ntsc_offset].comment, "Select display aspect ratio");
|
||||
strcpy(items[VI_OFFSET+5+ntsc_offset].comment, "Adjust display position");
|
||||
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);
|
||||
|
||||
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 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 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, 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, 660 - offset, (GXColor)WHITE);
|
||||
FONT_writeCenter("SVP core by Gravydas Ignotas (Notaz)", 18, 0, 640, 606 - offset, (GXColor)WHITE);
|
||||
FONT_writeCenter("Blip Buffer Library & NTSC Video Filter by Shay Green (Blargg)", 18, 0, 640, 624 - offset, (GXColor)WHITE);
|
||||
FONT_writeCenter("3-Band EQ implementation by Neil C", 18, 0, 640, 642 - 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("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("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("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("Special thanks to ...", 20, 0, 640, 1090 - offset, (GXColor)LIGHT_GREEN);
|
||||
|
@ -13,7 +13,7 @@ Upstream Authors:
|
||||
Files: *
|
||||
Copyright: 1998, 1999, 2000, 2001, 2002, 2003 Charles MacDonald
|
||||
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:
|
||||
Unless otherwise explicitly stated, all code in Genesis Plus GX is released
|
||||
under the following license:
|
||||
@ -77,7 +77,6 @@ License: LGPLv2.1
|
||||
Files: core/sound/blip_buf.c
|
||||
core/sound/blip_buf.h
|
||||
Copyright: 2003-2009 Shay Green
|
||||
2012-2013 EkeEke
|
||||
License: LGPLv2.1
|
||||
|
||||
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
|
||||
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
|
||||
core/sound/ym2413.h
|
||||
Copyright: 2002 Jarek Burczynski
|
||||
|
@ -97,6 +97,7 @@ char CART_BRAM[256];
|
||||
|
||||
static int vwidth;
|
||||
static int vheight;
|
||||
static double vaspect_ratio;
|
||||
|
||||
static uint32_t brm_crc[2];
|
||||
static uint8_t brm_format[0x40] =
|
||||
@ -494,8 +495,8 @@ static void config_default(void)
|
||||
/* sound options */
|
||||
config.psg_preamp = 150;
|
||||
config.fm_preamp = 100;
|
||||
config.hq_fm = 1; /* high-quality resampling */
|
||||
config.psgBoostNoise = 1;
|
||||
config.hq_fm = 1; /* high-quality FM resampling (slower) */
|
||||
config.hq_psg = 1; /* high-quality PSG resampling (slower) */
|
||||
config.filter = 0; /* no filter */
|
||||
config.lp_range = 0x9999; /* 0.6 in 16.16 fixed point */
|
||||
config.low_freq = 880;
|
||||
@ -503,7 +504,7 @@ static void config_default(void)
|
||||
config.lg = 1.0;
|
||||
config.mg = 1.0;
|
||||
config.hg = 1.0;
|
||||
config.dac_bits = 14; /* MAX DEPTH */
|
||||
config.dac_bits = 14; /* MAX DEPTH */
|
||||
config.ym2413 = 2; /* AUTO */
|
||||
config.mono = 0; /* STEREO output */
|
||||
|
||||
@ -520,6 +521,7 @@ static void config_default(void)
|
||||
|
||||
/* video options */
|
||||
config.overscan = 0;
|
||||
config.aspect_ratio = 0;
|
||||
config.gg_extra = 0;
|
||||
config.ntsc = 0;
|
||||
config.lcd = 0;
|
||||
@ -736,13 +738,46 @@ static void extract_directory(char *buf, const char *path, size_t size)
|
||||
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)
|
||||
{
|
||||
int ow = vwidth;
|
||||
int oh = vheight;
|
||||
double oar = vaspect_ratio;
|
||||
|
||||
vwidth = bitmap.viewport.w + (bitmap.viewport.x * 2);
|
||||
vheight = bitmap.viewport.h + (bitmap.viewport.y * 2);
|
||||
vaspect_ratio = calculate_display_aspect_ratio();
|
||||
|
||||
if (config.ntsc)
|
||||
{
|
||||
@ -756,8 +791,7 @@ static bool update_viewport(void)
|
||||
{
|
||||
vheight = vheight * 2;
|
||||
}
|
||||
|
||||
return ((ow != vwidth) || (oh != vheight));
|
||||
return ((ow != vwidth) || (oh != vheight) || (oar != vaspect_ratio));
|
||||
}
|
||||
|
||||
static void check_variables(void)
|
||||
@ -933,12 +967,7 @@ static void check_variables(void)
|
||||
break;
|
||||
}
|
||||
|
||||
/* force overscan change */
|
||||
bitmap.viewport.changed = 3;
|
||||
|
||||
/* reinitialize libretro audio/video timings */
|
||||
retro_get_system_av_info(&info);
|
||||
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &info);
|
||||
update_viewports = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1088,6 +1117,20 @@ static void check_variables(void)
|
||||
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";
|
||||
environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var);
|
||||
{
|
||||
@ -1125,11 +1168,12 @@ static void check_variables(void)
|
||||
system_init();
|
||||
system_reset();
|
||||
memcpy(sram.sram, temp, sizeof(temp));
|
||||
update_viewports = true;
|
||||
}
|
||||
|
||||
if (update_viewports)
|
||||
{
|
||||
bitmap.viewport.changed = 3;
|
||||
bitmap.viewport.changed = 11;
|
||||
if ((system_hw == SYSTEM_GG) && !config.gg_extra)
|
||||
bitmap.viewport.x = (config.overscan & 2) ? 14 : -48;
|
||||
else
|
||||
@ -1475,7 +1519,7 @@ unsigned retro_api_version(void) { return RETRO_API_VERSION; }
|
||||
|
||||
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_region_detect", "System region; auto|ntsc-u|pal|ntsc-j" },
|
||||
{ "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_overscan", "Borders; disabled|top/bottom|left/right|full" },
|
||||
{ "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_gun_cursor", "Show Lightgun crosshair; no|yes" },
|
||||
{ "genesis_plus_gx_invert_mouse", "Invert Mouse Y-axis; no|yes" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
struct retro_controller_description port_1[] = {
|
||||
static const struct retro_controller_description port_1[] = {
|
||||
{ "Joypad Auto", RETRO_DEVICE_JOYPAD },
|
||||
{ "Joypad Port Empty", RETRO_DEVICE_NONE },
|
||||
{ "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 },
|
||||
};
|
||||
|
||||
struct retro_controller_description port_2[] = {
|
||||
static const struct retro_controller_description port_2[] = {
|
||||
{ "Joypad Auto", RETRO_DEVICE_JOYPAD },
|
||||
{ "Joypad Port Empty", RETRO_DEVICE_NONE },
|
||||
{ "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 },
|
||||
};
|
||||
|
||||
struct retro_controller_info ports[] = {
|
||||
static const struct retro_controller_info ports[] = {
|
||||
{ port_1, 16 },
|
||||
{ port_2, 18 },
|
||||
{ 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_UP, "D-Pad Up" },
|
||||
{ 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.max_width = 720;
|
||||
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.sample_rate = 44100;
|
||||
}
|
||||
|
||||
void retro_set_controller_port_device(unsigned port, unsigned device)
|
||||
{
|
||||
if (port > 2)
|
||||
return;
|
||||
|
||||
switch(device)
|
||||
{
|
||||
case RETRO_DEVICE_NONE:
|
||||
input.system[port] = NO_SYSTEM;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
break;
|
||||
}
|
||||
case RETRO_DEVICE_MDPAD_3B_WAYPLAY:
|
||||
{
|
||||
int i;
|
||||
@ -1779,9 +1842,14 @@ void retro_set_controller_port_device(unsigned port, unsigned device)
|
||||
break;
|
||||
case RETRO_DEVICE_JOYPAD:
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
old_system[0] = input.system[0];
|
||||
@ -2082,14 +2150,22 @@ void retro_run(void)
|
||||
else
|
||||
system_frame_sms(0);
|
||||
|
||||
if (bitmap.viewport.changed & 1)
|
||||
if (bitmap.viewport.changed & 9)
|
||||
{
|
||||
bool geometry_updated = update_viewport();
|
||||
bitmap.viewport.changed &= ~1;
|
||||
if (update_viewport())
|
||||
if (bitmap.viewport.changed & 8)
|
||||
{
|
||||
struct retro_system_av_info info;
|
||||
retro_get_system_av_info(&info);
|
||||
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info.geometry);
|
||||
struct retro_system_av_info info;
|
||||
bitmap.viewport.changed &= ~8;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -394,7 +394,7 @@
|
||||
RelativePath="..\..\..\core\sound\eq.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\core\sound\sn76489.c">
|
||||
RelativePath="..\..\..\core\sound\psg.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\core\sound\sound.c">
|
||||
|
@ -143,7 +143,7 @@
|
||||
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Release_LTCG|Xbox 360'">CompileAsC</CompileAs>
|
||||
</ClCompile>
|
||||
<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\ym2413.c" />
|
||||
<ClCompile Include="..\..\..\core\sound\ym2612.c" />
|
||||
|
@ -80,7 +80,7 @@
|
||||
<ClCompile Include="..\..\..\core\sound\eq.c">
|
||||
<Filter>Source Files\sound</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\core\sound\sn76489.c">
|
||||
<ClCompile Include="..\..\..\core\sound\psg.c">
|
||||
<Filter>Source Files\sound</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\core\sound\sound.c">
|
||||
|
@ -49,7 +49,7 @@
|
||||
<ClCompile Include="..\..\..\core\ntsc\sms_ntsc.c" />
|
||||
<ClCompile Include="..\..\..\core\sound\blip_buf.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\ym2413.c" />
|
||||
<ClCompile Include="..\..\..\core\sound\ym2612.c" />
|
||||
|
@ -117,7 +117,7 @@
|
||||
<ClCompile Include="..\..\..\core\sound\eq.c">
|
||||
<Filter>Source Files\sound</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\core\sound\sn76489.c">
|
||||
<ClCompile Include="..\..\..\core\sound\psg.c">
|
||||
<Filter>Source Files\sound</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\core\sound\sound.c">
|
||||
|
@ -84,7 +84,7 @@ struct
|
||||
char version[16];
|
||||
uint8 hq_fm;
|
||||
uint8 filter;
|
||||
uint8 psgBoostNoise;
|
||||
uint8 hq_psg;
|
||||
uint8 dac_bits;
|
||||
uint8 ym2413;
|
||||
uint8 mono;
|
||||
@ -105,6 +105,7 @@ struct
|
||||
uint8 bios;
|
||||
uint8 lock_on;
|
||||
uint8 overscan;
|
||||
uint8 aspect_ratio;
|
||||
uint8 ntsc;
|
||||
uint8 lcd;
|
||||
uint8 gg_extra;
|
||||
|
@ -69,7 +69,7 @@ OBJECTS += $(OBJDIR)/input.o \
|
||||
$(OBJDIR)/graphic_board.o
|
||||
|
||||
OBJECTS += $(OBJDIR)/sound.o \
|
||||
$(OBJDIR)/sn76489.o \
|
||||
$(OBJDIR)/psg.o \
|
||||
$(OBJDIR)/ym2413.o \
|
||||
$(OBJDIR)/ym2612.o
|
||||
|
||||
|
@ -11,7 +11,7 @@ void set_config_defaults(void)
|
||||
config.psg_preamp = 150;
|
||||
config.fm_preamp = 100;
|
||||
config.hq_fm = 0;
|
||||
config.psgBoostNoise = 1;
|
||||
config.hq_psg = 0;
|
||||
config.filter = 1;
|
||||
config.low_freq = 200;
|
||||
config.high_freq = 8000;
|
||||
|
@ -19,7 +19,7 @@ typedef struct
|
||||
{
|
||||
uint8 hq_fm;
|
||||
uint8 filter;
|
||||
uint8 psgBoostNoise;
|
||||
uint8 hq_psg;
|
||||
uint8 dac_bits;
|
||||
uint8 ym2413;
|
||||
int16 psg_preamp;
|
||||
|
@ -19,17 +19,21 @@
|
||||
# -D16BPP_RENDERING - configure for 16-bit pixels (RGB565)
|
||||
# -D32BPP_RENDERING - configure for 32-bit pixels (RGB888)
|
||||
|
||||
NAME = gen_sdl.exe
|
||||
NAME = gen_sdl
|
||||
|
||||
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
|
||||
#-fomit-frame-pointer
|
||||
#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
|
||||
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
|
||||
|
||||
OBJDIR = ./build_sdl
|
||||
@ -63,7 +67,7 @@ OBJECTS += $(OBJDIR)/input.o \
|
||||
$(OBJDIR)/graphic_board.o
|
||||
|
||||
OBJECTS += $(OBJDIR)/sound.o \
|
||||
$(OBJDIR)/sn76489.o \
|
||||
$(OBJDIR)/psg.o \
|
||||
$(OBJDIR)/ym2413.o \
|
||||
$(OBJDIR)/ym2612.o
|
||||
|
||||
@ -114,7 +118,9 @@ OBJECTS += $(OBJDIR)/bitwise.o \
|
||||
$(OBJDIR)/vorbisfile.o \
|
||||
$(OBJDIR)/window.o
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
OBJECTS += $(OBJDIR)/icon.o
|
||||
endif
|
||||
|
||||
all: $(NAME)
|
||||
|
||||
@ -163,8 +169,13 @@ $(OBJDIR)/%.o : $(SRCDIR)/tremor/%.c
|
||||
$(OBJDIR)/%.o : $(SRCDIR)/../sdl/%.c $(SRCDIR)/../sdl/%.h
|
||||
$(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 :
|
||||
windres $(SRCDIR)/../sdl/icon.rc $@
|
||||
endif
|
||||
|
||||
pack :
|
||||
strip $(NAME)
|
185
sdl/Makefile.sdl2
Normal file
185
sdl/Makefile.sdl2
Normal 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)
|
@ -9,7 +9,7 @@ PLEASE DO NOT DISTRIBUTE WIN32 BINARIES WITHOUT THIS NOTICE.
|
||||
END USERS SHOULD PREFERABLY USE LIBRETRO PORT WITH RETROARCH.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
Genesis Plus (Windows Port)
|
||||
Genesis Plus (SDL Port)
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
based on the original version 1.3
|
||||
|
@ -12,7 +12,7 @@ void set_config_defaults(void)
|
||||
config.psg_preamp = 150;
|
||||
config.fm_preamp = 100;
|
||||
config.hq_fm = 1;
|
||||
config.psgBoostNoise = 1;
|
||||
config.hq_psg = 1;
|
||||
config.filter = 1;
|
||||
config.low_freq = 200;
|
||||
config.high_freq = 8000;
|
||||
|
@ -15,7 +15,7 @@ typedef struct
|
||||
{
|
||||
uint8 hq_fm;
|
||||
uint8 filter;
|
||||
uint8 psgBoostNoise;
|
||||
uint8 hq_psg;
|
||||
uint8 dac_bits;
|
||||
uint8 ym2413;
|
||||
int16 psg_preamp;
|
||||
|
@ -5,7 +5,9 @@
|
||||
|
||||
#include "osd.h"
|
||||
|
||||
#ifdef LOGERROR
|
||||
static FILE *error_log;
|
||||
#endif
|
||||
|
||||
void error_init(void)
|
||||
{
|
||||
|
@ -14,7 +14,7 @@
|
||||
#define SOUND_FREQUENCY 48000
|
||||
#define SOUND_SAMPLES_SIZE 2048
|
||||
|
||||
#define VIDEO_WIDTH 320
|
||||
#define VIDEO_WIDTH 320
|
||||
#define VIDEO_HEIGHT 240
|
||||
|
||||
int joynum = 0;
|
||||
@ -67,7 +67,7 @@ static int sdl_sound_init()
|
||||
{
|
||||
int n;
|
||||
SDL_AudioSpec as_desired, as_obtained;
|
||||
|
||||
|
||||
if(SDL_Init(SDL_INIT_AUDIO) < 0) {
|
||||
MessageBox(NULL, "SDL Audio initialization failed", "Error", 0);
|
||||
return 0;
|
||||
@ -101,10 +101,10 @@ static int sdl_sound_init()
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void sdl_sound_update(enabled)
|
||||
static void sdl_sound_update(int enabled)
|
||||
{
|
||||
int size = audio_update(soundframe) * 2;
|
||||
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
int i;
|
||||
@ -126,7 +126,7 @@ static void sdl_sound_close()
|
||||
{
|
||||
SDL_PauseAudio(1);
|
||||
SDL_CloseAudio();
|
||||
if (sdl_sound.buffer)
|
||||
if (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.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);
|
||||
|
||||
@ -304,7 +304,7 @@ static void sdl_sync_close()
|
||||
SDL_DestroySemaphore(sdl_sync.sem_sync);
|
||||
}
|
||||
|
||||
static const uint16 vc_table[4][2] =
|
||||
static const uint16 vc_table[4][2] =
|
||||
{
|
||||
/* NTSC, PAL */
|
||||
{0xDA , 0xF2}, /* Mode 4 (192 lines) */
|
||||
@ -332,8 +332,7 @@ static int sdl_control_update(SDLKey keystate)
|
||||
|
||||
case SDLK_F2:
|
||||
{
|
||||
if (fullscreen) fullscreen = 0;
|
||||
else fullscreen = SDL_FULLSCREEN;
|
||||
fullscreen = (fullscreen ? 0 : SDL_FULLSCREEN);
|
||||
sdl_video.surf_screen = SDL_SetVideoMode(VIDEO_WIDTH, VIDEO_HEIGHT, 16, SDL_SWSURFACE | fullscreen);
|
||||
break;
|
||||
}
|
||||
@ -497,7 +496,7 @@ int sdl_input_update(void)
|
||||
|
||||
/* reset input */
|
||||
input.pad[joynum] = 0;
|
||||
|
||||
|
||||
switch (input.dev[joynum])
|
||||
{
|
||||
case DEVICE_LIGHTGUN:
|
||||
@ -515,7 +514,7 @@ int sdl_input_update(void)
|
||||
/* 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(state & SDL_BUTTON_MMASK) input.pad[joynum] |= INPUT_C;
|
||||
if(keystate[SDLK_f]) input.pad[joynum] |= INPUT_START;
|
||||
break;
|
||||
}
|
||||
@ -586,7 +585,7 @@ int sdl_input_update(void)
|
||||
if(keystate[SDLK_x]) input.pad[joynum] |= INPUT_X;
|
||||
if(keystate[SDLK_c]) input.pad[joynum] |= INPUT_MODE;
|
||||
if(keystate[SDLK_v]) input.pad[joynum] |= INPUT_Z;
|
||||
|
||||
|
||||
/* Left Analog Stick (bidirectional) */
|
||||
if(keystate[SDLK_UP]) 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 */
|
||||
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;
|
||||
@ -642,7 +641,7 @@ int sdl_input_update(void)
|
||||
/* 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;
|
||||
|
||||
@ -658,7 +657,7 @@ int sdl_input_update(void)
|
||||
/* 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;
|
||||
@ -711,7 +710,7 @@ int main (int argc, char **argv)
|
||||
char caption[256];
|
||||
sprintf(caption, "Genesis Plus GX\\SDL\nusage: %s gamename\n", argv[0]);
|
||||
MessageBox(NULL, caption, "Information", 0);
|
||||
exit(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* set default config */
|
||||
@ -751,10 +750,8 @@ int main (int argc, char **argv)
|
||||
/* initialize SDL */
|
||||
if(SDL_Init(0) < 0)
|
||||
{
|
||||
char caption[256];
|
||||
sprintf(caption, "SDL initialization failed");
|
||||
MessageBox(NULL, caption, "Error", 0);
|
||||
exit(1);
|
||||
MessageBox(NULL, "SDL initialization failed", "Error", 0);
|
||||
return 1;
|
||||
}
|
||||
sdl_video_init();
|
||||
if (use_sound) sdl_sound_init();
|
||||
@ -784,7 +781,7 @@ int main (int argc, char **argv)
|
||||
char caption[256];
|
||||
sprintf(caption, "Error loading file `%s'.", argv[1]);
|
||||
MessageBox(NULL, caption, "Error", 0);
|
||||
exit(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* initialize system hardware */
|
||||
@ -866,14 +863,14 @@ int main (int argc, char **argv)
|
||||
while(running)
|
||||
{
|
||||
SDL_Event event;
|
||||
if (SDL_PollEvent(&event))
|
||||
if (SDL_PollEvent(&event))
|
||||
{
|
||||
switch(event.type)
|
||||
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);
|
||||
char caption[100];
|
||||
sprintf(caption,"Genesis Plus GX - %d fps - %s", event.user.code, (rominfo.international[0] != 0x20) ? rominfo.international : rominfo.domestic);
|
||||
SDL_WM_SetCaption(caption, NULL);
|
||||
break;
|
||||
}
|
945
sdl/sdl2/main.c
Normal file
945
sdl/sdl2/main.c
Normal 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
11
sdl/sdl2/main.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user