Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/genplus-gx32/core/system.c
2 views
1
/***************************************************************************************
2
* Genesis Plus
3
* Virtual System emulation
4
*
5
* Support for "Genesis", "Genesis + CD" & "Master System" modes
6
*
7
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
8
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
9
*
10
* Redistribution and use of this code or any derivative works are permitted
11
* provided that the following conditions are met:
12
*
13
* - Redistributions may not be sold, nor may they be used in a commercial
14
* product or activity.
15
*
16
* - Redistributions that are modified from the original source must include the
17
* complete source code, including the source code for all components used by a
18
* binary built from the modified sources. However, as a special exception, the
19
* source code distributed need not include anything that is normally distributed
20
* (in either source or binary form) with the major components (compiler, kernel,
21
* and so on) of the operating system on which the executable runs, unless that
22
* component itself accompanies the executable.
23
*
24
* - Redistributions must reproduce the above copyright notice, this list of
25
* conditions and the following disclaimer in the documentation and/or other
26
* materials provided with the distribution.
27
*
28
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
29
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
32
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38
* POSSIBILITY OF SUCH DAMAGE.
39
*
40
****************************************************************************************/
41
42
#include "shared.h"
43
#include "eq.h"
44
45
/* Global variables */
46
t_bitmap bitmap;
47
t_snd snd;
48
uint32 mcycles_vdp;
49
uint8 system_hw;
50
uint8 system_bios;
51
uint32 system_clock;
52
const int16 SVP_cycles = 800;
53
54
uint8 pause_b;
55
EQSTATE eq;
56
int16 llp,rrp;
57
58
/******************************************************************************************/
59
/* Audio subsystem */
60
/******************************************************************************************/
61
62
int audio_init(int samplerate, double framerate)
63
{
64
/* Number of M-cycles executed per second. */
65
/* All emulated chips are kept in sync by using a common oscillator (MCLOCK) */
66
/* */
67
/* The original console would run exactly 53693175 M-cycles per sec (53203424 for PAL), */
68
/* 3420 M-cycles per line and 262 (313 for PAL) lines per frame, which gives an exact */
69
/* framerate of 59.92 (49.70 for PAL) frames per second. */
70
/* */
71
/* Since audio samples are generated at the end of the frame, to prevent audio skipping */
72
/* or lag between emulated frames, number of samples rendered per frame must be set to */
73
/* output samplerate (number of samples played per second) divided by input framerate */
74
/* (number of frames emulated per seconds). */
75
/* */
76
/* On some systems, we may want to achieve 100% smooth video rendering by synchronizing */
77
/* frame emulation with VSYNC, which frequency is generally not exactly those values. */
78
/* In that case, input framerate (number of frames emulated per seconds) is the same as */
79
/* output framerate (number of frames rendered per seconds) by the host video hardware. */
80
/* */
81
/* When no framerate is specified, base clock is set to original master clock value. */
82
/* Otherwise, it is set to number of M-cycles emulated per line (fixed) multiplied by */
83
/* number of lines per frame (VDP mode specific) multiplied by input framerate. */
84
/* */
85
double mclk = framerate ? (MCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * framerate) : system_clock;
86
87
/* Shutdown first */
88
audio_shutdown();
89
90
/* Clear the sound data context */
91
memset(&snd, 0, sizeof (snd));
92
93
/* Initialize audio rates */
94
snd.sample_rate = samplerate;
95
snd.frame_rate = framerate;
96
97
/* Initialize Blip Buffers */
98
snd.blips[0][0] = blip_new(samplerate / 10);
99
snd.blips[0][1] = blip_new(samplerate / 10);
100
if (!snd.blips[0][0] || !snd.blips[0][1])
101
{
102
audio_shutdown();
103
return -1;
104
}
105
106
/* For maximal accuracy, sound chips are running at their original rate using common */
107
/* master clock timebase so they remain perfectly synchronized together, while still */
108
/* being synchronized with 68K and Z80 CPUs as well. Mixed sound chip output is then */
109
/* resampled to desired rate at the end of each frame, using Blip Buffer. */
110
blip_set_rates(snd.blips[0][0], mclk, samplerate);
111
blip_set_rates(snd.blips[0][1], mclk, samplerate);
112
113
/* Initialize PSG core */
114
SN76489_Init(snd.blips[0][0], snd.blips[0][1], (system_hw < SYSTEM_MARKIII) ? SN_DISCRETE : SN_INTEGRATED);
115
116
/* Mega CD sound hardware */
117
if (system_hw == SYSTEM_MCD)
118
{
119
/* allocate blip buffers */
120
snd.blips[1][0] = blip_new(samplerate / 10);
121
snd.blips[1][1] = blip_new(samplerate / 10);
122
snd.blips[2][0] = blip_new(samplerate / 10);
123
snd.blips[2][1] = blip_new(samplerate / 10);
124
if (!snd.blips[1][0] || !snd.blips[1][1] || !snd.blips[2][0] || !snd.blips[2][1])
125
{
126
audio_shutdown();
127
return -1;
128
}
129
130
/* Initialize PCM core */
131
pcm_init(snd.blips[1][0], snd.blips[1][1]);
132
133
/* Initialize CDD core */
134
cdd_init(snd.blips[2][0], snd.blips[2][1]);
135
}
136
137
/* Set audio enable flag */
138
snd.enabled = 1;
139
140
/* Reset audio */
141
audio_reset();
142
143
return (0);
144
}
145
146
void audio_reset(void)
147
{
148
int i,j;
149
150
/* Clear blip buffers */
151
for (i=0; i<3; i++)
152
{
153
for (j=0; j<2; j++)
154
{
155
if (snd.blips[i][j])
156
{
157
blip_clear(snd.blips[i][j]);
158
}
159
}
160
}
161
162
/* Low-Pass filter */
163
llp = 0;
164
rrp = 0;
165
166
/* 3 band EQ */
167
audio_set_equalizer();
168
}
169
170
void audio_set_equalizer(void)
171
{
172
init_3band_state(&eq,config.low_freq,config.high_freq,snd.sample_rate);
173
eq.lg = (double)(config.lg) / 100.0;
174
eq.mg = (double)(config.mg) / 100.0;
175
eq.hg = (double)(config.hg) / 100.0;
176
}
177
178
void audio_shutdown(void)
179
{
180
int i,j;
181
182
/* Delete blip buffers */
183
for (i=0; i<3; i++)
184
{
185
for (j=0; j<2; j++)
186
{
187
blip_delete(snd.blips[i][j]);
188
snd.blips[i][j] = 0;
189
}
190
}
191
}
192
193
int audio_update(int16 *buffer)
194
{
195
/* run sound chips until end of frame */
196
int size = sound_update(mcycles_vdp);
197
198
/* Mega CD specific */
199
if (system_hw == SYSTEM_MCD)
200
{
201
/* sync PCM chip with other sound chips */
202
pcm_update(size);
203
204
/* read CDDA samples */
205
cdd_read_audio(size);
206
}
207
208
#ifdef ALIGN_SND
209
/* return an aligned number of samples if required */
210
size &= ALIGN_SND;
211
#endif
212
213
/* resample FM & PSG mixed stream to output buffer */
214
#ifdef LSB_FIRST
215
blip_read_samples(snd.blips[0][0], buffer, size);
216
blip_read_samples(snd.blips[0][1], buffer + 1, size);
217
#else
218
blip_read_samples(snd.blips[0][0], buffer + 1, size);
219
blip_read_samples(snd.blips[0][1], buffer, size);
220
#endif
221
222
/* Mega CD specific */
223
if (system_hw == SYSTEM_MCD)
224
{
225
/* resample PCM & CD-DA streams to output buffer */
226
#ifdef LSB_FIRST
227
blip_mix_samples(snd.blips[1][0], buffer, size);
228
blip_mix_samples(snd.blips[1][1], buffer + 1, size);
229
blip_mix_samples(snd.blips[2][0], buffer, size);
230
blip_mix_samples(snd.blips[2][1], buffer + 1, size);
231
#else
232
blip_mix_samples(snd.blips[1][0], buffer + 1, size);
233
blip_mix_samples(snd.blips[1][1], buffer, size);
234
blip_mix_samples(snd.blips[2][0], buffer + 1, size);
235
blip_mix_samples(snd.blips[2][1], buffer, size);
236
#endif
237
}
238
239
/* Audio filtering */
240
if (config.filter)
241
{
242
int samples = size;
243
int16 *out = buffer;
244
int32 l, r;
245
246
if (config.filter & 1)
247
{
248
/* single-pole low-pass filter (6 dB/octave) */
249
uint32 factora = config.lp_range;
250
uint32 factorb = 0x10000 - factora;
251
252
/* restore previous sample */
253
l = llp;
254
r = rrp;
255
256
do
257
{
258
/* apply low-pass filter */
259
l = l*factora + out[0]*factorb;
260
r = r*factora + out[1]*factorb;
261
262
/* 16.16 fixed point */
263
l >>= 16;
264
r >>= 16;
265
266
/* update sound buffer */
267
*out++ = l;
268
*out++ = r;
269
}
270
while (--samples);
271
272
/* save last samples for next frame */
273
llp = l;
274
rrp = r;
275
}
276
else if (config.filter & 2)
277
{
278
do
279
{
280
/* 3 Band EQ */
281
l = do_3band(&eq,out[0]);
282
r = do_3band(&eq,out[1]);
283
284
/* clipping (16-bit samples) */
285
if (l > 32767) l = 32767;
286
else if (l < -32768) l = -32768;
287
if (r > 32767) r = 32767;
288
else if (r < -32768) r = -32768;
289
290
/* update sound buffer */
291
*out++ = l;
292
*out++ = r;
293
}
294
while (--samples);
295
}
296
}
297
298
/* Mono output mixing */
299
if (config.mono)
300
{
301
int16 out;
302
int samples = size;
303
do
304
{
305
out = (buffer[0] + buffer[1]) / 2;
306
*buffer++ = out;
307
*buffer++ = out;
308
}
309
while (--samples);
310
}
311
312
#ifdef LOGSOUND
313
error("%d samples returned\n\n",size);
314
#endif
315
316
return size;
317
}
318
319
/****************************************************************
320
* Virtual System emulation
321
****************************************************************/
322
void system_init(void)
323
{
324
gen_init();
325
io_init();
326
vdp_init();
327
render_init();
328
sound_init();
329
}
330
331
void system_reset(void)
332
{
333
gen_reset(1);
334
io_reset();
335
render_reset();
336
vdp_reset();
337
sound_reset();
338
audio_reset();
339
}
340
341
void system_frame_gen(int do_skip)
342
{
343
/* line counters */
344
int start, end, line = 0;
345
346
/* Z80 interrupt flag */
347
int zirq = 1;
348
349
/* reload H Counter */
350
int h_counter = reg[10];
351
352
/* reset frame cycle counter */
353
mcycles_vdp = 0;
354
355
/* reload V Counter */
356
v_counter = lines_per_frame - 1;
357
358
/* reset VDP FIFO */
359
fifo_write_cnt = 0;
360
fifo_slots = 0;
361
362
/* update 6-Buttons & Lightguns */
363
input_refresh();
364
365
/* display changed during VBLANK */
366
if (bitmap.viewport.changed & 2)
367
{
368
/* interlaced modes */
369
int old_interlaced = interlaced;
370
interlaced = (reg[12] & 0x02) >> 1;
371
372
if (old_interlaced != interlaced)
373
{
374
/* double resolution mode */
375
im2_flag = ((reg[12] & 0x06) == 0x06);
376
377
/* reset field status flag */
378
odd_frame = 1;
379
380
/* video mode has changed */
381
bitmap.viewport.changed = 5;
382
383
/* update rendering mode */
384
if (reg[1] & 0x04)
385
{
386
if (im2_flag)
387
{
388
render_bg = (reg[11] & 0x04) ? render_bg_m5_im2_vs : render_bg_m5_im2;
389
render_obj = (reg[12] & 0x08) ? render_obj_m5_im2_ste : render_obj_m5_im2;
390
}
391
else
392
{
393
render_bg = (reg[11] & 0x04) ? render_bg_m5_vs : render_bg_m5;
394
render_obj = (reg[12] & 0x08) ? render_obj_m5_ste : render_obj_m5;
395
}
396
}
397
}
398
else
399
{
400
/* clear flag */
401
bitmap.viewport.changed &= ~2;
402
}
403
404
/* active screen height */
405
if (reg[1] & 0x04)
406
{
407
/* Mode 5 */
408
if (reg[1] & 0x08)
409
{
410
/* 240 active lines */
411
bitmap.viewport.h = 240;
412
bitmap.viewport.y = (config.overscan & 1) * 24 * vdp_pal;
413
}
414
else
415
{
416
/* 224 active lines */
417
bitmap.viewport.h = 224;
418
bitmap.viewport.y = (config.overscan & 1) * (8 + (24 * vdp_pal));
419
}
420
}
421
else
422
{
423
/* Mode 4 (192 active lines) */
424
bitmap.viewport.h = 192;
425
bitmap.viewport.y = (config.overscan & 1) * 24 * (vdp_pal + 1);
426
}
427
428
/* active screen width */
429
bitmap.viewport.w = 256 + ((reg[12] & 0x01) << 6);
430
}
431
432
/* clear VBLANK, DMA, FIFO FULL & field flags */
433
status &= 0xFEE5;
434
435
/* set FIFO EMPTY flag */
436
status |= 0x0200;
437
438
/* even/odd field flag (interlaced modes only) */
439
odd_frame ^= 1;
440
if (interlaced)
441
{
442
status |= (odd_frame << 4);
443
}
444
445
/* update VDP DMA */
446
if (dma_length)
447
{
448
vdp_dma_update(0);
449
}
450
451
/* render last line of overscan */
452
if (bitmap.viewport.y > 0)
453
{
454
blank_line(v_counter, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);
455
}
456
457
/* parse first line of sprites */
458
if (reg[1] & 0x40)
459
{
460
parse_satb(-1);
461
}
462
463
/* run 68k & Z80 */
464
m68k_run(MCYCLES_PER_LINE);
465
if (zstate == 1)
466
{
467
z80_run(MCYCLES_PER_LINE);
468
}
469
else
470
{
471
Z80.cycles = MCYCLES_PER_LINE;
472
}
473
474
/* run SVP chip */
475
if (svp)
476
{
477
ssp1601_run(SVP_cycles);
478
}
479
480
/* update line cycle count */
481
mcycles_vdp += MCYCLES_PER_LINE;
482
483
/* Active Display */
484
do
485
{
486
/* update V Counter */
487
v_counter = line;
488
489
/* update 6-Buttons & Lightguns */
490
input_refresh();
491
492
/* H Interrupt */
493
if(--h_counter < 0)
494
{
495
/* reload H Counter */
496
h_counter = reg[10];
497
498
/* interrupt level 4 */
499
hint_pending = 0x10;
500
if (reg[0] & 0x10)
501
{
502
m68k_update_irq(4);
503
}
504
}
505
506
/* update VDP DMA */
507
if (dma_length)
508
{
509
vdp_dma_update(mcycles_vdp);
510
}
511
512
/* render scanline */
513
if (!do_skip)
514
{
515
render_line(line);
516
}
517
518
/* run 68k & Z80 */
519
m68k_run(mcycles_vdp + MCYCLES_PER_LINE);
520
if (zstate == 1)
521
{
522
z80_run(mcycles_vdp + MCYCLES_PER_LINE);
523
}
524
else
525
{
526
Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;
527
}
528
529
/* run SVP chip */
530
if (svp)
531
{
532
ssp1601_run(SVP_cycles);
533
}
534
535
/* update line cycle count */
536
mcycles_vdp += MCYCLES_PER_LINE;
537
}
538
while (++line < bitmap.viewport.h);
539
540
/* end of active display */
541
v_counter = line;
542
543
/* set VBLANK flag */
544
status |= 0x08;
545
546
/* overscan area */
547
start = lines_per_frame - bitmap.viewport.y;
548
end = bitmap.viewport.h + bitmap.viewport.y;
549
550
/* check viewport changes */
551
if ((bitmap.viewport.w != bitmap.viewport.ow) || (bitmap.viewport.h != bitmap.viewport.oh))
552
{
553
bitmap.viewport.ow = bitmap.viewport.w;
554
bitmap.viewport.oh = bitmap.viewport.h;
555
bitmap.viewport.changed |= 1;
556
}
557
558
/* update 6-Buttons & Lightguns */
559
input_refresh();
560
561
/* H Interrupt */
562
if(--h_counter < 0)
563
{
564
/* reload H Counter */
565
h_counter = reg[10];
566
567
/* interrupt level 4 */
568
hint_pending = 0x10;
569
if (reg[0] & 0x10)
570
{
571
m68k_update_irq(4);
572
}
573
}
574
575
/* update VDP DMA */
576
if (dma_length)
577
{
578
vdp_dma_update(mcycles_vdp);
579
}
580
581
/* render overscan */
582
if (line < end)
583
{
584
blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);
585
}
586
587
/* update inputs before VINT (Warriors of Eternal Sun) */
588
osd_input_update();
589
590
/* delay between VINT flag & V Interrupt (Ex-Mutants, Tyrant) */
591
m68k_run(mcycles_vdp + 588);
592
status |= 0x80;
593
594
/* delay between VBLANK flag & V Interrupt (Dracula, OutRunners, VR Troopers) */
595
m68k_run(mcycles_vdp + 788);
596
if (zstate == 1)
597
{
598
z80_run(mcycles_vdp + 788);
599
}
600
else
601
{
602
Z80.cycles = mcycles_vdp + 788;
603
}
604
605
/* V Interrupt */
606
vint_pending = 0x20;
607
if (reg[1] & 0x20)
608
{
609
m68k_set_irq(6);
610
}
611
612
/* assert Z80 interrupt */
613
Z80.irq_state = ASSERT_LINE;
614
615
/* run 68k & Z80 until end of line */
616
m68k_run(mcycles_vdp + MCYCLES_PER_LINE);
617
if (zstate == 1)
618
{
619
z80_run(mcycles_vdp + MCYCLES_PER_LINE);
620
}
621
else
622
{
623
Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;
624
}
625
626
/* run SVP chip */
627
if (svp)
628
{
629
ssp1601_run(SVP_cycles);
630
}
631
632
/* update line cycle count */
633
mcycles_vdp += MCYCLES_PER_LINE;
634
635
/* increment line count */
636
line++;
637
638
/* Vertical Blanking */
639
do
640
{
641
/* update V Counter */
642
v_counter = line;
643
644
/* update 6-Buttons & Lightguns */
645
input_refresh();
646
647
/* render overscan */
648
if ((line < end) || (line >= start))
649
{
650
blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);
651
}
652
653
if (zirq)
654
{
655
/* Z80 interrupt is asserted exactly for one line */
656
m68k_run(mcycles_vdp + 788);
657
if (zstate == 1)
658
{
659
z80_run(mcycles_vdp + 788);
660
}
661
else
662
{
663
Z80.cycles = mcycles_vdp + 788;
664
}
665
666
/* clear Z80 interrupt */
667
Z80.irq_state = CLEAR_LINE;
668
zirq = 0;
669
}
670
671
/* run 68k & Z80 */
672
m68k_run(mcycles_vdp + MCYCLES_PER_LINE);
673
if (zstate == 1)
674
{
675
z80_run(mcycles_vdp + MCYCLES_PER_LINE);
676
}
677
else
678
{
679
Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;
680
}
681
682
/* run SVP chip */
683
if (svp)
684
{
685
ssp1601_run(SVP_cycles);
686
}
687
688
/* update line cycle count */
689
mcycles_vdp += MCYCLES_PER_LINE;
690
}
691
while (++line < (lines_per_frame - 1));
692
693
/* adjust CPU cycle counters for next frame */
694
m68k.cycles -= mcycles_vdp;
695
Z80.cycles -= mcycles_vdp;
696
}
697
698
void system_frame_scd(int do_skip)
699
{
700
/* line counters */
701
int start, end, line = 0;
702
703
/* Z80 interrupt flag */
704
int zirq = 1;
705
706
/* reload H Counter */
707
int h_counter = reg[10];
708
709
/* reset frame cycle counters */
710
mcycles_vdp = 0;
711
scd.cycles = 0;
712
713
/* reload V Counter */
714
v_counter = lines_per_frame - 1;
715
716
/* reset VDP FIFO */
717
fifo_write_cnt = 0;
718
fifo_slots = 0;
719
720
/* update 6-Buttons & Lightguns */
721
input_refresh();
722
723
/* display changed during VBLANK */
724
if (bitmap.viewport.changed & 2)
725
{
726
/* interlaced modes */
727
int old_interlaced = interlaced;
728
interlaced = (reg[12] & 0x02) >> 1;
729
730
if (old_interlaced != interlaced)
731
{
732
/* double resolution mode */
733
im2_flag = ((reg[12] & 0x06) == 0x06);
734
735
/* reset field status flag */
736
odd_frame = 1;
737
738
/* video mode has changed */
739
bitmap.viewport.changed = 5;
740
741
/* update rendering mode */
742
if (reg[1] & 0x04)
743
{
744
if (im2_flag)
745
{
746
render_bg = (reg[11] & 0x04) ? render_bg_m5_im2_vs : render_bg_m5_im2;
747
render_obj = (reg[12] & 0x08) ? render_obj_m5_im2_ste : render_obj_m5_im2;
748
}
749
else
750
{
751
render_bg = (reg[11] & 0x04) ? render_bg_m5_vs : render_bg_m5;
752
render_obj = (reg[12] & 0x08) ? render_obj_m5_ste : render_obj_m5;
753
}
754
}
755
}
756
else
757
{
758
/* clear flag */
759
bitmap.viewport.changed &= ~2;
760
}
761
762
/* active screen height */
763
if (reg[1] & 0x04)
764
{
765
bitmap.viewport.h = 224 + ((reg[1] & 0x08) << 1);
766
bitmap.viewport.y = (config.overscan & 1) * ((240 + 48*vdp_pal - bitmap.viewport.h) >> 1);
767
}
768
else
769
{
770
bitmap.viewport.h = 192;
771
bitmap.viewport.y = (config.overscan & 1) * 24 * (vdp_pal + 1);
772
}
773
774
/* active screen width */
775
bitmap.viewport.w = 256 + ((reg[12] & 0x01) << 6);
776
}
777
778
/* clear VBLANK, DMA, FIFO FULL & field flags */
779
status &= 0xFEE5;
780
781
/* set FIFO EMPTY flag */
782
status |= 0x0200;
783
784
/* even/odd field flag (interlaced modes only) */
785
odd_frame ^= 1;
786
if (interlaced)
787
{
788
status |= (odd_frame << 4);
789
}
790
791
/* update VDP DMA */
792
if (dma_length)
793
{
794
vdp_dma_update(0);
795
}
796
797
/* render last line of overscan */
798
if (bitmap.viewport.y > 0)
799
{
800
blank_line(v_counter, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);
801
}
802
803
/* parse first line of sprites */
804
if (reg[1] & 0x40)
805
{
806
parse_satb(-1);
807
}
808
809
/* run both 68k & CD hardware */
810
scd_update(MCYCLES_PER_LINE);
811
812
/* run Z80 */
813
if (zstate == 1)
814
{
815
z80_run(MCYCLES_PER_LINE);
816
}
817
else
818
{
819
Z80.cycles = MCYCLES_PER_LINE;
820
}
821
822
/* update line cycle count */
823
mcycles_vdp += MCYCLES_PER_LINE;
824
825
/* Active Display */
826
do
827
{
828
/* update V Counter */
829
v_counter = line;
830
831
/* update 6-Buttons & Lightguns */
832
input_refresh();
833
834
/* H Interrupt */
835
if(--h_counter < 0)
836
{
837
/* reload H Counter */
838
h_counter = reg[10];
839
840
/* interrupt level 4 */
841
hint_pending = 0x10;
842
if (reg[0] & 0x10)
843
{
844
m68k_update_irq(4);
845
}
846
}
847
848
/* update VDP DMA */
849
if (dma_length)
850
{
851
vdp_dma_update(mcycles_vdp);
852
}
853
854
/* render scanline */
855
if (!do_skip)
856
{
857
render_line(line);
858
}
859
860
/* run both 68k & CD hardware */
861
scd_update(mcycles_vdp + MCYCLES_PER_LINE);
862
863
/* run Z80 */
864
if (zstate == 1)
865
{
866
z80_run(mcycles_vdp + MCYCLES_PER_LINE);
867
}
868
else
869
{
870
Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;
871
}
872
873
/* update line cycle count */
874
mcycles_vdp += MCYCLES_PER_LINE;
875
}
876
while (++line < bitmap.viewport.h);
877
878
/* end of active display */
879
v_counter = line;
880
881
/* set VBLANK flag */
882
status |= 0x08;
883
884
/* overscan area */
885
start = lines_per_frame - bitmap.viewport.y;
886
end = bitmap.viewport.h + bitmap.viewport.y;
887
888
/* check viewport changes */
889
if ((bitmap.viewport.w != bitmap.viewport.ow) || (bitmap.viewport.h != bitmap.viewport.oh))
890
{
891
bitmap.viewport.ow = bitmap.viewport.w;
892
bitmap.viewport.oh = bitmap.viewport.h;
893
bitmap.viewport.changed |= 1;
894
}
895
896
/* update 6-Buttons & Lightguns */
897
input_refresh();
898
899
/* H Interrupt */
900
if(--h_counter < 0)
901
{
902
/* reload H Counter */
903
h_counter = reg[10];
904
905
/* interrupt level 4 */
906
hint_pending = 0x10;
907
if (reg[0] & 0x10)
908
{
909
m68k_update_irq(4);
910
}
911
}
912
913
/* update VDP DMA */
914
if (dma_length)
915
{
916
vdp_dma_update(mcycles_vdp);
917
}
918
919
/* render overscan */
920
if (line < end)
921
{
922
blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);
923
}
924
925
/* update inputs before VINT (Warriors of Eternal Sun) */
926
osd_input_update();
927
928
/* delay between VINT flag & V Interrupt (Ex-Mutants, Tyrant) */
929
m68k_run(mcycles_vdp + 588);
930
status |= 0x80;
931
932
/* delay between VBLANK flag & V Interrupt (Dracula, OutRunners, VR Troopers) */
933
m68k_run(mcycles_vdp + 788);
934
if (zstate == 1)
935
{
936
z80_run(mcycles_vdp + 788);
937
}
938
else
939
{
940
Z80.cycles = mcycles_vdp + 788;
941
}
942
943
/* V Interrupt */
944
vint_pending = 0x20;
945
if (reg[1] & 0x20)
946
{
947
m68k_set_irq(6);
948
}
949
950
/* assert Z80 interrupt */
951
Z80.irq_state = ASSERT_LINE;
952
953
/* run both 68k & CD hardware */
954
scd_update(mcycles_vdp + MCYCLES_PER_LINE);
955
956
/* run Z80 until end of line */
957
if (zstate == 1)
958
{
959
z80_run(mcycles_vdp + MCYCLES_PER_LINE);
960
}
961
else
962
{
963
Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;
964
}
965
966
/* update line cycle count */
967
mcycles_vdp += MCYCLES_PER_LINE;
968
969
/* increment line count */
970
line++;
971
972
/* Vertical Blanking */
973
do
974
{
975
/* update V Counter */
976
v_counter = line;
977
978
/* update 6-Buttons & Lightguns */
979
input_refresh();
980
981
/* render overscan */
982
if ((line < end) || (line >= start))
983
{
984
blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);
985
}
986
987
if (zirq)
988
{
989
/* Z80 interrupt is asserted exactly for one line */
990
m68k_run(mcycles_vdp + 788);
991
if (zstate == 1)
992
{
993
z80_run(mcycles_vdp + 788);
994
}
995
else
996
{
997
Z80.cycles = mcycles_vdp + 788;
998
}
999
1000
/* clear Z80 interrupt */
1001
Z80.irq_state = CLEAR_LINE;
1002
zirq = 0;
1003
}
1004
1005
/* run both 68k & CD hardware */
1006
scd_update(mcycles_vdp + MCYCLES_PER_LINE);
1007
1008
/* run Z80 */
1009
if (zstate == 1)
1010
{
1011
z80_run(mcycles_vdp + MCYCLES_PER_LINE);
1012
}
1013
else
1014
{
1015
Z80.cycles = mcycles_vdp + MCYCLES_PER_LINE;
1016
}
1017
1018
/* update line cycle count */
1019
mcycles_vdp += MCYCLES_PER_LINE;
1020
}
1021
while (++line < (lines_per_frame - 1));
1022
1023
/* prepare for next SCD frame */
1024
scd_end_frame(scd.cycles);
1025
1026
/* adjust CPU cycle counters for next frame */
1027
Z80.cycles -= mcycles_vdp;
1028
m68k.cycles -= mcycles_vdp;
1029
}
1030
1031
void system_frame_sms(int do_skip)
1032
{
1033
/* line counter */
1034
int start, end, line = 0;
1035
1036
/* reload H Counter */
1037
int h_counter = reg[10];
1038
1039
/* reset line master cycle count */
1040
mcycles_vdp = 0;
1041
1042
/* reload V Counter */
1043
v_counter = lines_per_frame - 1;
1044
1045
/* reset VDP FIFO */
1046
fifo_write_cnt = 0;
1047
fifo_slots = 0;
1048
1049
/* update 6-Buttons & Lightguns */
1050
input_refresh();
1051
1052
/* display changed during VBLANK */
1053
if (bitmap.viewport.changed & 2)
1054
{
1055
bitmap.viewport.changed &= ~2;
1056
1057
if (system_hw & SYSTEM_MD)
1058
{
1059
/* interlaced mode */
1060
int old_interlaced = interlaced;
1061
interlaced = (reg[12] & 0x02) >> 1;
1062
if (old_interlaced != interlaced)
1063
{
1064
im2_flag = ((reg[12] & 0x06) == 0x06);
1065
odd_frame = 1;
1066
bitmap.viewport.changed = 5;
1067
1068
/* update rendering mode */
1069
if (reg[1] & 0x04)
1070
{
1071
if (im2_flag)
1072
{
1073
render_bg = (reg[11] & 0x04) ? render_bg_m5_im2_vs : render_bg_m5_im2;
1074
render_obj = render_obj_m5_im2;
1075
1076
}
1077
else
1078
{
1079
render_bg = (reg[11] & 0x04) ? render_bg_m5_vs : render_bg_m5;
1080
render_obj = render_obj_m5;
1081
}
1082
}
1083
}
1084
1085
/* active screen height */
1086
if (reg[1] & 0x04)
1087
{
1088
bitmap.viewport.h = 224 + ((reg[1] & 0x08) << 1);
1089
bitmap.viewport.y = (config.overscan & 1) * ((240 + 48*vdp_pal - bitmap.viewport.h) >> 1);
1090
}
1091
else
1092
{
1093
bitmap.viewport.h = 192;
1094
bitmap.viewport.y = (config.overscan & 1) * 24 * (vdp_pal + 1);
1095
}
1096
1097
/* active screen width */
1098
bitmap.viewport.w = 256 + ((reg[12] & 0x01) << 6);
1099
}
1100
else
1101
{
1102
/* check for VDP extended modes */
1103
int mode = (reg[0] & 0x06) | (reg[1] & 0x18);
1104
1105
/* update active height */
1106
if (mode == 0x0E)
1107
{
1108
bitmap.viewport.h = 240;
1109
}
1110
else if (mode == 0x16)
1111
{
1112
bitmap.viewport.h = 224;
1113
}
1114
else
1115
{
1116
bitmap.viewport.h = 192;
1117
}
1118
1119
/* update vertical overscan */
1120
if (config.overscan & 1)
1121
{
1122
bitmap.viewport.y = (240 + 48*vdp_pal - bitmap.viewport.h) >> 1;
1123
}
1124
else
1125
{
1126
if ((system_hw == SYSTEM_GG) && !config.gg_extra)
1127
{
1128
/* Display area reduced to 160x144 */
1129
bitmap.viewport.y = (144 - bitmap.viewport.h) / 2;
1130
}
1131
else
1132
{
1133
bitmap.viewport.y = 0;
1134
}
1135
}
1136
}
1137
}
1138
1139
/* Detect pause button input (in Game Gear Mode, NMI is not generated) */
1140
if (system_hw != SYSTEM_GG)
1141
{
1142
if (input.pad[0] & INPUT_START)
1143
{
1144
/* NMI is edge-triggered */
1145
if (!pause_b)
1146
{
1147
pause_b = 1;
1148
z80_set_nmi_line(ASSERT_LINE);
1149
z80_set_nmi_line(CLEAR_LINE);
1150
}
1151
}
1152
else
1153
{
1154
pause_b = 0;
1155
}
1156
}
1157
1158
/* 3-D glasses faking: skip rendering of left lens frame */
1159
do_skip |= (work_ram[0x1ffb] & cart.special & HW_3D_GLASSES);
1160
1161
/* Mega Drive VDP specific */
1162
if (system_hw & SYSTEM_MD)
1163
{
1164
/* clear VBLANK, DMA & field flags */
1165
status &= 0xE5;
1166
1167
/* even/odd field flag (interlaced modes only) */
1168
odd_frame ^= 1;
1169
if (interlaced)
1170
{
1171
status |= (odd_frame << 4);
1172
}
1173
1174
/* update VDP DMA */
1175
if (dma_length)
1176
{
1177
vdp_dma_update(0);
1178
}
1179
}
1180
1181
/* Master System & Game Gear VDP specific */
1182
if (system_hw < SYSTEM_MD)
1183
{
1184
/* Sprites are still processed during vertical borders */
1185
if (reg[1] & 0x40)
1186
{
1187
render_obj(1);
1188
}
1189
}
1190
1191
/* render last line of overscan */
1192
if (bitmap.viewport.y > 0)
1193
{
1194
blank_line(v_counter, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);
1195
}
1196
1197
/* parse first line of sprites (on Master System VDP, pre-processing still occurs when display is disabled) */
1198
if ((reg[1] & 0x40) || (system_hw < SYSTEM_MD))
1199
{
1200
parse_satb(-1);
1201
}
1202
1203
/* run Z80 */
1204
z80_run(MCYCLES_PER_LINE);
1205
1206
/* update line cycle count */
1207
mcycles_vdp += MCYCLES_PER_LINE;
1208
1209
/* latch Vertical Scroll register */
1210
vscroll = reg[0x09];
1211
1212
/* Active Display */
1213
do
1214
{
1215
/* update VDP DMA (Mega Drive VDP specific) */
1216
if (dma_length)
1217
{
1218
vdp_dma_update(mcycles_vdp);
1219
}
1220
1221
/* make sure we didn't already render that line */
1222
if (v_counter != line)
1223
{
1224
/* update V Counter */
1225
v_counter = line;
1226
1227
/* render scanline */
1228
if (!do_skip)
1229
{
1230
render_line(line);
1231
}
1232
}
1233
1234
/* update 6-Buttons & Lightguns */
1235
input_refresh();
1236
1237
/* H Interrupt */
1238
if(--h_counter < 0)
1239
{
1240
/* reload H Counter */
1241
h_counter = reg[10];
1242
1243
/* interrupt level 4 */
1244
hint_pending = 0x10;
1245
if (reg[0] & 0x10)
1246
{
1247
/* cycle-accurate HINT */
1248
/* IRQ line is latched between instructions, during instruction last cycle. */
1249
/* This means that if Z80 cycle count is exactly a multiple of MCYCLES_PER_LINE, */
1250
/* interrupt should be triggered AFTER the next instruction. */
1251
if ((Z80.cycles % MCYCLES_PER_LINE) == 0)
1252
{
1253
z80_run(Z80.cycles + 1);
1254
}
1255
1256
Z80.irq_state = ASSERT_LINE;
1257
}
1258
}
1259
1260
/* run Z80 */
1261
z80_run(mcycles_vdp + MCYCLES_PER_LINE);
1262
1263
/* update line cycle count */
1264
mcycles_vdp += MCYCLES_PER_LINE;
1265
}
1266
while (++line < bitmap.viewport.h);
1267
1268
/* end of active display */
1269
v_counter = line;
1270
1271
/* Mega Drive VDP specific */
1272
if (system_hw & SYSTEM_MD)
1273
{
1274
/* set VBLANK flag */
1275
status |= 0x08;
1276
}
1277
1278
/* overscan area */
1279
start = lines_per_frame - bitmap.viewport.y;
1280
end = bitmap.viewport.h + bitmap.viewport.y;
1281
1282
/* check viewport changes */
1283
if ((bitmap.viewport.w != bitmap.viewport.ow) || (bitmap.viewport.h != bitmap.viewport.oh))
1284
{
1285
bitmap.viewport.ow = bitmap.viewport.w;
1286
bitmap.viewport.oh = bitmap.viewport.h;
1287
bitmap.viewport.changed |= 1;
1288
}
1289
1290
/* update 6-Buttons & Lightguns */
1291
input_refresh();
1292
1293
/* H Interrupt */
1294
if(--h_counter < 0)
1295
{
1296
/* reload H Counter */
1297
h_counter = reg[10];
1298
1299
/* interrupt level 4 */
1300
hint_pending = 0x10;
1301
if (reg[0] & 0x10)
1302
{
1303
/* cycle-accurate HINT */
1304
if ((Z80.cycles % MCYCLES_PER_LINE) == 0)
1305
{
1306
z80_run(Z80.cycles + 1);
1307
}
1308
1309
Z80.irq_state = ASSERT_LINE;
1310
}
1311
}
1312
1313
/* update VDP DMA (Mega Drive VDP specific) */
1314
if (dma_length)
1315
{
1316
vdp_dma_update(mcycles_vdp);
1317
}
1318
1319
/* render overscan */
1320
if (line < end)
1321
{
1322
blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);
1323
}
1324
1325
/* update inputs before VINT */
1326
osd_input_update();
1327
1328
/* run Z80 until end of line */
1329
z80_run(mcycles_vdp + MCYCLES_PER_LINE);
1330
1331
/* make sure VINT flag was not cleared by last instruction */
1332
if (v_counter == line)
1333
{
1334
/* Set VINT flag */
1335
status |= 0x80;
1336
1337
/* V Interrupt */
1338
vint_pending = 0x20;
1339
if (reg[1] & 0x20)
1340
{
1341
Z80.irq_state = ASSERT_LINE;
1342
}
1343
}
1344
1345
/* update line cycle count */
1346
mcycles_vdp += MCYCLES_PER_LINE;
1347
1348
/* increment line count */
1349
line++;
1350
1351
/* Vertical Blanking */
1352
do
1353
{
1354
/* update V Counter */
1355
v_counter = line;
1356
1357
/* update 6-Buttons & Lightguns */
1358
input_refresh();
1359
1360
/* Master System & Game Gear VDP specific */
1361
if ((system_hw < SYSTEM_MD) && (line > (lines_per_frame - 16)))
1362
{
1363
/* Sprites are still processed during top border */
1364
render_obj((line - lines_per_frame) & 1);
1365
parse_satb(line - lines_per_frame);
1366
}
1367
1368
/* render overscan */
1369
if ((line < end) || (line >= start))
1370
{
1371
blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x);
1372
}
1373
1374
/* run Z80 */
1375
z80_run(mcycles_vdp + MCYCLES_PER_LINE);
1376
1377
/* update line cycle count */
1378
mcycles_vdp += MCYCLES_PER_LINE;
1379
}
1380
while (++line < (lines_per_frame - 1));
1381
1382
/* adjust Z80 cycle count for next frame */
1383
Z80.cycles -= mcycles_vdp;
1384
}
1385
1386