diff --git a/.gitignore b/.gitignore index 577fae8..550058c 100644 --- a/.gitignore +++ b/.gitignore @@ -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 + diff --git a/HISTORY.txt b/HISTORY.txt index 8de9980..d1b1a64 100644 --- a/HISTORY.txt +++ b/HISTORY.txt @@ -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 diff --git a/Makefile.gc b/Makefile.gc index d593d8f..f86e9dd 100644 --- a/Makefile.gc +++ b/Makefile.gc @@ -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 diff --git a/Makefile.wii b/Makefile.wii index aff1cd7..1febf4c 100644 --- a/Makefile.wii +++ b/Makefile.wii @@ -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 diff --git a/builds/genesis_plus_gx_libretro.dll b/builds/genesis_plus_gx_libretro.dll index d2c8295..1f9d29f 100644 Binary files a/builds/genesis_plus_gx_libretro.dll and b/builds/genesis_plus_gx_libretro.dll differ diff --git a/builds/genplus_cube.dol b/builds/genplus_cube.dol index ec11ffa..b56b9b1 100644 Binary files a/builds/genplus_cube.dol and b/builds/genplus_cube.dol differ diff --git a/builds/genplus_wii.dol b/builds/genplus_wii.dol index 5902708..51f5bab 100644 Binary files a/builds/genplus_wii.dol and b/builds/genplus_wii.dol differ diff --git a/core/cart_hw/sram.c b/core/cart_hw/sram.c index ed8bea9..c5e4d55 100644 --- a/core/cart_hw/sram.c +++ b/core/cart_hw/sram.c @@ -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 */ diff --git a/core/cart_hw/sram.h b/core/cart_hw/sram.h index 64934ca..da32a95 100644 --- a/core/cart_hw/sram.h +++ b/core/cart_hw/sram.h @@ -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: diff --git a/core/cd_hw/cdd.c b/core/cd_hw/cdd.c index e29cfae..d20aa87 100644 --- a/core/cd_hw/cdd.c +++ b/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) diff --git a/core/cd_hw/pcm.c b/core/cd_hw/pcm.c index 09f99d3..e068d70 100644 --- a/core/cd_hw/pcm.c +++ b/core/cd_hw/pcm.c @@ -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) diff --git a/core/cd_hw/pcm.h b/core/cd_hw/pcm.h index ee1a5c5..eef847d 100644 --- a/core/cd_hw/pcm.h +++ b/core/cd_hw/pcm.h @@ -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: diff --git a/core/genesis.c b/core/genesis.c index 7c36505..1ef2db9 100644 --- a/core/genesis.c +++ b/core/genesis.c @@ -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) diff --git a/core/genesis.h b/core/genesis.h index c172339..15fd52f 100644 --- a/core/genesis.h +++ b/core/genesis.h @@ -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: diff --git a/core/io_ctrl.c b/core/io_ctrl.c index 97a8964..44797fc 100644 --- a/core/io_ctrl.c +++ b/core/io_ctrl.c @@ -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 */ diff --git a/core/mem68k.c b/core/mem68k.c index 3966d16..cb60ed7 100644 --- a/core/mem68k.c +++ b/core/mem68k.c @@ -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; } diff --git a/core/membnk.c b/core/membnk.c index 64f16d8..50ffd52 100644 --- a/core/membnk.c +++ b/core/membnk.c @@ -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); diff --git a/core/membnk.h b/core/membnk.h index b4b242e..dd34631 100644 --- a/core/membnk.h +++ b/core/membnk.h @@ -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: diff --git a/core/memz80.c b/core/memz80.c index 0692ce3..b3a6ef1 100644 --- a/core/memz80.c +++ b/core/memz80.c @@ -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); diff --git a/core/shared.h b/core/shared.h index f463aa8..959f289 100644 --- a/core/shared.h +++ b/core/shared.h @@ -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" diff --git a/core/sound/blip_buf.c b/core/sound/blip_buf.c index 2f48536..ae0db45 100644 --- a/core/sound/blip_buf.c +++ b/core/sound/blip_buf.c @@ -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 diff --git a/core/sound/blip_buf.h b/core/sound/blip_buf.h index 21c45d0..81b986b 100644 --- a/core/sound/blip_buf.h +++ b/core/sound/blip_buf.h @@ -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* ); diff --git a/core/sound/psg.c b/core/sound/psg.c new file mode 100644 index 0000000..44c03ec --- /dev/null +++ b/core/sound/psg.c @@ -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; + } +} diff --git a/core/sound/psg.h b/core/sound/psg.h new file mode 100644 index 0000000..46370d4 --- /dev/null +++ b/core/sound/psg.h @@ -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_ */ diff --git a/core/sound/sn76489.c b/core/sound/sn76489.c deleted file mode 100644 index 4c450ac..0000000 --- a/core/sound/sn76489.c +++ /dev/null @@ -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; - } - } -} diff --git a/core/sound/sn76489.h b/core/sound/sn76489.h deleted file mode 100644 index 157a9f5..0000000 --- a/core/sound/sn76489.h +++ /dev/null @@ -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_ */ diff --git a/core/sound/sound.c b/core/sound/sound.c index 06bea1a..f99da18 100644 --- a/core/sound/sound.c +++ b/core/sound/sound.c @@ -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; diff --git a/core/sound/sound.h b/core/sound/sound.h index a4284a7..ae7cc56 100644 --- a/core/sound/sound.h +++ b/core/sound/sound.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: diff --git a/core/state.c b/core/state.c index f79fd3d..3bdab0b 100644 --- a/core/state.c +++ b/core/state.c @@ -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 */ diff --git a/core/state.h b/core/state.h index 6b480c0..1ac9322 100644 --- a/core/state.h +++ b/core/state.h @@ -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: diff --git a/core/system.c b/core/system.c index ac20496..07d86ed 100644 --- a/core/system.c +++ b/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); diff --git a/core/system.h b/core/system.h index 2a5a6d3..e6fe50d 100644 --- a/core/system.h +++ b/core/system.h @@ -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; diff --git a/gcw0/Makefile b/gcw0/Makefile index b17ced2..2400056 100644 --- a/gcw0/Makefile +++ b/gcw0/Makefile @@ -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 diff --git a/gcw0/config.c b/gcw0/config.c index 564cfaf..16b7b15 100644 --- a/gcw0/config.c +++ b/gcw0/config.c @@ -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; diff --git a/gcw0/config.h b/gcw0/config.h index 9b522e1..9cf5ed5 100644 --- a/gcw0/config.h +++ b/gcw0/config.h @@ -20,7 +20,7 @@ typedef struct { uint8 hq_fm; uint8 filter; - uint8 psgBoostNoise; + uint8 hq_psg; uint8 dac_bits; uint8 ym2413; int16 psg_preamp; diff --git a/gx/config.c b/gx/config.c index 3d1c67f..f3da2c2 100644 --- a/gx/config.c +++ b/gx/config.c @@ -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; diff --git a/gx/config.h b/gx/config.h index 3aad3db..9bc3097 100644 --- a/gx/config.h +++ b/gx/config.h @@ -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; diff --git a/gx/gui/menu.c b/gx/gui/menu.c index 57f9d55..0aeafb8 100644 --- a/gx/gui/menu.c +++ b/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); diff --git a/libretro/debian/copyright b/libretro/debian/copyright index 450a60b..5656c57 100644 --- a/libretro/debian/copyright +++ b/libretro/debian/copyright @@ -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 diff --git a/libretro/libretro.c b/libretro/libretro.c index 66cb692..7621242 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -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); } } diff --git a/libretro/msvc/msvc-2003-xbox1/msvc-2003-xbox1.vcproj b/libretro/msvc/msvc-2003-xbox1/msvc-2003-xbox1.vcproj index 7a597ad..4563a92 100644 --- a/libretro/msvc/msvc-2003-xbox1/msvc-2003-xbox1.vcproj +++ b/libretro/msvc/msvc-2003-xbox1/msvc-2003-xbox1.vcproj @@ -394,7 +394,7 @@ RelativePath="..\..\..\core\sound\eq.c"> + RelativePath="..\..\..\core\sound\psg.c"> diff --git a/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj b/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj index c97d11e..41d892f 100644 --- a/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj +++ b/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj @@ -143,7 +143,7 @@ CompileAsC - + diff --git a/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj.filters b/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj.filters index ae6ed86..ef720ec 100644 --- a/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj.filters +++ b/libretro/msvc/msvc-2010-360/msvc-2010-360.vcxproj.filters @@ -80,7 +80,7 @@ Source Files\sound - + Source Files\sound diff --git a/libretro/msvc/msvc-2010/msvc-2010.vcxproj b/libretro/msvc/msvc-2010/msvc-2010.vcxproj index 11ed4df..f6972c0 100644 --- a/libretro/msvc/msvc-2010/msvc-2010.vcxproj +++ b/libretro/msvc/msvc-2010/msvc-2010.vcxproj @@ -49,7 +49,7 @@ - + diff --git a/libretro/msvc/msvc-2010/msvc-2010.vcxproj.filters b/libretro/msvc/msvc-2010/msvc-2010.vcxproj.filters index 262e025..32ee69b 100644 --- a/libretro/msvc/msvc-2010/msvc-2010.vcxproj.filters +++ b/libretro/msvc/msvc-2010/msvc-2010.vcxproj.filters @@ -117,7 +117,7 @@ Source Files\sound - + Source Files\sound diff --git a/libretro/osd.h b/libretro/osd.h index 03c57d4..4c02b68 100644 --- a/libretro/osd.h +++ b/libretro/osd.h @@ -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; diff --git a/psp2/Makefile b/psp2/Makefile index 36bb06a..6c6a743 100644 --- a/psp2/Makefile +++ b/psp2/Makefile @@ -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 diff --git a/psp2/config.c b/psp2/config.c index 93f2ae3..b8299e2 100644 --- a/psp2/config.c +++ b/psp2/config.c @@ -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; diff --git a/psp2/config.h b/psp2/config.h index 30c2c5b..44aaddb 100644 --- a/psp2/config.h +++ b/psp2/config.h @@ -19,7 +19,7 @@ typedef struct { uint8 hq_fm; uint8 filter; - uint8 psgBoostNoise; + uint8 hq_psg; uint8 dac_bits; uint8 ym2413; int16 psg_preamp; diff --git a/sdl/Makefile b/sdl/Makefile.sdl1 similarity index 90% rename from sdl/Makefile rename to sdl/Makefile.sdl1 index 6134e52..dc38ca7 100644 --- a/sdl/Makefile +++ b/sdl/Makefile.sdl1 @@ -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) diff --git a/sdl/Makefile.sdl2 b/sdl/Makefile.sdl2 new file mode 100644 index 0000000..46b5895 --- /dev/null +++ b/sdl/Makefile.sdl2 @@ -0,0 +1,185 @@ + +# Makefile for genplus SDL2 +# +# (c) 1999, 2000, 2001, 2002, 2003 Charles MacDonald +# modified by Eke-Eke +# +# 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) diff --git a/sdl/README.txt b/sdl/README.txt index 500adec..b3bd6f5 100644 --- a/sdl/README.txt +++ b/sdl/README.txt @@ -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 diff --git a/sdl/config.c b/sdl/config.c index 99aa1e8..933e669 100644 --- a/sdl/config.c +++ b/sdl/config.c @@ -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; diff --git a/sdl/config.h b/sdl/config.h index 3434204..78e7c84 100644 --- a/sdl/config.h +++ b/sdl/config.h @@ -15,7 +15,7 @@ typedef struct { uint8 hq_fm; uint8 filter; - uint8 psgBoostNoise; + uint8 hq_psg; uint8 dac_bits; uint8 ym2413; int16 psg_preamp; diff --git a/sdl/error.c b/sdl/error.c index c731b30..4a41b5b 100644 --- a/sdl/error.c +++ b/sdl/error.c @@ -5,7 +5,9 @@ #include "osd.h" +#ifdef LOGERROR static FILE *error_log; +#endif void error_init(void) { diff --git a/sdl/main.c b/sdl/sdl1/main.c similarity index 93% rename from sdl/main.c rename to sdl/sdl1/main.c index ff68004..6831229 100644 --- a/sdl/main.c +++ b/sdl/sdl1/main.c @@ -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; } diff --git a/sdl/main.h b/sdl/sdl1/main.h similarity index 100% rename from sdl/main.h rename to sdl/sdl1/main.h diff --git a/sdl/sdl2/main.c b/sdl/sdl2/main.c new file mode 100644 index 0000000..7224276 --- /dev/null +++ b/sdl/sdl2/main.c @@ -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; +} diff --git a/sdl/sdl2/main.h b/sdl/sdl2/main.h new file mode 100644 index 0000000..dfa5d8d --- /dev/null +++ b/sdl/sdl2/main.h @@ -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_ */