/***************************************************************************************1* Genesis Plus2* Virtual System emulation3*4* Support for "Genesis", "Genesis + CD" & "Master System" modes5*6* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)7* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)8*9* Redistribution and use of this code or any derivative works are permitted10* provided that the following conditions are met:11*12* - Redistributions may not be sold, nor may they be used in a commercial13* product or activity.14*15* - Redistributions that are modified from the original source must include the16* complete source code, including the source code for all components used by a17* binary built from the modified sources. However, as a special exception, the18* source code distributed need not include anything that is normally distributed19* (in either source or binary form) with the major components (compiler, kernel,20* and so on) of the operating system on which the executable runs, unless that21* component itself accompanies the executable.22*23* - Redistributions must reproduce the above copyright notice, this list of24* conditions and the following disclaimer in the documentation and/or other25* materials provided with the distribution.26*27* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"28* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE29* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE30* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE31* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR32* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF33* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS34* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN35* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)36* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE37* POSSIBILITY OF SUCH DAMAGE.38*39****************************************************************************************/4041#include "shared.h"42#include "eq.h"4344/* Global variables */45t_bitmap bitmap;46t_snd snd;47uint32 mcycles_vdp;48uint8 system_hw;49uint8 system_bios;50uint32 system_clock;51const int16 SVP_cycles = 800;5253uint8 pause_b;54EQSTATE eq;55int16 llp,rrp;5657/******************************************************************************************/58/* Audio subsystem */59/******************************************************************************************/6061int audio_init(int samplerate, double framerate)62{63/* Number of M-cycles executed per second. */64/* All emulated chips are kept in sync by using a common oscillator (MCLOCK) */65/* */66/* The original console would run exactly 53693175 M-cycles per sec (53203424 for PAL), */67/* 3420 M-cycles per line and 262 (313 for PAL) lines per frame, which gives an exact */68/* framerate of 59.92 (49.70 for PAL) frames per second. */69/* */70/* Since audio samples are generated at the end of the frame, to prevent audio skipping */71/* or lag between emulated frames, number of samples rendered per frame must be set to */72/* output samplerate (number of samples played per second) divided by input framerate */73/* (number of frames emulated per seconds). */74/* */75/* On some systems, we may want to achieve 100% smooth video rendering by synchronizing */76/* frame emulation with VSYNC, which frequency is generally not exactly those values. */77/* In that case, input framerate (number of frames emulated per seconds) is the same as */78/* output framerate (number of frames rendered per seconds) by the host video hardware. */79/* */80/* When no framerate is specified, base clock is set to original master clock value. */81/* Otherwise, it is set to number of M-cycles emulated per line (fixed) multiplied by */82/* number of lines per frame (VDP mode specific) multiplied by input framerate. */83/* */84double mclk = framerate ? (MCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * framerate) : system_clock;8586/* Shutdown first */87audio_shutdown();8889/* Clear the sound data context */90memset(&snd, 0, sizeof (snd));9192/* Initialize audio rates */93snd.sample_rate = samplerate;94snd.frame_rate = framerate;9596/* Initialize Blip Buffers */97snd.blips[0][0] = blip_new(samplerate / 10);98snd.blips[0][1] = blip_new(samplerate / 10);99if (!snd.blips[0][0] || !snd.blips[0][1])100{101audio_shutdown();102return -1;103}104105/* For maximal accuracy, sound chips are running at their original rate using common */106/* master clock timebase so they remain perfectly synchronized together, while still */107/* being synchronized with 68K and Z80 CPUs as well. Mixed sound chip output is then */108/* resampled to desired rate at the end of each frame, using Blip Buffer. */109blip_set_rates(snd.blips[0][0], mclk, samplerate);110blip_set_rates(snd.blips[0][1], mclk, samplerate);111112/* Initialize PSG core */113SN76489_Init(snd.blips[0][0], snd.blips[0][1], (system_hw < SYSTEM_MARKIII) ? SN_DISCRETE : SN_INTEGRATED);114115/* Mega CD sound hardware */116if (system_hw == SYSTEM_MCD)117{118/* allocate blip buffers */119snd.blips[1][0] = blip_new(samplerate / 10);120snd.blips[1][1] = blip_new(samplerate / 10);121snd.blips[2][0] = blip_new(samplerate / 10);122snd.blips[2][1] = blip_new(samplerate / 10);123if (!snd.blips[1][0] || !snd.blips[1][1] || !snd.blips[2][0] || !snd.blips[2][1])124{125audio_shutdown();126return -1;127}128129/* Initialize PCM core */130pcm_init(snd.blips[1][0], snd.blips[1][1]);131132/* Initialize CDD core */133cdd_init(snd.blips[2][0], snd.blips[2][1]);134}135136/* Set audio enable flag */137snd.enabled = 1;138139/* Reset audio */140audio_reset();141142return (0);143}144145void audio_reset(void)146{147int i,j;148149/* Clear blip buffers */150for (i=0; i<3; i++)151{152for (j=0; j<2; j++)153{154if (snd.blips[i][j])155{156blip_clear(snd.blips[i][j]);157}158}159}160161/* Low-Pass filter */162llp = 0;163rrp = 0;164165/* 3 band EQ */166audio_set_equalizer();167}168169void audio_set_equalizer(void)170{171init_3band_state(&eq,config.low_freq,config.high_freq,snd.sample_rate);172eq.lg = (double)(config.lg) / 100.0;173eq.mg = (double)(config.mg) / 100.0;174eq.hg = (double)(config.hg) / 100.0;175}176177void audio_shutdown(void)178{179int i,j;180181/* Delete blip buffers */182for (i=0; i<3; i++)183{184for (j=0; j<2; j++)185{186blip_delete(snd.blips[i][j]);187snd.blips[i][j] = 0;188}189}190}191192int audio_update(int16 *buffer)193{194/* run sound chips until end of frame */195int size = sound_update(mcycles_vdp);196197/* Mega CD specific */198if (system_hw == SYSTEM_MCD)199{200/* sync PCM chip with other sound chips */201pcm_update(size);202203/* read CDDA samples */204cdd_read_audio(size);205}206207#ifdef ALIGN_SND208/* return an aligned number of samples if required */209size &= ALIGN_SND;210#endif211212/* resample FM & PSG mixed stream to output buffer */213#ifdef LSB_FIRST214blip_read_samples(snd.blips[0][0], buffer, size);215blip_read_samples(snd.blips[0][1], buffer + 1, size);216#else217blip_read_samples(snd.blips[0][0], buffer + 1, size);218blip_read_samples(snd.blips[0][1], buffer, size);219#endif220221/* Mega CD specific */222if (system_hw == SYSTEM_MCD)223{224/* resample PCM & CD-DA streams to output buffer */225#ifdef LSB_FIRST226blip_mix_samples(snd.blips[1][0], buffer, size);227blip_mix_samples(snd.blips[1][1], buffer + 1, size);228blip_mix_samples(snd.blips[2][0], buffer, size);229blip_mix_samples(snd.blips[2][1], buffer + 1, size);230#else231blip_mix_samples(snd.blips[1][0], buffer + 1, size);232blip_mix_samples(snd.blips[1][1], buffer, size);233blip_mix_samples(snd.blips[2][0], buffer + 1, size);234blip_mix_samples(snd.blips[2][1], buffer, size);235#endif236}237238/* Audio filtering */239if (config.filter)240{241int samples = size;242int16 *out = buffer;243int32 l, r;244245if (config.filter & 1)246{247/* single-pole low-pass filter (6 dB/octave) */248uint32 factora = config.lp_range;249uint32 factorb = 0x10000 - factora;250251/* restore previous sample */252l = llp;253r = rrp;254255do256{257/* apply low-pass filter */258l = l*factora + out[0]*factorb;259r = r*factora + out[1]*factorb;260261/* 16.16 fixed point */262l >>= 16;263r >>= 16;264265/* update sound buffer */266*out++ = l;267*out++ = r;268}269while (--samples);270271/* save last samples for next frame */272llp = l;273rrp = r;274}275else if (config.filter & 2)276{277do278{279/* 3 Band EQ */280l = do_3band(&eq,out[0]);281r = do_3band(&eq,out[1]);282283/* clipping (16-bit samples) */284if (l > 32767) l = 32767;285else if (l < -32768) l = -32768;286if (r > 32767) r = 32767;287else if (r < -32768) r = -32768;288289/* update sound buffer */290*out++ = l;291*out++ = r;292}293while (--samples);294}295}296297/* Mono output mixing */298if (config.mono)299{300int16 out;301int samples = size;302do303{304out = (buffer[0] + buffer[1]) / 2;305*buffer++ = out;306*buffer++ = out;307}308while (--samples);309}310311#ifdef LOGSOUND312error("%d samples returned\n\n",size);313#endif314315return size;316}317318/****************************************************************319* Virtual System emulation320****************************************************************/321void system_init(void)322{323gen_init();324io_init();325vdp_init();326render_init();327sound_init();328}329330void system_reset(void)331{332gen_reset(1);333io_reset();334render_reset();335vdp_reset();336sound_reset();337audio_reset();338}339340void system_frame_gen(int do_skip)341{342/* line counters */343int start, end, line = 0;344345/* Z80 interrupt flag */346int zirq = 1;347348/* reload H Counter */349int h_counter = reg[10];350351/* reset frame cycle counter */352mcycles_vdp = 0;353354/* reload V Counter */355v_counter = lines_per_frame - 1;356357/* reset VDP FIFO */358fifo_write_cnt = 0;359fifo_slots = 0;360361/* update 6-Buttons & Lightguns */362input_refresh();363364/* display changed during VBLANK */365if (bitmap.viewport.changed & 2)366{367/* interlaced modes */368int old_interlaced = interlaced;369interlaced = (reg[12] & 0x02) >> 1;370371if (old_interlaced != interlaced)372{373/* double resolution mode */374im2_flag = ((reg[12] & 0x06) == 0x06);375376/* reset field status flag */377odd_frame = 1;378379/* video mode has changed */380bitmap.viewport.changed = 5;381382/* update rendering mode */383if (reg[1] & 0x04)384{385if (im2_flag)386{387render_bg = (reg[11] & 0x04) ? render_bg_m5_im2_vs : render_bg_m5_im2;388render_obj = (reg[12] & 0x08) ? render_obj_m5_im2_ste : render_obj_m5_im2;389}390else391{392render_bg = (reg[11] & 0x04) ? render_bg_m5_vs : render_bg_m5;393render_obj = (reg[12] & 0x08) ? render_obj_m5_ste : render_obj_m5;394}395}396}397else398{399/* clear flag */400bitmap.viewport.changed &= ~2;401}402403/* active screen height */404if (reg[1] & 0x04)405{406/* Mode 5 */407if (reg[1] & 0x08)408{409/* 240 active lines */410bitmap.viewport.h = 240;411bitmap.viewport.y = (config.overscan & 1) * 24 * vdp_pal;412}413else414{415/* 224 active lines */416bitmap.viewport.h = 224;417bitmap.viewport.y = (config.overscan & 1) * (8 + (24 * vdp_pal));418}419}420else421{422/* Mode 4 (192 active lines) */423bitmap.viewport.h = 192;424bitmap.viewport.y = (config.overscan & 1) * 24 * (vdp_pal + 1);425}426427/* active screen width */428bitmap.viewport.w = 256 + ((reg[12] & 0x01) << 6);429}430431/* clear VBLANK, DMA, FIFO FULL & field flags */432status &= 0xFEE5;433434/* set FIFO EMPTY flag */435status |= 0x0200;436437/* even/odd field flag (interlaced modes only) */438odd_frame ^= 1;439if (interlaced)440{441status |= (odd_frame << 4);442}443444/* update VDP DMA */445if (dma_length)446{447vdp_dma_update(0);448}449450/* render last line of overscan */451if (bitmap.viewport.y > 0)452{453blank_line(v_counter, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);454}455456/* parse first line of sprites */457if (reg[1] & 0x40)458{459parse_satb(-1);460}461462/* run 68k & Z80 */463m68k_run(MCYCLES_PER_LINE);464if (zstate == 1)465{466z80_run(MCYCLES_PER_LINE);467}468else469{470Z80.cycles = MCYCLES_PER_LINE;471}472473/* run SVP chip */474if (svp)475{476ssp1601_run(SVP_cycles);477}478479/* update line cycle count */480mcycles_vdp += MCYCLES_PER_LINE;481482/* Active Display */483do484{485/* update V Counter */486v_counter = line;487488/* update 6-Buttons & Lightguns */489input_refresh();490491/* H Interrupt */492if(--h_counter < 0)493{494/* reload H Counter */495h_counter = reg[10];496497/* interrupt level 4 */498hint_pending = 0x10;499if (reg[0] & 0x10)500{501m68k_update_irq(4);502}503}504505/* update VDP DMA */506if (dma_length)507{508vdp_dma_update(mcycles_vdp);509}510511/* render scanline */512if (!do_skip)513{514render_line(line);515}516517/* run 68k & Z80 */518m68k_run(mcycles_vdp + MCYCLES_PER_LINE);519if (zstate == 1)520{521z80_run(mcycles_vdp + MCYCLES_PER_LINE);522}523else524{525Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;526}527528/* run SVP chip */529if (svp)530{531ssp1601_run(SVP_cycles);532}533534/* update line cycle count */535mcycles_vdp += MCYCLES_PER_LINE;536}537while (++line < bitmap.viewport.h);538539/* end of active display */540v_counter = line;541542/* set VBLANK flag */543status |= 0x08;544545/* overscan area */546start = lines_per_frame - bitmap.viewport.y;547end = bitmap.viewport.h + bitmap.viewport.y;548549/* check viewport changes */550if ((bitmap.viewport.w != bitmap.viewport.ow) || (bitmap.viewport.h != bitmap.viewport.oh))551{552bitmap.viewport.ow = bitmap.viewport.w;553bitmap.viewport.oh = bitmap.viewport.h;554bitmap.viewport.changed |= 1;555}556557/* update 6-Buttons & Lightguns */558input_refresh();559560/* H Interrupt */561if(--h_counter < 0)562{563/* reload H Counter */564h_counter = reg[10];565566/* interrupt level 4 */567hint_pending = 0x10;568if (reg[0] & 0x10)569{570m68k_update_irq(4);571}572}573574/* update VDP DMA */575if (dma_length)576{577vdp_dma_update(mcycles_vdp);578}579580/* render overscan */581if (line < end)582{583blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);584}585586/* update inputs before VINT (Warriors of Eternal Sun) */587osd_input_update();588589/* delay between VINT flag & V Interrupt (Ex-Mutants, Tyrant) */590m68k_run(mcycles_vdp + 588);591status |= 0x80;592593/* delay between VBLANK flag & V Interrupt (Dracula, OutRunners, VR Troopers) */594m68k_run(mcycles_vdp + 788);595if (zstate == 1)596{597z80_run(mcycles_vdp + 788);598}599else600{601Z80.cycles = mcycles_vdp + 788;602}603604/* V Interrupt */605vint_pending = 0x20;606if (reg[1] & 0x20)607{608m68k_set_irq(6);609}610611/* assert Z80 interrupt */612Z80.irq_state = ASSERT_LINE;613614/* run 68k & Z80 until end of line */615m68k_run(mcycles_vdp + MCYCLES_PER_LINE);616if (zstate == 1)617{618z80_run(mcycles_vdp + MCYCLES_PER_LINE);619}620else621{622Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;623}624625/* run SVP chip */626if (svp)627{628ssp1601_run(SVP_cycles);629}630631/* update line cycle count */632mcycles_vdp += MCYCLES_PER_LINE;633634/* increment line count */635line++;636637/* Vertical Blanking */638do639{640/* update V Counter */641v_counter = line;642643/* update 6-Buttons & Lightguns */644input_refresh();645646/* render overscan */647if ((line < end) || (line >= start))648{649blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);650}651652if (zirq)653{654/* Z80 interrupt is asserted exactly for one line */655m68k_run(mcycles_vdp + 788);656if (zstate == 1)657{658z80_run(mcycles_vdp + 788);659}660else661{662Z80.cycles = mcycles_vdp + 788;663}664665/* clear Z80 interrupt */666Z80.irq_state = CLEAR_LINE;667zirq = 0;668}669670/* run 68k & Z80 */671m68k_run(mcycles_vdp + MCYCLES_PER_LINE);672if (zstate == 1)673{674z80_run(mcycles_vdp + MCYCLES_PER_LINE);675}676else677{678Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;679}680681/* run SVP chip */682if (svp)683{684ssp1601_run(SVP_cycles);685}686687/* update line cycle count */688mcycles_vdp += MCYCLES_PER_LINE;689}690while (++line < (lines_per_frame - 1));691692/* adjust CPU cycle counters for next frame */693m68k.cycles -= mcycles_vdp;694Z80.cycles -= mcycles_vdp;695}696697void system_frame_scd(int do_skip)698{699/* line counters */700int start, end, line = 0;701702/* Z80 interrupt flag */703int zirq = 1;704705/* reload H Counter */706int h_counter = reg[10];707708/* reset frame cycle counters */709mcycles_vdp = 0;710scd.cycles = 0;711712/* reload V Counter */713v_counter = lines_per_frame - 1;714715/* reset VDP FIFO */716fifo_write_cnt = 0;717fifo_slots = 0;718719/* update 6-Buttons & Lightguns */720input_refresh();721722/* display changed during VBLANK */723if (bitmap.viewport.changed & 2)724{725/* interlaced modes */726int old_interlaced = interlaced;727interlaced = (reg[12] & 0x02) >> 1;728729if (old_interlaced != interlaced)730{731/* double resolution mode */732im2_flag = ((reg[12] & 0x06) == 0x06);733734/* reset field status flag */735odd_frame = 1;736737/* video mode has changed */738bitmap.viewport.changed = 5;739740/* update rendering mode */741if (reg[1] & 0x04)742{743if (im2_flag)744{745render_bg = (reg[11] & 0x04) ? render_bg_m5_im2_vs : render_bg_m5_im2;746render_obj = (reg[12] & 0x08) ? render_obj_m5_im2_ste : render_obj_m5_im2;747}748else749{750render_bg = (reg[11] & 0x04) ? render_bg_m5_vs : render_bg_m5;751render_obj = (reg[12] & 0x08) ? render_obj_m5_ste : render_obj_m5;752}753}754}755else756{757/* clear flag */758bitmap.viewport.changed &= ~2;759}760761/* active screen height */762if (reg[1] & 0x04)763{764bitmap.viewport.h = 224 + ((reg[1] & 0x08) << 1);765bitmap.viewport.y = (config.overscan & 1) * ((240 + 48*vdp_pal - bitmap.viewport.h) >> 1);766}767else768{769bitmap.viewport.h = 192;770bitmap.viewport.y = (config.overscan & 1) * 24 * (vdp_pal + 1);771}772773/* active screen width */774bitmap.viewport.w = 256 + ((reg[12] & 0x01) << 6);775}776777/* clear VBLANK, DMA, FIFO FULL & field flags */778status &= 0xFEE5;779780/* set FIFO EMPTY flag */781status |= 0x0200;782783/* even/odd field flag (interlaced modes only) */784odd_frame ^= 1;785if (interlaced)786{787status |= (odd_frame << 4);788}789790/* update VDP DMA */791if (dma_length)792{793vdp_dma_update(0);794}795796/* render last line of overscan */797if (bitmap.viewport.y > 0)798{799blank_line(v_counter, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);800}801802/* parse first line of sprites */803if (reg[1] & 0x40)804{805parse_satb(-1);806}807808/* run both 68k & CD hardware */809scd_update(MCYCLES_PER_LINE);810811/* run Z80 */812if (zstate == 1)813{814z80_run(MCYCLES_PER_LINE);815}816else817{818Z80.cycles = MCYCLES_PER_LINE;819}820821/* update line cycle count */822mcycles_vdp += MCYCLES_PER_LINE;823824/* Active Display */825do826{827/* update V Counter */828v_counter = line;829830/* update 6-Buttons & Lightguns */831input_refresh();832833/* H Interrupt */834if(--h_counter < 0)835{836/* reload H Counter */837h_counter = reg[10];838839/* interrupt level 4 */840hint_pending = 0x10;841if (reg[0] & 0x10)842{843m68k_update_irq(4);844}845}846847/* update VDP DMA */848if (dma_length)849{850vdp_dma_update(mcycles_vdp);851}852853/* render scanline */854if (!do_skip)855{856render_line(line);857}858859/* run both 68k & CD hardware */860scd_update(mcycles_vdp + MCYCLES_PER_LINE);861862/* run Z80 */863if (zstate == 1)864{865z80_run(mcycles_vdp + MCYCLES_PER_LINE);866}867else868{869Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;870}871872/* update line cycle count */873mcycles_vdp += MCYCLES_PER_LINE;874}875while (++line < bitmap.viewport.h);876877/* end of active display */878v_counter = line;879880/* set VBLANK flag */881status |= 0x08;882883/* overscan area */884start = lines_per_frame - bitmap.viewport.y;885end = bitmap.viewport.h + bitmap.viewport.y;886887/* check viewport changes */888if ((bitmap.viewport.w != bitmap.viewport.ow) || (bitmap.viewport.h != bitmap.viewport.oh))889{890bitmap.viewport.ow = bitmap.viewport.w;891bitmap.viewport.oh = bitmap.viewport.h;892bitmap.viewport.changed |= 1;893}894895/* update 6-Buttons & Lightguns */896input_refresh();897898/* H Interrupt */899if(--h_counter < 0)900{901/* reload H Counter */902h_counter = reg[10];903904/* interrupt level 4 */905hint_pending = 0x10;906if (reg[0] & 0x10)907{908m68k_update_irq(4);909}910}911912/* update VDP DMA */913if (dma_length)914{915vdp_dma_update(mcycles_vdp);916}917918/* render overscan */919if (line < end)920{921blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);922}923924/* update inputs before VINT (Warriors of Eternal Sun) */925osd_input_update();926927/* delay between VINT flag & V Interrupt (Ex-Mutants, Tyrant) */928m68k_run(mcycles_vdp + 588);929status |= 0x80;930931/* delay between VBLANK flag & V Interrupt (Dracula, OutRunners, VR Troopers) */932m68k_run(mcycles_vdp + 788);933if (zstate == 1)934{935z80_run(mcycles_vdp + 788);936}937else938{939Z80.cycles = mcycles_vdp + 788;940}941942/* V Interrupt */943vint_pending = 0x20;944if (reg[1] & 0x20)945{946m68k_set_irq(6);947}948949/* assert Z80 interrupt */950Z80.irq_state = ASSERT_LINE;951952/* run both 68k & CD hardware */953scd_update(mcycles_vdp + MCYCLES_PER_LINE);954955/* run Z80 until end of line */956if (zstate == 1)957{958z80_run(mcycles_vdp + MCYCLES_PER_LINE);959}960else961{962Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;963}964965/* update line cycle count */966mcycles_vdp += MCYCLES_PER_LINE;967968/* increment line count */969line++;970971/* Vertical Blanking */972do973{974/* update V Counter */975v_counter = line;976977/* update 6-Buttons & Lightguns */978input_refresh();979980/* render overscan */981if ((line < end) || (line >= start))982{983blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);984}985986if (zirq)987{988/* Z80 interrupt is asserted exactly for one line */989m68k_run(mcycles_vdp + 788);990if (zstate == 1)991{992z80_run(mcycles_vdp + 788);993}994else995{996Z80.cycles = mcycles_vdp + 788;997}998999/* clear Z80 interrupt */1000Z80.irq_state = CLEAR_LINE;1001zirq = 0;1002}10031004/* run both 68k & CD hardware */1005scd_update(mcycles_vdp + MCYCLES_PER_LINE);10061007/* run Z80 */1008if (zstate == 1)1009{1010z80_run(mcycles_vdp + MCYCLES_PER_LINE);1011}1012else1013{1014Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;1015}10161017/* update line cycle count */1018mcycles_vdp += MCYCLES_PER_LINE;1019}1020while (++line < (lines_per_frame - 1));10211022/* prepare for next SCD frame */1023scd_end_frame(scd.cycles);10241025/* adjust CPU cycle counters for next frame */1026Z80.cycles -= mcycles_vdp;1027m68k.cycles -= mcycles_vdp;1028}10291030void system_frame_sms(int do_skip)1031{1032/* line counter */1033int start, end, line = 0;10341035/* reload H Counter */1036int h_counter = reg[10];10371038/* reset line master cycle count */1039mcycles_vdp = 0;10401041/* reload V Counter */1042v_counter = lines_per_frame - 1;10431044/* reset VDP FIFO */1045fifo_write_cnt = 0;1046fifo_slots = 0;10471048/* update 6-Buttons & Lightguns */1049input_refresh();10501051/* display changed during VBLANK */1052if (bitmap.viewport.changed & 2)1053{1054bitmap.viewport.changed &= ~2;10551056if (system_hw & SYSTEM_MD)1057{1058/* interlaced mode */1059int old_interlaced = interlaced;1060interlaced = (reg[12] & 0x02) >> 1;1061if (old_interlaced != interlaced)1062{1063im2_flag = ((reg[12] & 0x06) == 0x06);1064odd_frame = 1;1065bitmap.viewport.changed = 5;10661067/* update rendering mode */1068if (reg[1] & 0x04)1069{1070if (im2_flag)1071{1072render_bg = (reg[11] & 0x04) ? render_bg_m5_im2_vs : render_bg_m5_im2;1073render_obj = render_obj_m5_im2;10741075}1076else1077{1078render_bg = (reg[11] & 0x04) ? render_bg_m5_vs : render_bg_m5;1079render_obj = render_obj_m5;1080}1081}1082}10831084/* active screen height */1085if (reg[1] & 0x04)1086{1087bitmap.viewport.h = 224 + ((reg[1] & 0x08) << 1);1088bitmap.viewport.y = (config.overscan & 1) * ((240 + 48*vdp_pal - bitmap.viewport.h) >> 1);1089}1090else1091{1092bitmap.viewport.h = 192;1093bitmap.viewport.y = (config.overscan & 1) * 24 * (vdp_pal + 1);1094}10951096/* active screen width */1097bitmap.viewport.w = 256 + ((reg[12] & 0x01) << 6);1098}1099else1100{1101/* check for VDP extended modes */1102int mode = (reg[0] & 0x06) | (reg[1] & 0x18);11031104/* update active height */1105if (mode == 0x0E)1106{1107bitmap.viewport.h = 240;1108}1109else if (mode == 0x16)1110{1111bitmap.viewport.h = 224;1112}1113else1114{1115bitmap.viewport.h = 192;1116}11171118/* update vertical overscan */1119if (config.overscan & 1)1120{1121bitmap.viewport.y = (240 + 48*vdp_pal - bitmap.viewport.h) >> 1;1122}1123else1124{1125if ((system_hw == SYSTEM_GG) && !config.gg_extra)1126{1127/* Display area reduced to 160x144 */1128bitmap.viewport.y = (144 - bitmap.viewport.h) / 2;1129}1130else1131{1132bitmap.viewport.y = 0;1133}1134}1135}1136}11371138/* Detect pause button input (in Game Gear Mode, NMI is not generated) */1139if (system_hw != SYSTEM_GG)1140{1141if (input.pad[0] & INPUT_START)1142{1143/* NMI is edge-triggered */1144if (!pause_b)1145{1146pause_b = 1;1147z80_set_nmi_line(ASSERT_LINE);1148z80_set_nmi_line(CLEAR_LINE);1149}1150}1151else1152{1153pause_b = 0;1154}1155}11561157/* 3-D glasses faking: skip rendering of left lens frame */1158do_skip |= (work_ram[0x1ffb] & cart.special & HW_3D_GLASSES);11591160/* Mega Drive VDP specific */1161if (system_hw & SYSTEM_MD)1162{1163/* clear VBLANK, DMA & field flags */1164status &= 0xE5;11651166/* even/odd field flag (interlaced modes only) */1167odd_frame ^= 1;1168if (interlaced)1169{1170status |= (odd_frame << 4);1171}11721173/* update VDP DMA */1174if (dma_length)1175{1176vdp_dma_update(0);1177}1178}11791180/* Master System & Game Gear VDP specific */1181if (system_hw < SYSTEM_MD)1182{1183/* Sprites are still processed during vertical borders */1184if (reg[1] & 0x40)1185{1186render_obj(1);1187}1188}11891190/* render last line of overscan */1191if (bitmap.viewport.y > 0)1192{1193blank_line(v_counter, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);1194}11951196/* parse first line of sprites (on Master System VDP, pre-processing still occurs when display is disabled) */1197if ((reg[1] & 0x40) || (system_hw < SYSTEM_MD))1198{1199parse_satb(-1);1200}12011202/* run Z80 */1203z80_run(MCYCLES_PER_LINE);12041205/* update line cycle count */1206mcycles_vdp += MCYCLES_PER_LINE;12071208/* latch Vertical Scroll register */1209vscroll = reg[0x09];12101211/* Active Display */1212do1213{1214/* update VDP DMA (Mega Drive VDP specific) */1215if (dma_length)1216{1217vdp_dma_update(mcycles_vdp);1218}12191220/* make sure we didn't already render that line */1221if (v_counter != line)1222{1223/* update V Counter */1224v_counter = line;12251226/* render scanline */1227if (!do_skip)1228{1229render_line(line);1230}1231}12321233/* update 6-Buttons & Lightguns */1234input_refresh();12351236/* H Interrupt */1237if(--h_counter < 0)1238{1239/* reload H Counter */1240h_counter = reg[10];12411242/* interrupt level 4 */1243hint_pending = 0x10;1244if (reg[0] & 0x10)1245{1246/* cycle-accurate HINT */1247/* IRQ line is latched between instructions, during instruction last cycle. */1248/* This means that if Z80 cycle count is exactly a multiple of MCYCLES_PER_LINE, */1249/* interrupt should be triggered AFTER the next instruction. */1250if ((Z80.cycles % MCYCLES_PER_LINE) == 0)1251{1252z80_run(Z80.cycles + 1);1253}12541255Z80.irq_state = ASSERT_LINE;1256}1257}12581259/* run Z80 */1260z80_run(mcycles_vdp + MCYCLES_PER_LINE);12611262/* update line cycle count */1263mcycles_vdp += MCYCLES_PER_LINE;1264}1265while (++line < bitmap.viewport.h);12661267/* end of active display */1268v_counter = line;12691270/* Mega Drive VDP specific */1271if (system_hw & SYSTEM_MD)1272{1273/* set VBLANK flag */1274status |= 0x08;1275}12761277/* overscan area */1278start = lines_per_frame - bitmap.viewport.y;1279end = bitmap.viewport.h + bitmap.viewport.y;12801281/* check viewport changes */1282if ((bitmap.viewport.w != bitmap.viewport.ow) || (bitmap.viewport.h != bitmap.viewport.oh))1283{1284bitmap.viewport.ow = bitmap.viewport.w;1285bitmap.viewport.oh = bitmap.viewport.h;1286bitmap.viewport.changed |= 1;1287}12881289/* update 6-Buttons & Lightguns */1290input_refresh();12911292/* H Interrupt */1293if(--h_counter < 0)1294{1295/* reload H Counter */1296h_counter = reg[10];12971298/* interrupt level 4 */1299hint_pending = 0x10;1300if (reg[0] & 0x10)1301{1302/* cycle-accurate HINT */1303if ((Z80.cycles % MCYCLES_PER_LINE) == 0)1304{1305z80_run(Z80.cycles + 1);1306}13071308Z80.irq_state = ASSERT_LINE;1309}1310}13111312/* update VDP DMA (Mega Drive VDP specific) */1313if (dma_length)1314{1315vdp_dma_update(mcycles_vdp);1316}13171318/* render overscan */1319if (line < end)1320{1321blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);1322}13231324/* update inputs before VINT */1325osd_input_update();13261327/* run Z80 until end of line */1328z80_run(mcycles_vdp + MCYCLES_PER_LINE);13291330/* make sure VINT flag was not cleared by last instruction */1331if (v_counter == line)1332{1333/* Set VINT flag */1334status |= 0x80;13351336/* V Interrupt */1337vint_pending = 0x20;1338if (reg[1] & 0x20)1339{1340Z80.irq_state = ASSERT_LINE;1341}1342}13431344/* update line cycle count */1345mcycles_vdp += MCYCLES_PER_LINE;13461347/* increment line count */1348line++;13491350/* Vertical Blanking */1351do1352{1353/* update V Counter */1354v_counter = line;13551356/* update 6-Buttons & Lightguns */1357input_refresh();13581359/* Master System & Game Gear VDP specific */1360if ((system_hw < SYSTEM_MD) && (line > (lines_per_frame - 16)))1361{1362/* Sprites are still processed during top border */1363render_obj((line - lines_per_frame) & 1);1364parse_satb(line - lines_per_frame);1365}13661367/* render overscan */1368if ((line < end) || (line >= start))1369{1370blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);1371}13721373/* run Z80 */1374z80_run(mcycles_vdp + MCYCLES_PER_LINE);13751376/* update line cycle count */1377mcycles_vdp += MCYCLES_PER_LINE;1378}1379while (++line < (lines_per_frame - 1));13801381/* adjust Z80 cycle count for next frame */1382Z80.cycles -= mcycles_vdp;1383}138413851386