Previous 199869 Revisions Next

r29510 Thursday 10th April, 2014 at 11:57:10 UTC by Fabio Priuli
Converted SNES PPU to be a device. [Fabio Priuli]
[src/emu/video]snes_ppu.c* snes_ppu.h* video.mak
[src/mame]mame.mak
[src/mame/drivers]nss.c sfcbox.c snesb.c
[src/mame/includes]snes.h
[src/mame/machine]snes.c
[src/mame/video]snes.c
[src/mess]mess.mak
[src/mess/drivers]snes.c

trunk/src/mame/machine/snes.c
r29509r29510
2323#define __MACHINE_SNES_C
2424
2525#include "emu.h"
26#include "cpu/superfx/superfx.h"
27#include "cpu/g65816/g65816.h"
28#include "cpu/upd7725/upd7725.h"
2926#include "includes/snes.h"
30#include "audio/snes_snd.h"
3127
3228
3329#define DMA_REG(a) m_dma_regs[a - 0x4300]   // regs 0x4300-0x437f
3430
35void snes_state::video_start()
36{
37   m_ppu.ppu_start(m_screen,this);
38}
39
4031UINT32 snes_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
4132{
4233   /* NTSC SNES draw range is 1-225. */
4334   for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
44      m_ppu.refresh_scanline(machine(), bitmap, y + 1);
35      m_ppu->refresh_scanline(machine(), bitmap, y + 1);
4536
4637   return 0;
4738}
r29509r29510
9687{
9788   // latch the counters and pull IRQ
9889   // (don't need to switch to the 65816 context, we don't do anything dependant on it)
99   m_ppu.latch_counters(machine());
90   m_ppu->latch_counters(machine());
10091   SNES_CPU_REG(TIMEUP) = 0x80;    /* Indicate that irq occurred */
10192   m_maincpu->set_input_line(G65816_LINE_IRQ, ASSERT_LINE);
10293
r29509r29510
114105   // make sure we're in the 65816's context since we're messing with the OAM and stuff
115106   address_space &space = m_maincpu->space(AS_PROGRAM);
116107
117   if (!(m_ppu.m_screen_disabled)) //Reset OAM address, byuu says it happens at H=10
108   if (!(m_ppu->m_screen_disabled)) //Reset OAM address, byuu says it happens at H=10
118109   {
119      space.write_byte(OAMADDL, m_ppu.m_oam.saved_address_low); /* Reset oam address */
120      space.write_byte(OAMADDH, m_ppu.m_oam.saved_address_high);
121      m_ppu.m_oam.first_sprite = m_ppu.m_oam.priority_rotation ? (m_ppu.m_oam.address >> 1) & 127 : 0;
110      space.write_byte(OAMADDL, m_ppu->m_oam.saved_address_low); /* Reset oam address */
111      space.write_byte(OAMADDH, m_ppu->m_oam.saved_address_high);
112      m_ppu->m_oam.first_sprite = m_ppu->m_oam.priority_rotation ? (m_ppu->m_oam.address >> 1) & 127 : 0;
122113   }
123114}
124115
r29509r29510
139130TIMER_CALLBACK_MEMBER(snes_state::snes_scanline_tick)
140131{
141132   /* Increase current line - we want to latch on this line during it, not after it */
142   m_ppu.m_beam.current_vert = m_screen->vpos();
133   m_ppu->m_beam.current_vert = m_screen->vpos();
143134
144135   // not in hblank
145136   SNES_CPU_REG(HVBJOY) &= ~0x40;
r29509r29510
147138   /* Vertical IRQ timer - only if horizontal isn't also enabled! */
148139   if ((SNES_CPU_REG(NMITIMEN) & 0x20) && !(SNES_CPU_REG(NMITIMEN) & 0x10))
149140   {
150      if (m_ppu.m_beam.current_vert == m_vtime)
141      if (m_ppu->m_beam.current_vert == m_vtime)
151142      {
152143         SNES_CPU_REG(TIMEUP) = 0x80;    /* Indicate that irq occurred */
153144         // IRQ latches the counters, do it now
154         m_ppu.latch_counters(machine());
145         m_ppu->latch_counters(machine());
155146         m_maincpu->set_input_line(G65816_LINE_IRQ, ASSERT_LINE );
156147      }
157148   }
r29509r29510
164155      // is the HIRQ on a specific scanline?
165156      if (SNES_CPU_REG(NMITIMEN) & 0x20)
166157      {
167         if (m_ppu.m_beam.current_vert != m_vtime)
158         if (m_ppu->m_beam.current_vert != m_vtime)
168159         {
169160            setirq = 0;
170161         }
r29509r29510
172163
173164      if (setirq)
174165      {
175//          printf("HIRQ @ %d, %d\n", pixel * m_ppu.m_htmult, m_ppu.m_beam.current_vert);
166//          printf("HIRQ @ %d, %d\n", pixel * m_ppu->m_htmult, m_ppu->m_beam.current_vert);
176167         if (pixel == 0)
177168         {
178169            hirq_tick();
179170         }
180171         else
181172         {
182            m_hirq_timer->adjust(m_screen->time_until_pos(m_ppu.m_beam.current_vert, pixel * m_ppu.m_htmult));
173            m_hirq_timer->adjust(m_screen->time_until_pos(m_ppu->m_beam.current_vert, pixel * m_ppu->m_htmult));
183174         }
184175      }
185176   }
186177
187178   /* Start of VBlank */
188   if (m_ppu.m_beam.current_vert == m_ppu.m_beam.last_visible_line)
179   if (m_ppu->m_beam.current_vert == m_ppu->m_beam.last_visible_line)
189180   {
190      timer_set(m_screen->time_until_pos(m_ppu.m_beam.current_vert, 10), TIMER_RESET_OAM_ADDRESS);
181      timer_set(m_screen->time_until_pos(m_ppu->m_beam.current_vert, 10), TIMER_RESET_OAM_ADDRESS);
191182
192183      SNES_CPU_REG(HVBJOY) |= 0x81;       /* Set vblank bit to on & indicate controllers being read */
193184      SNES_CPU_REG(RDNMI) |= 0x80;        /* Set NMI occurred bit */
r29509r29510
199190      }
200191
201192      /* three lines after start of vblank we update the controllers (value from snes9x) */
202      m_io_timer->adjust(m_screen->time_until_pos(m_ppu.m_beam.current_vert + 2, m_hblank_offset * m_ppu.m_htmult));
193      m_io_timer->adjust(m_screen->time_until_pos(m_ppu->m_beam.current_vert + 2, m_hblank_offset * m_ppu->m_htmult));
203194   }
204195
205196   // hdma reset happens at scanline 0, H=~6
206   if (m_ppu.m_beam.current_vert == 0)
197   if (m_ppu->m_beam.current_vert == 0)
207198   {
208199      address_space &cpu0space = m_maincpu->space(AS_PROGRAM);
209200      hdma_init(cpu0space);
210201   }
211202
212   if (m_ppu.m_beam.current_vert == 0)
203   if (m_ppu->m_beam.current_vert == 0)
213204   {   /* VBlank is over, time for a new frame */
214205      SNES_CPU_REG(HVBJOY) &= 0x7f;       /* Clear vblank bit */
215206      SNES_CPU_REG(RDNMI)  &= 0x7f;       /* Clear nmi occurred bit */
216      m_ppu.m_stat78 ^= 0x80;       /* Toggle field flag */
217      m_ppu.m_stat77 &= 0x3f;  /* Clear Time Over and Range Over bits */
207      m_ppu->m_stat78 ^= 0x80;       /* Toggle field flag */
208      m_ppu->m_stat77 &= 0x3f;  /* Clear Time Over and Range Over bits */
218209
219210      m_maincpu->set_input_line(G65816_LINE_NMI, CLEAR_LINE );
220211   }
221212
222213   m_scanline_timer->adjust(attotime::never);
223   m_hblank_timer->adjust(m_screen->time_until_pos(m_ppu.m_beam.current_vert, m_hblank_offset * m_ppu.m_htmult));
214   m_hblank_timer->adjust(m_screen->time_until_pos(m_ppu->m_beam.current_vert, m_hblank_offset * m_ppu->m_htmult));
224215
225//  printf("%02x %d\n",SNES_CPU_REG(HVBJOY),m_ppu.m_beam.current_vert);
216//  printf("%02x %d\n",SNES_CPU_REG(HVBJOY),m_ppu->m_beam.current_vert);
226217}
227218
228219/* This is called at the start of hblank *before* the scanline indicated in current_vert! */
r29509r29510
231222   address_space &cpu0space = m_maincpu->space(AS_PROGRAM);
232223   int nextscan;
233224
234   m_ppu.m_beam.current_vert = m_screen->vpos();
225   m_ppu->m_beam.current_vert = m_screen->vpos();
235226
236227   /* make sure we halt */
237228   m_hblank_timer->adjust(attotime::never);
238229
239230   /* draw a scanline */
240   if (m_ppu.m_beam.current_vert <= m_ppu.m_beam.last_visible_line)
231   if (m_ppu->m_beam.current_vert <= m_ppu->m_beam.last_visible_line)
241232   {
242233      if (m_screen->vpos() > 0)
243234      {
r29509r29510
245236         if (SNES_CPU_REG(HDMAEN))
246237            hdma(cpu0space);
247238
248         m_screen->update_partial((m_ppu.m_interlace == 2) ? (m_ppu.m_beam.current_vert * m_ppu.m_interlace) : m_ppu.m_beam.current_vert - 1);
239         m_screen->update_partial((m_ppu->m_interlace == 2) ? (m_ppu->m_beam.current_vert * m_ppu->m_interlace) : m_ppu->m_beam.current_vert - 1);
249240      }
250241   }
251242
r29509r29510
253244   SNES_CPU_REG(HVBJOY) |= 0x40;
254245
255246   /* kick off the start of scanline timer */
256   nextscan = m_ppu.m_beam.current_vert + 1;
257   if (nextscan >= (((m_ppu.m_stat78 & 0x10) == SNES_NTSC) ? SNES_VTOTAL_NTSC : SNES_VTOTAL_PAL))
247   nextscan = m_ppu->m_beam.current_vert + 1;
248   if (nextscan >= (((m_ppu->m_stat78 & 0x10) == SNES_NTSC) ? SNES_VTOTAL_NTSC : SNES_VTOTAL_PAL))
258249   {
259250      nextscan = 0;
260251   }
r29509r29510
402393   // PPU accesses are from 2100 to 213f
403394   if (offset >= INIDISP && offset < APU00)
404395   {
405      return m_ppu.read(space, offset, SNES_CPU_REG(WRIO) & 0x80);
396      return m_ppu->read(space, offset, SNES_CPU_REG(WRIO) & 0x80);
406397   }
407398
408399   // APU is mirrored from 2140 to 217f
r29509r29510
490481   // PPU accesses are from 2100 to 213f
491482   if (offset >= INIDISP && offset < APU00)
492483   {
493      m_ppu.write(space, offset, data);
484      m_ppu->write(space, offset, data);
494485      return;
495486   }
496487
r29509r29510
548539         if (!(SNES_CPU_REG(WRIO) & 0x80) && (data & 0x80))
549540         {
550541            // external latch
551            m_ppu.latch_counters(space.machine());
542            m_ppu->latch_counters(space.machine());
552543         }
553544         SNES_CPU_REG(WRIO) = data;
554545         return;
r29509r29510
574565         return;
575566      case HDMAEN:    /* HDMA channel designation */
576567         if (data) //if a HDMA is enabled, data is inited at the next scanline
577            timer_set(m_screen->time_until_pos(m_ppu.m_beam.current_vert + 1), TIMER_RESET_HDMA);
568            timer_set(m_screen->time_until_pos(m_ppu->m_beam.current_vert + 1), TIMER_RESET_HDMA);
578569         SNES_CPU_REG(HDMAEN) = data;
579570         return;
580571      case TIMEUP:    // IRQ Flag is cleared on both read and write
r29509r29510
10421033   // SNES hcounter has a 0-339 range.  hblank starts at counter 260.
10431034   // clayfighter sets an HIRQ at 260, apparently it wants it to be before hdma kicks off, so we'll delay 2 pixels.
10441035   m_hblank_offset = 274;
1045   m_hblank_timer->adjust(m_screen->time_until_pos(((m_ppu.m_stat78 & 0x10) == SNES_NTSC) ? SNES_VTOTAL_NTSC - 1 : SNES_VTOTAL_PAL - 1, m_hblank_offset));
1036   m_hblank_timer->adjust(m_screen->time_until_pos(((m_ppu->m_stat78 & 0x10) == SNES_NTSC) ? SNES_VTOTAL_NTSC - 1 : SNES_VTOTAL_PAL - 1, m_hblank_offset));
10461037}
10471038
10481039void snes_state::snes_init_ram()
r29509r29510
10691060
10701061   // init frame counter so first line is 0
10711062   if (ATTOSECONDS_TO_HZ(m_screen->frame_period().attoseconds) >= 59)
1072      m_ppu.m_beam.current_vert = SNES_VTOTAL_NTSC;
1063      m_ppu->m_beam.current_vert = SNES_VTOTAL_NTSC;
10731064   else
1074      m_ppu.m_beam.current_vert = SNES_VTOTAL_PAL;
1065      m_ppu->m_beam.current_vert = SNES_VTOTAL_PAL;
10751066}
10761067
10771068void snes_state::machine_start()
r29509r29510
11351126
11361127   /* Set STAT78 to NTSC or PAL */
11371128   if (ATTOSECONDS_TO_HZ(m_screen->frame_period().attoseconds) >= 59.0f)
1138      m_ppu.m_stat78 = SNES_NTSC;
1129      m_ppu->m_stat78 = SNES_NTSC;
11391130   else /* if (ATTOSECONDS_TO_HZ(m_screen->frame_period().attoseconds) == 50.0f) */
1140      m_ppu.m_stat78 = SNES_PAL;
1131      m_ppu->m_stat78 = SNES_PAL;
11411132
11421133   // reset does this to these registers
11431134   SNES_CPU_REG(NMITIMEN) = 0;
11441135   m_htime = 0x1ff;
11451136   m_vtime = 0x1ff;
11461137
1147   m_ppu.m_htmult = 1;
1148   m_ppu.m_interlace = 1;
1149   m_ppu.m_obj_interlace = 1;
1138   m_ppu->m_htmult = 1;
1139   m_ppu->m_interlace = 1;
1140   m_ppu->m_obj_interlace = 1;
11501141}
11511142
11521143
trunk/src/mame/includes/snes.h
r29509r29510
33
44#include "cpu/spc700/spc700.h"
55#include "cpu/g65816/g65816.h"
6#include "cpu/upd7725/upd7725.h"
76#include "audio/snes_snd.h"
7#include "video/snes_ppu.h"
88
99/*
1010    SNES timing theory:
r29509r29510
1919    This is because 2 dots are "long" dots that last 6 MC ticks, resulting in 1 extra dot per line.
2020*/
2121
22#define MCLK_NTSC   (21477272)  /* verified */
23#define MCLK_PAL    (21218370)  /* verified */
24
25#define DOTCLK_NTSC (MCLK_NTSC/4)
26#define DOTCLK_PAL  (MCLK_PAL/4)
27
28#define SNES_LAYER_DEBUG  0
29
30/* Debug definitions */
31#ifdef MAME_DEBUG
32/* #define SNES_DBG_GENERAL*/       /* Display general debug info */
33/* #define SNES_DBG_VIDEO */        /* Display video debug info */
34/* #define SNES_DBG_DMA*/           /* Display DMA debug info */
35/* #define SNES_DBG_HDMA*/          /* Display HDMA debug info */
36/* #define SNES_DBG_REG_R*/         /* Display register read info */
37/* #define SNES_DBG_REG_W*/         /* Display register write info */
38#endif /* MAME_DEBUG */
39
40/* Useful definitions */
41#define SNES_SCR_WIDTH        256       /* 32 characters 8 pixels wide */
42#define SNES_SCR_HEIGHT_NTSC  225       /* Can be 224 or 240 height */
43#define SNES_SCR_HEIGHT_PAL   240       /* ??? */
44#define SNES_VTOTAL_NTSC      262       /* Maximum number of lines for NTSC systems */
45#define SNES_VTOTAL_PAL       312       /* Maximum number of lines for PAL systems */
46#define SNES_HTOTAL           341       /* Maximum number pixels per line (incl. hblank) */
22// Useful definitions
4723#define SNES_DMA_BASE         0x4300    /* Base DMA register address */
4824#define SNES_MODE_20          0x01      /* Lo-ROM cart */
4925#define SNES_MODE_21          0x02      /* Hi-ROM cart */
r29509r29510
5329#define SNES_MODE_BSLO        0x20
5430#define SNES_MODE_BSHI        0x40
5531#define SNES_MODE_ST          0x80
56#define SNES_NTSC             0x00
57#define SNES_PAL              0x10
58#define SNES_VRAM_SIZE        0x20000   /* 128kb of video ram */
59#define SNES_CGRAM_SIZE       0x202     /* 256 16-bit colours + 1 tacked on 16-bit colour for fixed colour */
60#define SNES_OAM_SIZE         0x440     /* 1088 bytes of Object Attribute Memory */
6132#define SNES_EXROM_START      0x1000000
62#define FIXED_COLOUR          256       /* Position in cgram for fixed colour */
63/* Definitions for PPU Memory-Mapped registers */
33
34// some PPU registers we still use in machine/snes.c
6435#define INIDISP        0x2100
65#define OBSEL          0x2101
6636#define OAMADDL        0x2102
6737#define OAMADDH        0x2103
68#define OAMDATA        0x2104
69#define BGMODE         0x2105   /* abcdefff = abcd: bg4-1 tile size | e: BG3 high priority | f: mode */
70#define MOSAIC         0x2106   /* xxxxabcd = x: pixel size | abcd: affects bg 1-4 */
71#define BG1SC          0x2107
72#define BG2SC          0x2108
73#define BG3SC          0x2109
74#define BG4SC          0x210A
75#define BG12NBA        0x210B
76#define BG34NBA        0x210C
77#define BG1HOFS        0x210D
78#define BG1VOFS        0x210E
79#define BG2HOFS        0x210F
80#define BG2VOFS        0x2110
81#define BG3HOFS        0x2111
82#define BG3VOFS        0x2112
83#define BG4HOFS        0x2113
84#define BG4VOFS        0x2114
85#define VMAIN          0x2115   /* i---ffrr = i: Increment timing | f: Full graphic | r: increment rate */
86#define VMADDL         0x2116   /* aaaaaaaa = a: LSB of vram address */
87#define VMADDH         0x2117   /* aaaaaaaa = a: MSB of vram address */
88#define VMDATAL        0x2118   /* dddddddd = d: data to be written */
89#define VMDATAH        0x2119   /* dddddddd = d: data to be written */
90#define M7SEL          0x211A   /* ab----yx = a: screen over | y: vertical flip | x: horizontal flip */
91#define M7A            0x211B   /* aaaaaaaa = a: COSINE rotate angle / X expansion */
92#define M7B            0x211C   /* aaaaaaaa = a: SINE rotate angle / X expansion */
93#define M7C            0x211D   /* aaaaaaaa = a: SINE rotate angle / Y expansion */
94#define M7D            0x211E   /* aaaaaaaa = a: COSINE rotate angle / Y expansion */
95#define M7X            0x211F
96#define M7Y            0x2120
97#define CGADD          0x2121
98#define CGDATA         0x2122
99#define W12SEL         0x2123
100#define W34SEL         0x2124
101#define WOBJSEL        0x2125
102#define WH0            0x2126   /* pppppppp = p: Left position of window 1 */
103#define WH1            0x2127   /* pppppppp = p: Right position of window 1 */
104#define WH2            0x2128   /* pppppppp = p: Left position of window 2 */
105#define WH3            0x2129   /* pppppppp = p: Right position of window 2 */
106#define WBGLOG         0x212A   /* aabbccdd = a: BG4 params | b: BG3 params | c: BG2 params | d: BG1 params */
107#define WOBJLOG        0x212B   /* ----ccoo = c: Colour window params | o: Object window params */
108#define TM             0x212C
109#define TS             0x212D
110#define TMW            0x212E
111#define TSW            0x212F
112#define CGWSEL         0x2130
113#define CGADSUB        0x2131
114#define COLDATA        0x2132
11538#define SETINI         0x2133
11639#define MPYL           0x2134
11740#define MPYM           0x2135
11841#define MPYH           0x2136
119#define SLHV           0x2137
120#define ROAMDATA       0x2138
121#define RVMDATAL       0x2139
122#define RVMDATAH       0x213A
123#define RCGDATA        0x213B
124#define OPHCT          0x213C
125#define OPVCT          0x213D
126#define STAT77         0x213E
127#define STAT78         0x213F
42
12843#define APU00          0x2140
12944#define APU01          0x2141
13045#define APU02          0x2142
13146#define APU03          0x2143
47
13248#define WMDATA         0x2180
13349#define WMADDL         0x2181
13450#define WMADDM         0x2182
13551#define WMADDH         0x2183
136/* Definitions for CPU Memory-Mapped registers */
52
53// Definitions for CPU Memory-Mapped registers
13754#define OLDJOY1        0x4016
13855#define OLDJOY2        0x4017
13956#define NMITIMEN       0x4200
r29509r29510
363280
364281#define SNES_CPU_REG(a) m_cpu_regs[a - 0x4200]  // regs 0x4200-0x421f
365282
366/* (PPU) Video related */
367
368struct SNES_SCANLINE
369{
370   int enable, clip;
371
372   UINT16 buffer[SNES_SCR_WIDTH];
373   UINT8  priority[SNES_SCR_WIDTH];
374   UINT8  layer[SNES_SCR_WIDTH];
375   UINT8  blend_exception[SNES_SCR_WIDTH];
376};
377
378class snes_state;
379
380class snes_ppu_class  /* once all the regs are saved in this structure, it would be better to reorganize it a bit... */
381{
382public:
383   UINT8 m_regs[0x40];
384
385   SNES_SCANLINE m_scanlines[2];
386
387   struct
388   {
389      /* clipmasks */
390      UINT8 window1_enabled, window1_invert;
391      UINT8 window2_enabled, window2_invert;
392      UINT8 wlog_mask;
393      /* color math enabled */
394      UINT8 color_math;
395
396      UINT8 charmap;
397      UINT8 tilemap;
398      UINT8 tilemap_size;
399
400      UINT8 tile_size;
401      UINT8 mosaic_enabled;   // actually used only for layers 0->3!
402
403      UINT8 main_window_enabled;
404      UINT8 sub_window_enabled;
405      UINT8 main_bg_enabled;
406      UINT8 sub_bg_enabled;
407
408      UINT16 hoffs;
409      UINT16 voffs;
410   } m_layer[6]; // this is for the BG1 - BG2 - BG3 - BG4 - OBJ - color layers
411
412   struct
413   {
414      UINT8 address_low;
415      UINT8 address_high;
416      UINT8 saved_address_low;
417      UINT8 saved_address_high;
418      UINT16 address;
419      UINT16 priority_rotation;
420      UINT8 next_charmap;
421      UINT8 next_size;
422      UINT8 size;
423      UINT32 next_name_select;
424      UINT32 name_select;
425      UINT8 first_sprite;
426      UINT8 flip;
427      UINT16 write_latch;
428   } m_oam;
429
430   struct
431   {
432      UINT16 latch_horz;
433      UINT16 latch_vert;
434      UINT16 current_horz;
435      UINT16 current_vert;
436      UINT8 last_visible_line;
437      UINT8 interlace_count;
438   } m_beam;
439
440   struct
441   {
442      UINT8 repeat;
443      UINT8 hflip;
444      UINT8 vflip;
445      INT16 matrix_a;
446      INT16 matrix_b;
447      INT16 matrix_c;
448      INT16 matrix_d;
449      INT16 origin_x;
450      INT16 origin_y;
451      UINT16 hor_offset;
452      UINT16 ver_offset;
453      UINT8 extbg;
454   } m_mode7;
455
456   struct OAM
457   {
458      UINT16 tile;
459      INT16 x, y;
460      UINT8 size, vflip, hflip, priority_bits, pal;
461      int height, width;
462   };
463
464   struct OAM m_oam_spritelist[SNES_SCR_WIDTH / 2];
465
466   UINT8 m_oam_itemlist[32];
467
468   struct TILELIST {
469      INT16 x;
470      UINT16 priority, pal, tileaddr;
471      int hflip;
472   };
473
474   struct TILELIST m_oam_tilelist[34];
475
476   #if SNES_LAYER_DEBUG
477   struct DEBUGOPTS
478   {
479      UINT8 bg_disabled[5];
480      UINT8 mode_disabled[8];
481      UINT8 draw_subscreen;
482      UINT8 windows_disabled;
483      UINT8 mosaic_disabled;
484      UINT8 colormath_disabled;
485      UINT8 sprite_reversed;
486      UINT8 select_pri[5];
487   };
488   struct DEBUGOPTS m_debug_options;
489   #endif
490
491   screen_device *m_screen;
492
493   UINT8 m_mosaic_size;
494   UINT8 m_clip_to_black;
495   UINT8 m_prevent_color_math;
496   UINT8 m_sub_add_mode;
497   UINT8 m_bg3_priority_bit;
498   UINT8 m_direct_color;
499   UINT8 m_ppu_last_scroll;      /* as per Anomie's doc and Theme Park, all scroll regs shares (but mode 7 ones) the same
500                                 'previous' scroll value */
501   UINT8 m_mode7_last_scroll;    /* as per Anomie's doc mode 7 scroll regs use a different value, shared with mode 7 matrix! */
502
503   UINT8 m_ppu1_open_bus, m_ppu2_open_bus;
504   UINT8 m_ppu1_version, m_ppu2_version;
505   UINT8 m_window1_left, m_window1_right, m_window2_left, m_window2_right;
506
507   UINT16 m_mosaic_table[16][4096];
508   UINT8 m_clipmasks[6][SNES_SCR_WIDTH];
509   UINT8 m_update_windows;
510   UINT8 m_update_offsets;
511   UINT8 m_update_oam_list;
512   UINT8 m_mode;
513   UINT8 m_interlace; //doubles the visible resolution
514   UINT8 m_obj_interlace;
515   UINT8 m_screen_brightness;
516   UINT8 m_screen_disabled;
517   UINT8 m_pseudo_hires;
518   UINT8 m_color_modes;
519   UINT8 m_stat77;
520   UINT8 m_stat78;
521
522   UINT16                m_htmult;     /* in 512 wide, we run HTOTAL double and halve it on latching */
523   UINT16                m_cgram_address;  /* CGRAM address */
524   UINT8                 m_read_ophct;
525   UINT8                 m_read_opvct;
526   UINT16                m_vram_fgr_high;
527   UINT16                m_vram_fgr_increment;
528   UINT16                m_vram_fgr_count;
529   UINT16                m_vram_fgr_mask;
530   UINT16                m_vram_fgr_shift;
531   UINT16                m_vram_read_buffer;
532   UINT16                m_vmadd;
533
534   inline UINT16 get_bgcolor(UINT8 direct_colors, UINT16 palette, UINT8 color);
535   inline void set_scanline_pixel(int screen, INT16 x, UINT16 color, UINT8 priority, UINT8 layer, int blend);
536   inline void draw_bgtile_lores(UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority);
537   inline void draw_bgtile_hires(UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority);
538   inline void draw_oamtile(INT16 ii, UINT8 colour, UINT16 pal, UINT8 priority);
539   inline void draw_tile(UINT8 planes, UINT8 layer, UINT32 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT8 direct_colors, UINT16 pal, UINT8 hires);
540   inline UINT32 get_tmap_addr(UINT8 layer, UINT8 tile_size, UINT32 base, UINT32 x, UINT32 y);
541   inline void update_line(UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a, UINT8 color_depth, UINT8 hires, UINT8 offset_per_tile, UINT8 direct_colors);
542   void update_line_mode7(UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a);
543   void update_obsel(void);
544   void oam_list_build(void);
545   int is_sprite_on_scanline(UINT16 curline, UINT8 sprite);
546   void update_objects_rto(UINT16 curline);
547   void update_objects(UINT8 priority_oam0, UINT8 priority_oam1, UINT8 priority_oam2, UINT8 priority_oam3);
548   void update_mode_0(UINT16 curline);
549   void update_mode_1(UINT16 curline);
550   void update_mode_2(UINT16 curline);
551   void update_mode_3(UINT16 curline);
552   void update_mode_4(UINT16 curline);
553   void update_mode_5(UINT16 curline);
554   void update_mode_6(UINT16 curline);
555   void update_mode_7(UINT16 curline);
556   void draw_screens(UINT16 curline);
557   void update_windowmasks(void);
558   void update_offsets(void);
559   inline void draw_blend(UINT16 offset, UINT16 *colour, UINT8 prevent_color_math, UINT8 black_pen_clip, int switch_screens);
560   void refresh_scanline(running_machine &machine, bitmap_rgb32 &bitmap, UINT16 curline);
561
562   void latch_counters(running_machine &machine);
563   void dynamic_res_change(running_machine &machine);
564   inline UINT32 get_vram_address(running_machine &machine);
565   UINT8 dbg_video(running_machine &machine, UINT16 curline);
566
567   void ppu_start(screen_device &screen,snes_state *state);
568   UINT8 read(address_space &space, UINT32 offset, UINT8 wrio_bit7);
569   void write(address_space &space, UINT32 offset, UINT8 data);
570
571   DECLARE_READ8_MEMBER( oam_read );
572   DECLARE_WRITE8_MEMBER( oam_write );
573   DECLARE_READ8_MEMBER( cgram_read );
574   DECLARE_WRITE8_MEMBER( cgram_write );
575   DECLARE_READ8_MEMBER( vram_read );
576   DECLARE_WRITE8_MEMBER( vram_write );
577   UINT16 *m_oam_ram;     /* Object Attribute Memory */
578   UINT16 *m_cgram;   /* Palette RAM */
579   UINT8  *m_vram;    /* Video RAM (TODO: Should be 16-bit, but it's easier this way) */
580
581   snes_state *m_state;
582};
583
584283struct snes_cart_info
585284{
586285   UINT8 *m_rom;
r29509r29510
613312      m_maincpu(*this, "maincpu"),
614313      m_soundcpu(*this, "soundcpu"),
615314      m_spc700(*this, "spc700"),
616      m_superfx(*this, "superfx"),
315      m_ppu(*this, "ppu"),
617316      m_screen(*this, "screen") { }
618317
619318   /* misc */
r29509r29510
670369   snes_cart_info m_cart;   // used by NSS/SFCBox only! to be moved in a derived class!
671370   void rom_map_setup(UINT32 size);
672371
673   snes_ppu_class        m_ppu;
674372   UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
675373
676374   /* devices */
677375   required_device<_5a22_device> m_maincpu;
678376   required_device<spc700_device> m_soundcpu;
679377   required_device<snes_sound_device> m_spc700;
680   optional_device<cpu_device> m_superfx;
378   required_device<snes_ppu_device> m_ppu;
681379   required_device<screen_device> m_screen;
682380
683381
r29509r29510
728426   DECLARE_WRITE_LINE_MEMBER(snes_extern_irq_w);
729427   DECLARE_DEVICE_IMAGE_LOAD_MEMBER(snes_cart);
730428   DECLARE_DEVICE_IMAGE_LOAD_MEMBER(sufami_cart);
731   virtual void video_start();
732429   void snes_init_timers();
733430   virtual void machine_start();
734431   virtual void machine_reset();
r29509r29510
760457   HAS_UNK
761458};
762459
763/* offset-per-tile modes */
764enum
765{
766   SNES_OPT_NONE = 0,
767   SNES_OPT_MODE2,
768   SNES_OPT_MODE4,
769   SNES_OPT_MODE6
770};
771
772/* layers */
773enum
774{
775   SNES_BG1 = 0,
776   SNES_BG2,
777   SNES_BG3,
778   SNES_BG4,
779   SNES_OAM,
780   SNES_COLOR
781};
782
783460#endif /* _SNES_H_ */
trunk/src/mame/mame.mak
r29509r29510
309309VIDEOS += RAMDAC
310310VIDEOS += SAA5050
311311#VIDEOS += SED1330
312VIDEOS += SNES_PPU
312313VIDEOS += STVVDP
313314VIDEOS += TLC34076
314315VIDEOS += TMS34061
r29509r29510
14261427   $(DRIVERS)/mario.o $(AUDIO)/mario.o $(VIDEO)/mario.o \
14271428   $(DRIVERS)/multigam.o \
14281429   $(DRIVERS)/n8080.o $(AUDIO)/n8080.o $(VIDEO)/n8080.o \
1429   $(DRIVERS)/nss.o $(MACHINE)/snes.o $(AUDIO)/snes_snd.o $(VIDEO)/snes.o \
1430   $(DRIVERS)/nss.o $(MACHINE)/snes.o $(AUDIO)/snes_snd.o \
14301431   $(DRIVERS)/playch10.o $(MACHINE)/playch10.o $(VIDEO)/playch10.o \
14311432   $(DRIVERS)/popeye.o $(VIDEO)/popeye.o \
14321433   $(DRIVERS)/punchout.o $(VIDEO)/punchout.o \
trunk/src/mame/video/snes.c
r29509r29510
1/***************************************************************************
2
3  snes.c
4
5  Video file to handle emulation of the Nintendo Super NES.
6
7  Anthony Kruize
8  Based on the original code by Lee Hammerton (aka Savoury Snax)
9
10  Some notes on the snes video hardware:
11
12  Object Attribute Memory(OAM) is made up of 128 blocks of 32 bits, followed
13  by 128 blocks of 2 bits. The format for each block is:
14  -First Block----------------------------------------------------------------
15  | x pos  | y pos  |char no.| v flip | h flip |priority|palette |char no msb|
16  +--------+--------+--------+--------+--------+--------+--------+-----------+
17  | 8 bits | 8 bits | 8 bits | 1 bit  | 1 bit  | 2 bits | 3 bits | 1 bit     |
18  -Second Block---------------------------------------------------------------
19  | size  | x pos msb |
20  +-------+-----------+
21  | 1 bit | 1 bit     |
22  ---------------------
23
24  Video RAM contains information for character data and screen maps.
25  Screen maps are made up of 32 x 32 blocks of 16 bits each.
26  The format for each block is:
27  ----------------------------------------------
28  | v flip | x flip |priority|palette |char no.|
29  +--------+--------+--------+--------+--------+
30  | 1 bit  | 1 bit  | 1 bit  | 3 bits |10 bits |
31  ----------------------------------------------
32  Mode 7 is stored differently. Character data and screen map are interleaved.
33  There are two formats:
34  -Normal-----------------  -EXTBG-----------------------------
35  | char data | char no. |  | priority | char data | char no. |
36  +-----------+----------+  +----------+-----------+----------+
37  | 8 bits    | 8 bits   |  | 1 bit    | 7 bits    | 8 bits   |
38  ------------------------  -----------------------------------
39
40  The screen layers are drawn with the following priorities (updated info courtesy of byuu):
41
42  |           |   1   |   2   |   3   |   4   |   5   |   6   |   7   |   8   |   9   |  10   |  11   |  12   |
43  -------------------------------------------------------------------------------------------------------------
44  | Mode 0    |  BG4B |  BG3B |  OAM0 |  BG4A |  BG3A |  OAM1 |  BG2B |  BG1B |  OAM2 |  BG2A |  BG1A |  OAM3 |
45  -------------------------------------------------------------------------------------------------------------
46  | Mode 1 (*)|  BG3B |  OAM0 |  OAM1 |  BG2B |  BG1B |  OAM2 |  BG2A |  BG1A |  OAM3 |  BG3A |       |       |
47  -------------------------------------------------------------------------------------------------------------
48  | Mode 1 (!)|  BG3B |  OAM0 |  BG3A |  OAM1 |  BG2B |  BG1B |  OAM2 |  BG2A |  BG1A |  OAM3 |       |       |
49  -------------------------------------------------------------------------------------------------------------
50  | Mode 2    |  BG2B |  OAM0 |  BG1B |  OAM1 |  BG2A |  OAM2 |  BG1A |  OAM3 |       |       |       |       |
51  -------------------------------------------------------------------------------------------------------------
52  | Mode 3    |  BG2B |  OAM0 |  BG1B |  OAM1 |  BG2A |  OAM2 |  BG1A |  OAM3 |       |       |       |       |
53  -------------------------------------------------------------------------------------------------------------
54  | Mode 4    |  BG2B |  OAM0 |  BG1B |  OAM1 |  BG2A |  OAM2 |  BG1A |  OAM3 |       |       |       |       |
55  -------------------------------------------------------------------------------------------------------------
56  | Mode 5    |  BG2B |  OAM0 |  BG1B |  OAM1 |  BG2A |  OAM2 |  BG1A |  OAM3 |       |       |       |       |
57  -------------------------------------------------------------------------------------------------------------
58  | Mode 6    |  OAM0 |  BG1B |  OAM1 |  OAM2 |  BG1A |  OAM3 |       |       |       |       |       |       |
59  -------------------------------------------------------------------------------------------------------------
60  | Mode 7 (+)|  OAM0 |  BG1n |  OAM1 |  OAM2 |  OAM3 |       |       |       |       |       |       |       |
61  -------------------------------------------------------------------------------------------------------------
62  | Mode 7 (-)|  BG2B |  OAM0 |  BG1n |  OAM1 |  BG2A |  OAM2 |  OAM3 |       |       |       |       |       |
63  -------------------------------------------------------------------------------------------------------------
64
65  Where:
66   - Mode 1 (*) is Mode 1 with bg3_pty = 1
67   - Mode 1 (!) is Mode 1 with bg3_pty = 0
68   - Mode 7 (+) is base Mode 7
69   - Mode 7 (-) is Mode 7 EXTBG
70
71***************************************************************************/
72
73#include "emu.h"
74#include "includes/snes.h"
75
76#define SNES_MAINSCREEN    0
77#define SNES_SUBSCREEN     1
78#define SNES_CLIP_NEVER    0
79#define SNES_CLIP_IN       1
80#define SNES_CLIP_OUT      2
81#define SNES_CLIP_ALWAYS   3
82
83#if SNES_LAYER_DEBUG
84/*                                    red   green  blue    purple  yellow cyan    grey    white */
85static const UINT16 dbg_mode_colours[8] = { 0x1f, 0x3e0, 0x7c00, 0x7c1f, 0x3ff, 0x7fe0, 0x4210, 0x7fff };
86#endif /* SNES_LAYER_DEBUG */
87
88static const UINT16 table_obj_offset[8][8] =
89{
90   { (0*32),   (0*32)+32,   (0*32)+64,   (0*32)+96,   (0*32)+128,   (0*32)+160,   (0*32)+192,   (0*32)+224 },
91   { (16*32),  (16*32)+32,  (16*32)+64,  (16*32)+96,  (16*32)+128,  (16*32)+160,  (16*32)+192,  (16*32)+224 },
92   { (32*32),  (32*32)+32,  (32*32)+64,  (32*32)+96,  (32*32)+128,  (32*32)+160,  (32*32)+192,  (32*32)+224 },
93   { (48*32),  (48*32)+32,  (48*32)+64,  (48*32)+96,  (48*32)+128,  (48*32)+160,  (48*32)+192,  (48*32)+224 },
94   { (64*32),  (64*32)+32,  (64*32)+64,  (64*32)+96,  (64*32)+128,  (64*32)+160,  (64*32)+192,  (64*32)+224 },
95   { (80*32),  (80*32)+32,  (80*32)+64,  (80*32)+96,  (80*32)+128,  (80*32)+160,  (80*32)+192,  (80*32)+224 },
96   { (96*32),  (96*32)+32,  (96*32)+64,  (96*32)+96,  (96*32)+128,  (96*32)+160,  (96*32)+192,  (96*32)+224 },
97   { (112*32), (112*32)+32, (112*32)+64, (112*32)+96, (112*32)+128, (112*32)+160, (112*32)+192, (112*32)+224 }
98};
99
100
101enum
102{
103   SNES_COLOR_DEPTH_2BPP = 0,
104   SNES_COLOR_DEPTH_4BPP,
105   SNES_COLOR_DEPTH_8BPP
106};
107
108
109#define PPU_REG(a) m_regs[a - 0x2100]
110
111
112/*****************************************
113 * get_bgcolor()
114 *
115 * Get the proper color (direct or from cgram)
116 *****************************************/
117
118inline UINT16 snes_ppu_class::get_bgcolor( UINT8 direct_colors, UINT16 palette, UINT8 color )
119{
120   UINT16 c = 0;
121
122   if (direct_colors)
123   {
124      /* format is  0 | BBb00 | GGGg0 | RRRr0, HW confirms that the data is zero padded. */
125      c = ((color & 0x07) << 2) | ((color & 0x38) << 4) | ((color & 0xc0) << 7);
126      c |= ((palette & 0x04) >> 1) | ((palette & 0x08) << 3) | ((palette & 0x10) << 8);
127   }
128   else
129      c = m_cgram[(palette + color) % FIXED_COLOUR];
130
131   return c;
132}
133
134/*****************************************
135 * set_scanline_pixel()
136 *
137 * Store pixel color, priority, layer and
138 * color math exception (for OAM) in the
139 * proper scanline
140 *****************************************/
141
142inline void snes_ppu_class::set_scanline_pixel( int screen, INT16 x, UINT16 color, UINT8 priority, UINT8 layer, int blend )
143{
144   m_scanlines[screen].buffer[x] = color;
145   m_scanlines[screen].priority[x] = priority;
146   m_scanlines[screen].layer[x] = layer;
147   m_scanlines[screen].blend_exception[x] = blend;
148}
149
150/*************************************************************************************************
151 * SNES tiles
152 *
153 * The way vram is accessed to draw tiles is basically the same for both BG and OAM tiles. Main
154 * differences are bit planes (variable for BG and fixed for OAM) and a few details of the scanline
155 * output (since OAM has neither mosaic, nor hires, nor direct colors).
156 * Hence, we use a common function to take data from VRAM and then we call specific routines for
157 * OAM vs BG vs Hi-Res BG tiles.
158 *************************************************************************************************/
159
160/*****************************************
161 * draw_bgtile_lores()
162 * draw_bgtile_hires()
163 * draw_oamtile_()
164 *
165 * Check if a pixel is clipped or not, and
166 * copy it to the scanline buffer when
167 * appropriate. The actual way to perform
168 * such operations depends on the source
169 * (BG or OAM) and on the resolution (hires
170 * or lores)
171 *****************************************/
172
173inline void snes_ppu_class::draw_bgtile_lores( UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority )
174{
175   int screen;
176   UINT16 c;
177
178   for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
179   {
180      if (ii >= 0 && ii < SNES_SCR_WIDTH && m_scanlines[screen].enable)
181      {
182         if (m_scanlines[screen].priority[ii] <= priority)
183         {
184            UINT8 clr = colour;
185            UINT8 clipmask = m_clipmasks[layer][ii];
186
187#if SNES_LAYER_DEBUG
188            if (m_debug_options.windows_disabled)
189               clipmask = 0xff;
190#endif /* SNES_LAYER_DEBUG */
191
192            /* Clip to windows */
193            if (m_scanlines[screen].clip)
194               clr &= clipmask;
195
196            /* Only draw if we have a colour (0 == transparent) */
197            if (clr)
198            {
199               c = get_bgcolor(direct_colors, pal, clr);
200               set_scanline_pixel(screen, ii, c, priority, layer, 0);
201            }
202         }
203      }
204   }
205}
206
207inline void snes_ppu_class::draw_bgtile_hires( UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority )
208{
209   int screen;
210   UINT16 c;
211
212   for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
213   {
214      // odd pixels to main screen, even pixels to sub screen
215      if (ii >= 0 && ii < (SNES_SCR_WIDTH << 1) && ((ii & 1) ^ screen) && m_scanlines[screen].enable)
216      {
217         if (m_scanlines[screen].priority[ii >> 1] <= priority)
218         {
219            UINT8 clr = colour;
220            UINT8 clipmask = m_clipmasks[layer][ii >> 1];
221
222#if SNES_LAYER_DEBUG
223            if (m_debug_options.windows_disabled)
224               clipmask = 0xff;
225#endif /* SNES_LAYER_DEBUG */
226
227            /* Clip to windows */
228            if (m_scanlines[screen].clip)
229               clr &= clipmask;
230
231            /* Only draw if we have a colour (0 == transparent) */
232            if (clr)
233            {
234               c = get_bgcolor(direct_colors, pal, clr);
235               set_scanline_pixel(screen, ii >> 1, c, priority, layer, 0);
236            }
237         }
238      }
239   }
240}
241
242inline void snes_ppu_class::draw_oamtile( INT16 ii, UINT8 colour, UINT16 pal, UINT8 priority )
243{
244   int screen;
245   int blend;
246   UINT16 c;
247   INT16 pos = ii & 0x1ff;
248
249   for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
250   {
251      if (pos >= 0 && pos < SNES_SCR_WIDTH && m_scanlines[screen].enable)
252      {
253         UINT8 clr = colour;
254         UINT8 clipmask = m_clipmasks[SNES_OAM][pos];
255
256#if SNES_LAYER_DEBUG
257         if (m_debug_options.windows_disabled)
258            clipmask = 0xff;
259#endif /* SNES_LAYER_DEBUG */
260
261         /* Clip to windows */
262         if (m_scanlines[screen].clip)
263            clr &= clipmask;
264
265         /* Only draw if we have a colour (0 == transparent) */
266         if (clr)
267         {
268            c = m_cgram[(pal + clr) % FIXED_COLOUR];
269            blend = (pal + clr < 192) ? 1 : 0;
270            set_scanline_pixel(screen, pos, c, priority, SNES_OAM, blend);
271         }
272      }
273   }
274}
275
276/*****************************************
277 * draw_tile()
278 *
279 * Draw 8 pixels from the expected tile
280 * by reading the color planes from vram
281 * and by calling the appropriate routine
282 * (depending on layer and resolution)
283 *****************************************/
284
285inline void snes_ppu_class::draw_tile( UINT8 planes, UINT8 layer, UINT32 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT8 direct_colors, UINT16 pal, UINT8 hires )
286{
287   UINT8 plane[8];
288   INT16 ii, jj;
289   int x_mos;
290
291   for (ii = 0; ii < planes / 2; ii++)
292   {
293      plane[2 * ii + 0] = m_vram[(tileaddr + 16 * ii + 0) % SNES_VRAM_SIZE];
294      plane[2 * ii + 1] = m_vram[(tileaddr + 16 * ii + 1) % SNES_VRAM_SIZE];
295   }
296
297   for (ii = x; ii < (x + 8); ii++)
298   {
299      UINT8 colour = 0;
300      UINT8 mosaic = m_layer[layer].mosaic_enabled;
301
302#if SNES_LAYER_DEBUG
303      if (m_debug_options.mosaic_disabled)
304         mosaic = 0;
305#endif /* SNES_LAYER_DEBUG */
306
307      if (flip)
308      {
309         for (jj = 0; jj < planes; jj++)
310            colour |= BIT(plane[jj], ii - x) ? (1 << jj) : 0;
311      }
312      else
313      {
314         for (jj = 0; jj < planes; jj++)
315            colour |= BIT(plane[jj], 7 - (ii - x)) ? (1 << jj) : 0;
316      }
317
318      if (layer == SNES_OAM)
319         draw_oamtile(ii, colour, pal, priority);
320      else if (!hires)
321      {
322         if (mosaic)
323         {
324            for (x_mos = 0; x_mos < (m_mosaic_size + 1); x_mos++)
325               draw_bgtile_lores(layer, ii + x_mos, colour, pal, direct_colors, priority);
326            ii += x_mos - 1;
327         }
328         else
329            draw_bgtile_lores(layer, ii, colour, pal, direct_colors, priority);
330      }
331      else /* hires */
332      {
333         if (mosaic)
334         {
335            for (x_mos = 0; x_mos < (m_mosaic_size + 1); x_mos++)
336               draw_bgtile_hires(layer, ii + x_mos, colour, pal, direct_colors, priority);
337            ii += x_mos - 1;
338         }
339         else
340            draw_bgtile_hires(layer, ii, colour, pal, direct_colors, priority);
341      }
342   }
343}
344
345/*************************************************************************************************
346 * SNES BG layers
347 *
348 * BG drawing theory of each scanline is quite easy: depending on the graphics Mode (0-7), there
349 * are up to 4 background layers. Pixels for each BG layer can have two different priorities.
350 * Depending on the line and on the BGHOFS and BGVOFS PPU registers, we first determine the tile
351 * address in m_vram (by determining x,y coord and tile size and by calling get_tmap_addr).
352 * Then, we load the correspondent data and we determine the tile properties: which priority to
353 * use, which palette etc. Finally, for each pixel of the tile appearing on screen, we check if
354 * the tile priority is higher than the BG/OAM already stored in that pixel for that line. If so
355 * we store the pixel in the buffer, otherwise we discard it.
356 *
357 * Of course, depending on the graphics Mode, it might be easier or harder to determine the proper
358 * tile address in vram (Mode 7 uses different registers, Mode 2, 4 and 6 uses OPT effect, etc.),
359 * but in general it works as described.
360 *************************************************************************************************/
361
362/*********************************************
363 * get_tmap_addr()
364 *
365 * Find the address in VRAM of the tile (x,y)
366 *********************************************/
367
368inline UINT32 snes_ppu_class::get_tmap_addr( UINT8 layer, UINT8 tile_size, UINT32 base, UINT32 x, UINT32 y )
369{
370   UINT32 res = base;
371   x  >>= (3 + tile_size);
372   y  >>= (3 + tile_size);
373
374   res += (m_layer[layer].tilemap_size & 2) ? ((y & 0x20) << ((m_layer[layer].tilemap_size & 1) ? 7 : 6)) : 0;
375   /* Scroll vertically */
376   res += (y & 0x1f) << 6;
377   /* Offset horizontally */
378   res += (m_layer[layer].tilemap_size & 1) ? ((x & 0x20) << 6) : 0;
379   /* Scroll horizontally */
380   res += (x & 0x1f) << 1;
381
382   return res;
383}
384
385/*********************************************
386 * update_line()
387 *
388 * Update an entire line of tiles.
389 *********************************************/
390
391inline void snes_ppu_class::update_line( UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a, UINT8 color_depth, UINT8 hires, UINT8 offset_per_tile, UINT8 direct_colors )
392{
393   UINT32 tmap, tile, xoff, yoff, charaddr, addr;
394   UINT16 ii = 0, vflip, hflip, pal, pal_direct, tilemap;
395   UINT8 xscroll, priority;
396   INT8 yscroll;
397   int tile_incr = 0;
398   UINT16 opt_bit = (layer == SNES_BG1) ? 13 : (layer == SNES_BG2) ? 14 : 0;
399   UINT8 tile_size;
400   /* variables depending on color_depth */
401   UINT8 color_planes = 2 << color_depth;
402   /* below we cheat to simplify the code: 8BPP should have 0 pal offset, not 0x100 (but we take care of this by later using pal % FIXED_COLOUR) */
403   UINT8 color_shift = 2 << color_depth;
404
405#if SNES_LAYER_DEBUG
406   if (m_debug_options.bg_disabled[layer])
407      return;
408#endif /* SNES_LAYER_DEBUG */
409
410   m_scanlines[SNES_MAINSCREEN].enable = m_layer[layer].main_bg_enabled;
411   m_scanlines[SNES_SUBSCREEN].enable = m_layer[layer].sub_bg_enabled;
412   m_scanlines[SNES_MAINSCREEN].clip = m_layer[layer].main_window_enabled;
413   m_scanlines[SNES_SUBSCREEN].clip = m_layer[layer].sub_window_enabled;
414
415   if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
416      return;
417
418   /* Handle Mosaic effects */
419   if (m_layer[layer].mosaic_enabled)
420      curline -= (curline % (m_mosaic_size + 1));
421
422   if ((m_interlace == 2) && !hires && !m_pseudo_hires)
423      curline /= 2;
424
425   /* Find the size of the tiles (8x8 or 16x16) */
426   tile_size = m_layer[layer].tile_size;
427
428   /* Find scroll info */
429   xoff = m_layer[layer].hoffs;
430   yoff = m_layer[layer].voffs;
431
432   xscroll = xoff & ((1 << (3 + tile_size)) - 1);
433
434   /* Jump to base map address */
435   tmap = m_layer[layer].tilemap << 9;
436   charaddr = m_layer[layer].charmap << 13;
437
438   while (ii < 256 + (8 << tile_size))
439   {
440      // determine the horizontal position (Bishojo Janshi Suchi Pai & Desert Figther have tile_size & hires == 1)
441      UINT32 xpos = xoff + (ii << (tile_size * hires));
442      UINT32 ypos = yoff + curline;
443
444      if (offset_per_tile != SNES_OPT_NONE)
445      {
446         int opt_x = ii + (xoff & 7);
447         UINT32 haddr = 0, vaddr = 0;
448         UINT16 hval = 0, vval = 0;
449
450         if (opt_x >= 8)
451         {
452            switch (offset_per_tile)
453            {
454            case SNES_OPT_MODE2:
455            case SNES_OPT_MODE6:
456               haddr = get_tmap_addr(SNES_BG3, m_layer[SNES_BG3].tile_size, m_layer[SNES_BG3].tilemap << 9, (opt_x - 8) + ((m_layer[SNES_BG3].hoffs & 0x3ff) & ~7), (m_layer[SNES_BG3].voffs & 0x3ff));
457               vaddr = get_tmap_addr(SNES_BG3, m_layer[SNES_BG3].tile_size, m_layer[SNES_BG3].tilemap << 9, (opt_x - 8) + ((m_layer[SNES_BG3].hoffs & 0x3ff) & ~7), (m_layer[SNES_BG3].voffs & 0x3ff) + 8);
458               hval = m_vram[haddr % SNES_VRAM_SIZE] | (m_vram[(haddr + 1) % SNES_VRAM_SIZE] << 8);
459               vval = m_vram[vaddr % SNES_VRAM_SIZE] | (m_vram[(vaddr + 1) % SNES_VRAM_SIZE] << 8);
460               if (BIT(hval, opt_bit))
461                  xpos = opt_x + (hval & ~7);
462               if (BIT(vval, opt_bit))
463                  ypos = curline + vval;
464               break;
465            case SNES_OPT_MODE4:
466               haddr = get_tmap_addr(SNES_BG3, m_layer[SNES_BG3].tile_size, m_layer[SNES_BG3].tilemap << 9, (opt_x - 8) + ((m_layer[SNES_BG3].hoffs & 0x3ff) & ~7), (m_layer[SNES_BG3].voffs & 0x3ff));
467               hval = m_vram[haddr % SNES_VRAM_SIZE] | (m_vram[(haddr + 1) % SNES_VRAM_SIZE] << 8);
468               if (BIT(hval, opt_bit))
469               {
470                  if (!BIT(hval, 15))
471                     xpos = opt_x + (hval & ~7);
472                  else
473                     ypos = curline + hval;
474               }
475               break;
476            }
477         }
478      }
479
480      addr = get_tmap_addr(layer, tile_size, tmap, xpos, ypos);
481
482      /*
483      Tilemap format
484        vhopppcc cccccccc
485
486        v/h  = Vertical/Horizontal flip this tile.
487        o    = Tile priority.
488        ppp  = Tile palette. The number of entries in the palette depends on the Mode and the BG.
489        cccccccccc = Tile number.
490      */
491      tilemap = m_vram[addr % SNES_VRAM_SIZE] | (m_vram[(addr + 1) % SNES_VRAM_SIZE] << 8);
492      vflip = BIT(tilemap, 15);
493      hflip = BIT(tilemap, 14);
494      priority = BIT(tilemap, 13) ? priority_a : priority_b;
495      pal_direct = ((tilemap & 0x1c00) >> 8);
496      tile = tilemap & 0x03ff;
497
498      pal = ((pal_direct >> 2) << color_shift);
499
500      /* Mode 0 palettes are layer specific */
501      if (m_mode == 0)
502      {
503         pal += (layer << 5);
504      }
505
506#if SNES_LAYER_DEBUG
507      /* if we want to draw only one of the priorities of this layer */
508      if (((m_debug_options.select_pri[layer] & 0x01) && (priority == priority_a)) ||
509         ((m_debug_options.select_pri[layer] & 0x02) && (priority == priority_b)))
510      {
511         if (!hires && tile_size)
512            ii += 16;
513         else
514            ii += 8;
515         continue;
516      }
517#endif /* SNES_LAYER_DEBUG */
518
519      /* figure out which line to draw */
520      yscroll = ypos & ((8 << tile_size) - 1);
521
522      if (tile_size)
523         if (BIT(yscroll, 3) != vflip)
524            tile += 16;
525
526      if (yscroll > 7)
527         yscroll &= 7;
528
529      if (vflip)
530         yscroll = 7 - yscroll;
531
532      yscroll <<= 1;
533
534      /* if we have to draw 16 pixels, set tile_incr and adjust tile for horizontal flip */
535      if (tile_size || hires)
536      {
537         if (hflip)
538         {
539            tile += 1;
540            tile_incr = -1; // next 8 pixels from previous tile (because of hflip)
541         }
542         else
543            tile_incr = 1;  // next 8 pixels from next tile
544      }
545
546      if (hires)
547      {
548         /* draw 16 pixels (the routine will automatically send half of them to the mainscreen scanline and half to the subscreen one) */
549         draw_tile(color_planes, layer, charaddr + (((tile + 0)         & 0x3ff) * 8 * color_planes) + yscroll, (ii - xscroll) * 2,     priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
550         draw_tile(color_planes, layer, charaddr + (((tile + tile_incr) & 0x3ff) * 8 * color_planes) + yscroll, (ii - xscroll) * 2 + 8, priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
551         ii += 8;
552      }
553      else
554      {
555         draw_tile(color_planes, layer, charaddr + ((tile & 0x3ff) * 8 * color_planes) + yscroll, ii - xscroll, priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
556         ii += 8;
557
558         if (tile_size)
559         {
560            draw_tile(color_planes, layer, charaddr + (((tile + tile_incr) & 0x3ff) * 8 * color_planes) + yscroll, ii - xscroll, priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
561            ii += 8;
562         }
563      }
564   }
565}
566
567
568/*********************************************
569 * update_line_mode7()
570 *
571 * Update an entire line of mode7 tiles.
572 *********************************************/
573
574#define MODE7_CLIP(x) (((x) & 0x2000) ? ((x) | ~0x03ff) : ((x) & 0x03ff))
575
576void snes_ppu_class::update_line_mode7( UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a )
577{
578   UINT32 tiled;
579   INT16 ma, mb, mc, md;
580   INT32 xc, yc, tx, ty, sx, sy, hs, vs, xpos, xdir, x0, y0;
581   UINT8 priority = priority_b;
582   UINT8 colour = 0;
583   UINT16 *mosaic_x, *mosaic_y;
584   UINT16 c;
585   int screen;
586
587#if SNES_LAYER_DEBUG
588   if (m_debug_options.bg_disabled[layer])
589      return;
590#endif /* SNES_LAYER_DEBUG */
591
592   m_scanlines[SNES_MAINSCREEN].enable = m_layer[layer].main_bg_enabled;
593   m_scanlines[SNES_SUBSCREEN].enable = m_layer[layer].sub_bg_enabled;
594   m_scanlines[SNES_MAINSCREEN].clip = m_layer[layer].main_window_enabled;
595   m_scanlines[SNES_SUBSCREEN].clip = m_layer[layer].sub_window_enabled;
596
597   if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
598      return;
599
600   ma = m_mode7.matrix_a;
601   mb = m_mode7.matrix_b;
602   mc = m_mode7.matrix_c;
603   md = m_mode7.matrix_d;
604   xc = m_mode7.origin_x;
605   yc = m_mode7.origin_y;
606   hs = m_mode7.hor_offset;
607   vs = m_mode7.ver_offset;
608
609   /* Sign extend */
610   xc <<= 19;
611   xc >>= 19;
612   yc <<= 19;
613   yc >>= 19;
614   hs <<= 19;
615   hs >>= 19;
616   vs <<= 19;
617   vs >>= 19;
618
619   /* Vertical flip */
620   if (m_mode7.vflip)
621      sy = 255 - curline;
622   else
623      sy = curline;
624
625   /* Horizontal flip */
626   if (m_mode7.hflip)
627   {
628      xpos = 255;
629      xdir = -1;
630   }
631   else
632   {
633      xpos = 0;
634      xdir = 1;
635   }
636
637   /* MOSAIC - to be verified */
638   if (layer == SNES_BG2)  // BG2 use two different bits for horizontal and vertical mosaic
639   {
640      mosaic_x = m_mosaic_table[m_layer[SNES_BG2].mosaic_enabled ? m_mosaic_size : 0];
641      mosaic_y = m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
642   }
643   else    // BG1 works as usual
644   {
645      mosaic_x =  m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
646      mosaic_y =  m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
647   }
648
649#if SNES_LAYER_DEBUG
650   if (m_debug_options.mosaic_disabled)
651   {
652      mosaic_x =  m_mosaic_table[0];
653      mosaic_y =  m_mosaic_table[0];
654   }
655#endif /* SNES_LAYER_DEBUG */
656
657   /* Let's do some mode7 drawing huh? */
658   /* These can be computed only once, since they do not depend on sx */
659   x0 = ((ma * MODE7_CLIP(hs - xc)) & ~0x3f) + ((mb * mosaic_y[sy]) & ~0x3f) + ((mb * MODE7_CLIP(vs - yc)) & ~0x3f) + (xc << 8);
660   y0 = ((mc * MODE7_CLIP(hs - xc)) & ~0x3f) + ((md * mosaic_y[sy]) & ~0x3f) + ((md * MODE7_CLIP(vs - yc)) & ~0x3f) + (yc << 8);
661
662   for (sx = 0; sx < 256; sx++, xpos += xdir)
663   {
664      tx = (x0 + (ma * mosaic_x[sx])) >> 8;
665      ty = (y0 + (mc * mosaic_x[sx])) >> 8;
666
667      switch (m_mode7.repeat)
668      {
669         case 0x00:  /* Repeat if outside screen area */
670         case 0x01:  /* Repeat if outside screen area */
671            tx &= 0x3ff;
672            ty &= 0x3ff;
673            tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
674            colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
675            break;
676         case 0x02:  /* Single colour backdrop screen if outside screen area */
677            if ((tx >= 0) && (tx < 1024) && (ty >= 0) && (ty < 1024))
678            {
679               tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
680               colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
681            }
682            else
683               colour = 0;
684            break;
685         case 0x03:  /* Character 0x00 repeat if outside screen area */
686            if ((tx >= 0) && (tx < 1024) && (ty >= 0) && (ty < 1024))
687               tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
688            else
689               tiled = 0;
690
691            colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
692            break;
693      }
694
695      /* The last bit is for priority in EXTBG mode (used only for BG2) */
696      if (layer == SNES_BG2)
697      {
698         priority = ((colour & 0x80) >> 7) ? priority_a : priority_b;
699         colour &= 0x7f;
700
701#if SNES_LAYER_DEBUG
702      /* if we want to draw only one of the priorities of this layer */
703      if (((m_debug_options.select_pri[layer] & 0x01) && (priority == priority_a)) ||
704         ((m_debug_options.select_pri[layer] & 0x02) && (priority == priority_b)))
705         continue;
706#endif /* SNES_LAYER_DEBUG */
707      }
708
709      for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
710      {
711         if (m_scanlines[screen].enable)
712         {
713            UINT8 clr = colour;
714            UINT8 clipmask = m_clipmasks[layer][xpos];
715
716#if SNES_LAYER_DEBUG
717            if (m_debug_options.windows_disabled)
718               clipmask = 0xff;
719#endif /* SNES_LAYER_DEBUG */
720
721            /* Clip to windows */
722            if (m_scanlines[screen].clip)
723               clr &= clipmask;
724
725            /* Draw pixel if appropriate */
726            if (m_scanlines[screen].priority[xpos] <= priority && clr > 0)
727            {
728               /* Direct select, but only outside EXTBG! */
729               // Direct color format is: 0 | BB000 | GGG00 | RRR00, HW confirms that the data is zero padded.
730               // In other words, like normal direct color, with pal = 0
731               c = get_bgcolor(m_direct_color && layer == SNES_BG1, 0, clr);
732               set_scanline_pixel(screen, xpos, c, priority, layer, 0);
733            }
734         }
735      }
736   }
737}
738
739/*************************************************************************************************
740 * SNES Sprites
741 *
742 * 1. First of all: sprites are drawn one line in advance. We emulate this by caching the
743 * starting vram address, the sprite size and the "name select" at each line, and by using
744 * them the next line to output the proper sprites - see update_obsel.
745 *
746 * 2. Each line can select its sprites among 128 available ones in oam_ram, hence we start
747 * by creating a list of the available objects (each one with its x,y coordinate, its size,
748 * its tile address, etc.) - see oam_list_build.
749 *
750 * 3. Next, we start finding out which sprites will appear in the line: starting from
751 * FirstSprite, we count 32 OBJs which intersect our line and we store their indexes in the
752 * oam_itemlist array (if more than 32 sprites intersect our line, we set the Range Over
753 * flag); then, selecting among these sprites, we count 34 8x8 tiles which are visible
754 * in our line (i.e. whose x coord is between -size and 256) and we store the corresponding
755 * coordinates/priorities/palettes/etc. in the oam_tilelist array (if more than 34 tiles would
756 * appear on screen, we set the Time Over flag).
757 * Notice that when we populate oam_tilelist, we proceed from oam_itemlist[31] (or from the last
758 * item which intersects the scanline), towards oam_itemlist[0], i.e. the higher tiles (say
759 * oam_tilelist[34], or the last tile which appear on screen) will contain FirstSprite object,
760 * or the sprites with closer index to FirstSprite which get displayed. This will play an
761 * important role for sprite priority - see update_objects_rto.
762 *
763 * 4. All the above happens at the beginning of each VIDEO_UPDATE. When we finally draw the
764 * scanline, we pass through the oam_tilelist and we store the displayed pixels in our scanline
765 * buffer. Notice that, for each pixel of a SNES sprite, only the priority of the topmost sprite
766 * is tested against the priority of the BG pixel (because FirstSprite is on top of FirstSprite+1,
767 * which is on top of FirstSprite+2, etc., and therefore other sprites are already covered by the
768 * topmost one). To emulate this, we draw each tile over the previous ones no matter what
769 * priorities are (differently from what we did with BGs): in the end, we will have in each pixel z
770 * its topmost sprite and scanline.priority[z] will be the topmost sprite priority as expected.
771 * Of course, sprite drawing must happen before BG drawing, so that afterwords BG pixels properly
772 * test their priority with the one of the correct sprite - see update_objects.
773 *************************************************************************************************/
774
775
776/*********************************************
777 * update_obsel()
778 *
779 * Update sprite settings for next line.
780 *********************************************/
781
782void snes_ppu_class::update_obsel( void )
783{
784   m_layer[SNES_OAM].charmap = m_oam.next_charmap;
785   m_oam.name_select = m_oam.next_name_select;
786
787   if (m_oam.size != m_oam.next_size)
788   {
789      m_oam.size = m_oam.next_size;
790      m_update_oam_list = 1;
791   }
792}
793
794/*********************************************
795 * oam_list_build()
796 *
797 * Build a list of the available obj in OAM ram.
798 *********************************************/
799
800void snes_ppu_class::oam_list_build( void )
801{
802   UINT8 *oamram = (UINT8 *)m_oam_ram;
803   INT16 oam = 0x1ff;
804   UINT16 oam_extra = oam + 0x20;
805   UINT16 extra = 0;
806   int ii;
807
808   m_update_oam_list = 0;       // eventually, we can optimize the code by only calling this function when there is a change in size
809
810   for (ii = 127; ii >= 0; ii--)
811   {
812      if (((ii + 1) % 4) == 0)
813         extra = oamram[oam_extra--];
814
815      m_oam_spritelist[ii].vflip = (oamram[oam] & 0x80) >> 7;
816      m_oam_spritelist[ii].hflip = (oamram[oam] & 0x40) >> 6;
817      m_oam_spritelist[ii].priority_bits = (oamram[oam] & 0x30) >> 4;
818      m_oam_spritelist[ii].pal = 128 + ((oamram[oam] & 0x0e) << 3);
819      m_oam_spritelist[ii].tile = (oamram[oam--] & 0x1) << 8;
820      m_oam_spritelist[ii].tile |= oamram[oam--];
821      m_oam_spritelist[ii].y = oamram[oam--] + 1;
822      m_oam_spritelist[ii].x = oamram[oam--];
823      m_oam_spritelist[ii].size = (extra & 0x80) >> 7;
824      extra <<= 1;
825      m_oam_spritelist[ii].x |= ((extra & 0x80) << 1);
826      extra <<= 1;
827
828      m_oam_spritelist[ii].y *= m_obj_interlace;
829      m_oam_spritelist[ii].y &= 0x1ff;
830
831      m_oam_spritelist[ii].x &= 0x1ff;
832
833      /* Determine object size */
834      switch (m_oam.size)
835      {
836      case 0:         /* 8x8 or 16x16 */
837         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 2 : 1;
838         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 2 : 1;
839         break;
840      case 1:         /* 8x8 or 32x32 */
841         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 4 : 1;
842         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 1;
843         break;
844      case 2:         /* 8x8 or 64x64 */
845         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 8 : 1;
846         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 1;
847         break;
848      case 3:         /* 16x16 or 32x32 */
849         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 4 : 2;
850         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 2;
851         break;
852      case 4:         /* 16x16 or 64x64 */
853         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 8 : 2;
854         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 2;
855         break;
856      case 5:         /* 32x32 or 64x64 */
857         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 8 : 4;
858         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 4;
859         break;
860      case 6:         /* undocumented: 16x32 or 32x64 */
861         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 4 : 2;
862         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 4;
863         if (m_obj_interlace && !m_oam_spritelist[ii].size)
864            m_oam_spritelist[ii].height = 2;
865         break;
866      case 7:         /* undocumented: 16x32 or 32x32 */
867         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 4 : 2;
868         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 4;
869         if (m_obj_interlace && !m_oam_spritelist[ii].size)
870            m_oam_spritelist[ii].height = 2;
871         break;
872      default:
873         /* we should never enter here... */
874         logerror("Object size unsupported: %d\n", m_oam.size);
875         break;
876      }
877   }
878}
879
880/*********************************************
881 * is_sprite_on_scanline()
882 *
883 * Check if a given sprites intersect current
884 * scanline
885 *********************************************/
886
887int snes_ppu_class::is_sprite_on_scanline( UINT16 curline, UINT8 sprite )
888{
889   //if sprite is entirely offscreen and doesn't wrap around to the left side of the screen,
890   //then it is not counted. this *should* be 256, and not 255, even though dot 256 is offscreen.
891   int spr_height = (m_oam_spritelist[sprite].height << 3);
892
893   if (m_oam_spritelist[sprite].x > 256 && (m_oam_spritelist[sprite].x + (m_oam_spritelist[sprite].width << 3) - 1) < 512)
894      return 0;
895
896   if (curline >= m_oam_spritelist[sprite].y && curline < (m_oam_spritelist[sprite].y + spr_height))
897      return 1;
898
899   if ((m_oam_spritelist[sprite].y + spr_height) >= 256 && curline < ((m_oam_spritelist[sprite].y + spr_height) & 255))
900      return 1;
901
902   return 0;
903}
904
905/*********************************************
906 * update_objects_rto()
907 *
908 * Determine which OBJs will be drawn on this
909 * scanline.
910 *********************************************/
911
912void snes_ppu_class::update_objects_rto( UINT16 curline )
913{
914   int ii, jj, active_sprite;
915   UINT8 range_over, time_over;
916   INT8 xs, ys;
917   UINT8 line;
918   UINT8 height, width, vflip, hflip, priority, pal;
919   UINT16 tile;
920   INT16 x, y;
921   UINT32 name_sel = 0;
922
923   oam_list_build();
924
925   /* initialize counters */
926   range_over = 0;
927   time_over = 0;
928
929   /* setup the proper line */
930   curline /= m_interlace;
931   curline *= m_obj_interlace;
932
933   /* reset the list of first 32 objects which intersect current scanline */
934   memset(m_oam_itemlist, 0xff, 32);
935
936   /* populate the list of 32 objects */
937   for (ii = 0; ii < 128; ii++)
938   {
939      active_sprite = (ii + m_oam.first_sprite) & 0x7f;
940
941      if (!is_sprite_on_scanline(curline, active_sprite))
942         continue;
943
944      if (range_over++ >= 32)
945         break;
946
947      m_oam_itemlist[range_over - 1] = active_sprite;
948   }
949
950   /* reset the list of first 34 tiles to be drawn */
951   for (ii = 0; ii < 34; ii++)
952      m_oam_tilelist[ii].tileaddr = 0xffff;
953
954   /* populate the list of 34 tiles */
955   for (ii = 31; ii >= 0; ii--)
956   {
957      if (m_oam_itemlist[ii] == 0xff)
958         continue;
959
960      active_sprite = m_oam_itemlist[ii];
961
962      tile = m_oam_spritelist[active_sprite].tile;
963      x = m_oam_spritelist[active_sprite].x;
964      y = m_oam_spritelist[active_sprite].y;
965      height = m_oam_spritelist[active_sprite].height;
966      width = m_oam_spritelist[active_sprite].width;
967      vflip = m_oam_spritelist[active_sprite].vflip;
968      hflip = m_oam_spritelist[active_sprite].hflip;
969      priority = m_oam_spritelist[active_sprite].priority_bits;
970      pal = m_oam_spritelist[active_sprite].pal;
971
972      /* Adjust y, if past maximum position (for sprites which overlap between top & bottom) */
973      if (y >= (0x100 - 16) * m_interlace)
974         y -= (0x100) * m_interlace;
975
976      if (curline >= y && curline < (y + (height << 3)))
977      {
978         /* Only objects using tiles over 255 use name select */
979         name_sel = (tile < 256) ? 0 : m_oam.name_select;
980
981         ys = (curline - y) >> 3;
982         line = (curline - y) % 8;
983         if (vflip)
984         {
985            ys = height - ys - 1;
986            line = 7 - line;
987         }
988         line <<= 1;
989         tile <<= 5;
990
991         for (jj = 0; jj < width; jj++)
992         {
993            INT16 xx = (x + (jj << 3)) & 0x1ff;
994
995            if (x != 256 && xx >= 256 && (xx + 7) < 512)
996               continue;
997
998            if (time_over++ >= 34)
999               break;
1000
1001            xs = (hflip) ? (width - 1 - jj) : jj;
1002            m_oam_tilelist[time_over - 1].tileaddr = name_sel + tile + table_obj_offset[ys][xs] + line;
1003            m_oam_tilelist[time_over - 1].hflip = hflip;
1004            m_oam_tilelist[time_over - 1].x = xx;
1005            m_oam_tilelist[time_over - 1].pal = pal;
1006            m_oam_tilelist[time_over - 1].priority = priority;
1007         }
1008      }
1009   }
1010
1011   /* set Range Over flag if necessary */
1012   if (range_over > 32)
1013      m_stat77 |= 0x40;
1014
1015   /* set Time Over flag if necessary */
1016   if (time_over > 34)
1017      m_stat77 |= 0x80;
1018}
1019
1020/*********************************************
1021 * update_objects()
1022 *
1023 * Update an entire line of sprites.
1024 *********************************************/
1025
1026void snes_ppu_class::update_objects( UINT8 priority_oam0, UINT8 priority_oam1, UINT8 priority_oam2, UINT8 priority_oam3 )
1027{
1028   UINT8 pri, priority[4];
1029   UINT32 charaddr;
1030   int ii;
1031
1032#if SNES_LAYER_DEBUG
1033   if (m_debug_options.bg_disabled[SNES_OAM])
1034      return;
1035#endif /* SNES_LAYER_DEBUG */
1036
1037   m_scanlines[SNES_MAINSCREEN].enable = m_layer[SNES_OAM].main_bg_enabled;
1038   m_scanlines[SNES_SUBSCREEN].enable = m_layer[SNES_OAM].sub_bg_enabled;
1039   m_scanlines[SNES_MAINSCREEN].clip = m_layer[SNES_OAM].main_window_enabled;
1040   m_scanlines[SNES_SUBSCREEN].clip = m_layer[SNES_OAM].sub_window_enabled;
1041
1042   if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
1043      return;
1044
1045   charaddr = m_layer[SNES_OAM].charmap << 13;
1046
1047   priority[0] = priority_oam0;
1048   priority[1] = priority_oam1;
1049   priority[2] = priority_oam2;
1050   priority[3] = priority_oam3;
1051
1052   /* finally draw the tiles from the tilelist */
1053   for (ii = 0; ii < 34; ii++)
1054   {
1055      int tile = ii;
1056#if SNES_LAYER_DEBUG
1057      if (m_debug_options.sprite_reversed)
1058         tile = 33 - ii;
1059#endif /* SNES_LAYER_DEBUG */
1060
1061      if (m_oam_tilelist[tile].tileaddr == 0xffff)
1062         continue;
1063
1064      pri = priority[m_oam_tilelist[tile].priority];
1065
1066#if SNES_LAYER_DEBUG
1067      if (m_debug_options.select_pri[SNES_OAM])
1068      {
1069         int oam_draw = m_debug_options.select_pri[SNES_OAM] - 1;
1070         if (oam_draw != m_oam_tilelist[tile].priority)
1071            continue;
1072      }
1073#endif /* SNES_LAYER_DEBUG */
1074
1075      /* OAM tiles have fixed planes (4), no direct color and no hires, but otherwise work the same as BG ones */
1076      draw_tile(4, SNES_OAM, charaddr + m_oam_tilelist[tile].tileaddr, m_oam_tilelist[tile].x, pri, m_oam_tilelist[tile].hflip, 0, m_oam_tilelist[tile].pal, 0);
1077   }
1078}
1079
1080
1081/*********************************************
1082 * snes_update_mode_X()
1083 *
1084 * Update Mode X line.
1085 *********************************************/
1086
1087void snes_ppu_class::update_mode_0( UINT16 curline )
1088{
1089#if SNES_LAYER_DEBUG
1090   if (m_debug_options.mode_disabled[0])
1091      return;
1092#endif /* SNES_LAYER_DEBUG */
1093
1094   update_objects(3, 6, 9, 12);
1095   update_line(curline, SNES_BG1, 8, 11, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1096   update_line(curline, SNES_BG2, 7, 10, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1097   update_line(curline, SNES_BG3, 2, 5,  SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1098   update_line(curline, SNES_BG4, 1, 4,  SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1099}
1100
1101void snes_ppu_class::update_mode_1( UINT16 curline )
1102{
1103#if SNES_LAYER_DEBUG
1104   if (m_debug_options.mode_disabled[1])
1105      return;
1106#endif /* SNES_LAYER_DEBUG */
1107
1108   if (!m_bg3_priority_bit)
1109   {
1110      update_objects(2, 4, 7, 10);
1111      update_line(curline, SNES_BG1, 6, 9, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
1112      update_line(curline, SNES_BG2, 5, 8, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
1113      update_line(curline, SNES_BG3, 1, 3, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1114   }
1115   else
1116   {
1117      update_objects(2, 3, 6, 9);
1118      update_line(curline, SNES_BG1, 5, 8,  SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
1119      update_line(curline, SNES_BG2, 4, 7,  SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
1120      update_line(curline, SNES_BG3, 1, 10, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1121   }
1122}
1123
1124void snes_ppu_class::update_mode_2( UINT16 curline )
1125{
1126#if SNES_LAYER_DEBUG
1127   if (m_debug_options.mode_disabled[2])
1128      return;
1129#endif /* SNES_LAYER_DEBUG */
1130
1131   update_objects(2, 4, 6, 8);
1132   update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_MODE2, 0);
1133   update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_MODE2, 0);
1134}
1135
1136void snes_ppu_class::update_mode_3( UINT16 curline )
1137{
1138#if SNES_LAYER_DEBUG
1139   if (m_debug_options.mode_disabled[3])
1140      return;
1141#endif /* SNES_LAYER_DEBUG */
1142
1143   update_objects(2, 4, 6, 8);
1144   update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_8BPP, 0, SNES_OPT_NONE, m_direct_color);
1145   update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
1146}
1147
1148void snes_ppu_class::update_mode_4( UINT16 curline )
1149{
1150#if SNES_LAYER_DEBUG
1151   if (m_debug_options.mode_disabled[4])
1152      return;
1153#endif /* SNES_LAYER_DEBUG */
1154
1155   update_objects(2, 4, 6, 8);
1156   update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_8BPP, 0, SNES_OPT_MODE4, m_direct_color);
1157   update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_MODE4, 0);
1158}
1159
1160void snes_ppu_class::update_mode_5( UINT16 curline )
1161{
1162#if SNES_LAYER_DEBUG
1163   if (m_debug_options.mode_disabled[5])
1164      return;
1165#endif /* SNES_LAYER_DEBUG */
1166
1167   update_objects(2, 4, 6, 8);
1168   update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_4BPP, 1, SNES_OPT_NONE, 0);
1169   update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_2BPP, 1, SNES_OPT_NONE, 0);
1170}
1171
1172void snes_ppu_class::update_mode_6( UINT16 curline )
1173{
1174#if SNES_LAYER_DEBUG
1175   if (m_debug_options.mode_disabled[6])
1176      return;
1177#endif /* SNES_LAYER_DEBUG */
1178
1179   update_objects(1, 3, 4, 6);
1180   update_line(curline, SNES_BG1, 2, 5, SNES_COLOR_DEPTH_4BPP, 1, SNES_OPT_MODE6, 0);
1181}
1182
1183void snes_ppu_class::update_mode_7( UINT16 curline )
1184{
1185#if SNES_LAYER_DEBUG
1186   if (m_debug_options.mode_disabled[7])
1187      return;
1188#endif /* SNES_LAYER_DEBUG */
1189
1190   if (!m_mode7.extbg)
1191   {
1192      update_objects(1, 3, 4, 5);
1193      update_line_mode7(curline, SNES_BG1, 2, 2);
1194   }
1195   else
1196   {
1197      update_objects(2, 4, 6, 7);
1198      update_line_mode7(curline, SNES_BG1, 3, 3);
1199      update_line_mode7(curline, SNES_BG2, 1, 5);
1200   }
1201}
1202
1203/*********************************************
1204 * snes_draw_screens()
1205 *
1206 * Draw the whole screen (Mode 0 -> 7).
1207 *********************************************/
1208
1209void snes_ppu_class::draw_screens( UINT16 curline )
1210{
1211   switch (m_mode)
1212   {
1213      case 0: update_mode_0(curline); break;     /* Mode 0 */
1214      case 1: update_mode_1(curline); break;     /* Mode 1 */
1215      case 2: update_mode_2(curline); break;     /* Mode 2 - Supports offset per tile */
1216      case 3: update_mode_3(curline); break;     /* Mode 3 - Supports direct colour */
1217      case 4: update_mode_4(curline); break;     /* Mode 4 - Supports offset per tile and direct colour */
1218      case 5: update_mode_5(curline); break;     /* Mode 5 - Supports hires */
1219      case 6: update_mode_6(curline); break;     /* Mode 6 - Supports offset per tile and hires */
1220      case 7: update_mode_7(curline); break;     /* Mode 7 - Supports direct colour */
1221   }
1222}
1223
1224/*********************************************
1225 * update_windowmasks()
1226 *
1227 * An example of how windows work:
1228 * Win1: ...#####......
1229 * Win2: ......#####...
1230 *             IN                 OUT
1231 * OR:   ...########...     ###........###
1232 * AND:  ......##......     ######..######
1233 * XOR:  ...###..###...     ###...##...###
1234 * XNOR: ###...##...###     ...###..###...
1235 *********************************************/
1236
1237void snes_ppu_class::update_windowmasks( void )
1238{
1239   UINT16 ii, jj;
1240   INT8 w1, w2;
1241
1242   m_update_windows = 0;        /* reset the flag */
1243
1244   for (ii = 0; ii < SNES_SCR_WIDTH; ii++)
1245   {
1246      /* update bg 1, 2, 3, 4, obj & color windows */
1247      /* jj = layer */
1248      for (jj = 0; jj < 6; jj++)
1249      {
1250         m_clipmasks[jj][ii] = 0xff;  /* let's start from un-masked */
1251         w1 = w2 = -1;
1252
1253         if (m_layer[jj].window1_enabled)
1254         {
1255            /* Default to mask area inside */
1256            if ((ii < m_window1_left) || (ii > m_window1_right))
1257               w1 = 0;
1258            else
1259               w1 = 1;
1260
1261            /* If mask area is outside then swap */
1262            if (m_layer[jj].window1_invert)
1263               w1 = !w1;
1264         }
1265
1266         if (m_layer[jj].window2_enabled)
1267         {
1268            if ((ii < m_window2_left) || (ii > m_window2_right))
1269               w2 = 0;
1270            else
1271               w2 = 1;
1272            if (m_layer[jj].window2_invert)
1273               w2 = !w2;
1274         }
1275
1276         /* mask if the appropriate expression is true */
1277         if (w1 >= 0 && w2 >= 0)
1278         {
1279            switch (m_layer[jj].wlog_mask)
1280            {
1281               case 0x00:  /* OR */
1282                  m_clipmasks[jj][ii] = (w1 | w2) ? 0x00 : 0xff;
1283                  break;
1284               case 0x01:  /* AND */
1285                  m_clipmasks[jj][ii] = (w1 & w2) ? 0x00 : 0xff;
1286                  break;
1287               case 0x02:  /* XOR */
1288                  m_clipmasks[jj][ii] = (w1 ^ w2) ? 0x00 : 0xff;
1289                  break;
1290               case 0x03:  /* XNOR */
1291                  m_clipmasks[jj][ii] = !(w1 ^ w2) ? 0x00 : 0xff;
1292                  break;
1293            }
1294         }
1295         else if (w1 >= 0)
1296            m_clipmasks[jj][ii] = w1 ? 0x00 : 0xff;
1297         else if (w2 >= 0)
1298            m_clipmasks[jj][ii] = w2 ? 0x00 : 0xff;
1299      }
1300   }
1301}
1302
1303/*********************************************
1304 * update_offsets()
1305 *
1306 * Update the offsets with the latest changes.
1307 * This is currently unused, but it could
1308 * possibly be handy for some minor optimization
1309 *********************************************/
1310
1311void snes_ppu_class::update_offsets( void )
1312{
1313   int ii;
1314   for (ii = 0; ii < 4; ii++)
1315   {
1316   }
1317   m_update_offsets = 0;
1318}
1319
1320/*****************************************
1321 * draw_blend()
1322 *
1323 * Routine for additive/subtractive blending
1324 * between the main and sub screens, i.e.
1325 * color math.
1326 *****************************************/
1327
1328inline void snes_ppu_class::draw_blend( UINT16 offset, UINT16 *colour, UINT8 prevent_color_math, UINT8 black_pen_clip, int switch_screens )
1329{
1330#if SNES_LAYER_DEBUG
1331   if (m_debug_options.colormath_disabled)
1332      return;
1333#endif /* SNES_LAYER_DEBUG */
1334
1335   /* when color math is applied to subscreen pixels, the blending depends on the blending used by the previous mainscreen
1336   pixel, except for subscreen pixel 0 which has no previous mainscreen pixel, see comments in refresh_scanline */
1337   if (switch_screens && offset > 0)
1338      offset -= 1;
1339
1340   if ((black_pen_clip == SNES_CLIP_ALWAYS) ||
1341      (black_pen_clip == SNES_CLIP_IN && m_clipmasks[SNES_COLOR][offset]) ||
1342      (black_pen_clip == SNES_CLIP_OUT && !m_clipmasks[SNES_COLOR][offset]))
1343      *colour = 0; //clip to black before color math
1344
1345   if (prevent_color_math == SNES_CLIP_ALWAYS) // blending mode 3 == always OFF
1346      return;
1347
1348   if ((prevent_color_math == SNES_CLIP_NEVER) ||
1349      (prevent_color_math == SNES_CLIP_IN  && !m_clipmasks[SNES_COLOR][offset]) ||
1350      (prevent_color_math == SNES_CLIP_OUT && m_clipmasks[SNES_COLOR][offset]))
1351   {
1352      UINT16 r, g, b;
1353      struct SNES_SCANLINE *subscreen;
1354      int clip_max = 0;   // if add then clip to 0x1f, if sub then clip to 0
1355
1356#if SNES_LAYER_DEBUG
1357      /* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */
1358      if (m_debug_options.draw_subscreen)
1359      {
1360         subscreen = switch_screens ? &m_scanlines[SNES_SUBSCREEN] : &m_scanlines[SNES_MAINSCREEN];
1361      }
1362      else
1363#endif /* SNES_LAYER_DEBUG */
1364      {
1365         subscreen = switch_screens ? &m_scanlines[SNES_MAINSCREEN] : &m_scanlines[SNES_SUBSCREEN];
1366      }
1367
1368      if (m_sub_add_mode) /* SNES_SUBSCREEN*/
1369      {
1370         if (!BIT(m_color_modes, 7))
1371         {
1372            /* 0x00 add */
1373            r = (*colour & 0x1f) + (subscreen->buffer[offset] & 0x1f);
1374            g = ((*colour & 0x3e0) >> 5) + ((subscreen->buffer[offset] & 0x3e0) >> 5);
1375            b = ((*colour & 0x7c00) >> 10) + ((subscreen->buffer[offset] & 0x7c00) >> 10);
1376            clip_max = 1;
1377         }
1378         else
1379         {
1380            /* 0x80 sub */
1381            r = (*colour & 0x1f) - (subscreen->buffer[offset] & 0x1f);
1382            g = ((*colour & 0x3e0) >> 5) - ((subscreen->buffer[offset] & 0x3e0) >> 5);
1383            b = ((*colour & 0x7c00) >> 10) - ((subscreen->buffer[offset] & 0x7c00) >> 10);
1384            if (r > 0x1f) r = 0;
1385            if (g > 0x1f) g = 0;
1386            if (b > 0x1f) b = 0;
1387         }
1388         /* only halve if the color is not the back colour */
1389         if (BIT(m_color_modes, 6) && (subscreen->buffer[offset] != m_cgram[FIXED_COLOUR]))
1390         {
1391            r >>= 1;
1392            g >>= 1;
1393            b >>= 1;
1394         }
1395      }
1396      else /* Fixed colour */
1397      {
1398         if (!BIT(m_color_modes, 7))
1399         {
1400            /* 0x00 add */
1401            r = (*colour & 0x1f) + (m_cgram[FIXED_COLOUR] & 0x1f);
1402            g = ((*colour & 0x3e0) >> 5) + ((m_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
1403            b = ((*colour & 0x7c00) >> 10) + ((m_cgram[FIXED_COLOUR] & 0x7c00) >> 10);
1404            clip_max = 1;
1405         }
1406         else
1407         {
1408            /* 0x80: sub */
1409            r = (*colour & 0x1f) - (m_cgram[FIXED_COLOUR] & 0x1f);
1410            g = ((*colour & 0x3e0) >> 5) - ((m_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
1411            b = ((*colour & 0x7c00) >> 10) - ((m_cgram[FIXED_COLOUR] & 0x7c00) >> 10);
1412            if (r > 0x1f) r = 0;
1413            if (g > 0x1f) g = 0;
1414            if (b > 0x1f) b = 0;
1415         }
1416         /* halve if necessary */
1417         if (BIT(m_color_modes, 6))
1418         {
1419            r >>= 1;
1420            g >>= 1;
1421            b >>= 1;
1422         }
1423      }
1424
1425      /* according to anomie's docs, after addition has been performed, division by 2 happens *before* clipping to max, hence we clip now */
1426      if (clip_max)
1427      {
1428         if (r > 0x1f) r = 0x1f;
1429         if (g > 0x1f) g = 0x1f;
1430         if (b > 0x1f) b = 0x1f;
1431      }
1432
1433      *colour = ((r & 0x1f) | ((g & 0x1f) << 5) | ((b & 0x1f) << 10));
1434   }
1435}
1436
1437/*********************************************
1438 * refresh_scanline()
1439 *
1440 * Redraw the current line.
1441 *********************************************/
1442/*********************************************
1443 * Notice that in hires and pseudo hires modes,
1444 * i.e. when 512 different pixels are present
1445 * in a scanline, a crt TV monitor would end
1446 * up blending adjacent pixels. To mimic this,
1447 * we add a small (optional) hack which enters
1448 * only in the very last stage of the scanline
1449 * drawing and which simulates the TV by
1450 * replacing the exact pixel color with an
1451 * average of the current and next pixel colors.
1452 * Credits (and thanks) to Blargg and Byuu for
1453 * the optimized averaging algorithm.
1454 *********************************************/
1455
1456void snes_ppu_class::refresh_scanline( running_machine &machine, bitmap_rgb32 &bitmap, UINT16 curline )
1457{
1458   UINT16 ii;
1459   int x;
1460   int fade;
1461   struct SNES_SCANLINE *scanline1, *scanline2;
1462   UINT16 c;
1463   UINT16 prev_colour = 0;
1464   int blurring = machine.root_device().ioport("OPTIONS")->read_safe(0) & 0x01;
1465
1466   g_profiler.start(PROFILER_VIDEO);
1467
1468   if (m_screen_disabled) /* screen is forced blank */
1469      for (x = 0; x < SNES_SCR_WIDTH * 2; x++)
1470         bitmap.pix32(curline, x) = rgb_t::black;
1471   else
1472   {
1473      /* Update clip window masks if necessary */
1474      if (m_update_windows)
1475         update_windowmasks();
1476      /* Update the offsets if necessary */
1477      if (m_update_offsets)
1478         update_offsets();
1479
1480      /* Clear priority */
1481      memset(m_scanlines[SNES_MAINSCREEN].priority, 0, SNES_SCR_WIDTH);
1482      memset(m_scanlines[SNES_SUBSCREEN].priority, 0, SNES_SCR_WIDTH);
1483
1484      /* Clear layers */
1485      memset(m_scanlines[SNES_MAINSCREEN].layer, SNES_COLOR, SNES_SCR_WIDTH);
1486      memset(m_scanlines[SNES_SUBSCREEN].layer, SNES_COLOR, SNES_SCR_WIDTH);
1487
1488      /* Clear blend_exception (only used for OAM) */
1489      memset(m_scanlines[SNES_MAINSCREEN].blend_exception, 0, SNES_SCR_WIDTH);
1490      memset(m_scanlines[SNES_SUBSCREEN].blend_exception, 0, SNES_SCR_WIDTH);
1491
1492      /* Draw back colour */
1493      for (ii = 0; ii < SNES_SCR_WIDTH; ii++)
1494      {
1495         if (m_mode == 5 || m_mode == 6 || m_pseudo_hires)
1496            m_scanlines[SNES_SUBSCREEN].buffer[ii] = m_cgram[0];
1497         else
1498            m_scanlines[SNES_SUBSCREEN].buffer[ii] = m_cgram[FIXED_COLOUR];
1499
1500         m_scanlines[SNES_MAINSCREEN].buffer[ii] = m_cgram[0];
1501      }
1502
1503      /* Prepare OAM for this scanline */
1504      update_objects_rto(curline);
1505
1506      /* Draw scanline */
1507      draw_screens(curline);
1508
1509      update_obsel();
1510
1511#if SNES_LAYER_DEBUG
1512      if (dbg_video(machine, curline))
1513      {
1514         g_profiler.stop();
1515         return;
1516      }
1517
1518      /* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */
1519      if (m_debug_options.draw_subscreen)
1520      {
1521         scanline1 = &m_scanlines[SNES_SUBSCREEN];
1522         scanline2 = &m_scanlines[SNES_MAINSCREEN];
1523      }
1524      else
1525#endif /* SNES_LAYER_DEBUG */
1526      {
1527         scanline1 = &m_scanlines[SNES_MAINSCREEN];
1528         scanline2 = &m_scanlines[SNES_SUBSCREEN];
1529      }
1530
1531      /* Draw the scanline to screen */
1532
1533      fade = m_screen_brightness;
1534
1535      for (x = 0; x < SNES_SCR_WIDTH; x++)
1536      {
1537         int r, g, b, hires;
1538         UINT16 tmp_col[2];
1539         hires = (m_mode != 5 && m_mode != 6 && !m_pseudo_hires) ? 0 : 1;
1540
1541         /* in hires, the first pixel (of 512) is subscreen pixel, then the first mainscreen pixel follows, and so on... */
1542         if (!hires)
1543         {
1544            c = scanline1->buffer[x];
1545
1546            /* perform color math if the layer wants it (except if it's an object > 192) */
1547            if (!scanline1->blend_exception[x] && m_layer[scanline1->layer[x]].color_math)
1548               draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 0);
1549
1550            r = ((c & 0x1f) * fade) >> 4;
1551            g = (((c & 0x3e0) >> 5) * fade) >> 4;
1552            b = (((c & 0x7c00) >> 10) * fade) >> 4;
1553
1554            bitmap.pix32(curline, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
1555            bitmap.pix32(curline, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
1556         }
1557         else
1558         {
1559            /* prepare the pixel from main screen */
1560            c = scanline1->buffer[x];
1561
1562            /* perform color math if the layer wants it (except if it's an object > 192) */
1563            if (!scanline1->blend_exception[x] && m_layer[scanline1->layer[x]].color_math)
1564               draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 0);
1565
1566            tmp_col[1] = c;
1567
1568            /* prepare the pixel from sub screen */
1569            c = scanline2->buffer[x];
1570
1571            /* in hires/pseudo-hires, subscreen pixels are blended as well: for each subscreen pixel, color math
1572            is applied if it had been applied to the previous mainscreen pixel. What happens at subscreen pixel 0
1573            (which has no previous mainscreen pixel) is undocumented. Until more info are discovered, we (arbitrarily)
1574            apply to it the same color math as the *next* mainscreen pixel (i.e. mainscreen pixel 0), which seems as good as
1575            any other choice */
1576            if (x == 0 && !scanline1->blend_exception[0] && m_layer[scanline1->layer[0]].color_math)
1577               draw_blend(0, &c, m_prevent_color_math, m_clip_to_black, 1);
1578            else if (x > 0  && !scanline1->blend_exception[x - 1] && m_layer[scanline1->layer[x - 1]].color_math)
1579               draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 1);
1580
1581            tmp_col[0] = c;
1582
1583            /* average the first pixel if required, or draw it directly*/
1584            if (blurring)
1585               c = (prev_colour + tmp_col[0] - ((prev_colour ^ tmp_col[0]) & 0x0421)) >> 1;    // Hack code to mimic TV pixel blurring
1586            else
1587               c = tmp_col[0];
1588
1589            r = ((c & 0x1f) * fade) >> 4;
1590            g = (((c & 0x3e0) >> 5) * fade) >> 4;
1591            b = (((c & 0x7c00) >> 10) * fade) >> 4;
1592
1593            bitmap.pix32(curline, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
1594            prev_colour = tmp_col[0];
1595
1596            /* average the second pixel if required, or draw it directly*/
1597            if (blurring)
1598               c = (prev_colour + tmp_col[1] - ((prev_colour ^ tmp_col[1]) & 0x0421)) >> 1;    // Hack code to mimic TV pixel blurring
1599            else
1600               c = tmp_col[1];
1601
1602            r = ((c & 0x1f) * fade) >> 4;
1603            g = (((c & 0x3e0) >> 5) * fade) >> 4;
1604            b = (((c & 0x7c00) >> 10) * fade) >> 4;
1605
1606            bitmap.pix32(curline, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
1607            prev_colour = tmp_col[1];
1608         }
1609      }
1610   }
1611
1612   g_profiler.stop();
1613}
1614
1615void snes_ppu_class::ppu_start(screen_device &screen,snes_state *state)
1616{
1617   m_screen = &screen;
1618   running_machine &machine = screen.machine();
1619   m_state = state;
1620#if SNES_LAYER_DEBUG
1621   memset(&m_debug_options, 0, sizeof(m_debug_options));
1622#endif
1623
1624   m_vram = auto_alloc_array(machine, UINT8, SNES_VRAM_SIZE);
1625   m_cgram = auto_alloc_array(machine, UINT16, SNES_CGRAM_SIZE/2);
1626   m_oam_ram = auto_alloc_array(machine, UINT16, SNES_OAM_SIZE/2);
1627
1628   /* Inititialize registers/variables */
1629   m_update_windows = 1;
1630   m_beam.latch_vert = 0;
1631   m_beam.latch_horz = 0;
1632   m_beam.current_vert = 0;
1633   m_beam.current_horz = 0;
1634   m_beam.last_visible_line = 225; /* TODO: PAL setting */
1635   m_mode = 0;
1636   m_ppu1_version = 1;  // 5C77 chip version number, read by STAT77, only '1' is known
1637   m_ppu2_version = 3;  // 5C78 chip version number, read by STAT78, only '2' & '3' encountered so far.
1638
1639   m_cgram_address = 0;
1640   m_read_ophct = 0;
1641   m_read_opvct = 0;
1642
1643   PPU_REG(VMAIN) = 0x80;
1644   // what about other regs?
1645
1646   /* Inititialize mosaic table */
1647   for (int j = 0; j < 16; j++)
1648   {
1649      for (int i = 0; i < 4096; i++)
1650         m_mosaic_table[j][i] = (i / (j + 1)) * (j + 1);
1651   }
1652
1653   /* Init VRAM */
1654   memset(m_vram, 0, SNES_VRAM_SIZE);
1655
1656   /* Init Palette RAM */
1657   memset((UINT8 *)m_cgram, 0, SNES_CGRAM_SIZE);
1658
1659   /* Init oam RAM */
1660   memset((UINT8 *)m_oam_ram, 0xff, SNES_OAM_SIZE);
1661
1662
1663   for (int i = 0; i < 2; i++)
1664   {
1665      state_save_register_item(machine, "snes_ppu", NULL, i, m_scanlines[i].enable);
1666      state_save_register_item(machine, "snes_ppu", NULL, i, m_scanlines[i].clip);
1667      state_save_register_item(machine, "snes_ppu", NULL, i, m_scanlines[i].buffer);
1668      state_save_register_item(machine, "snes_ppu", NULL, i, m_scanlines[i].priority);
1669      state_save_register_item(machine, "snes_ppu", NULL, i, m_scanlines[i].layer);
1670      state_save_register_item(machine, "snes_ppu", NULL, i, m_scanlines[i].blend_exception);
1671   }
1672
1673   for (int i = 0; i < 6; i++)
1674   {
1675      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].window1_enabled);
1676      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].window1_invert);
1677      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].window2_enabled);
1678      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].window2_invert);
1679      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].wlog_mask);
1680      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].color_math);
1681      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].charmap);
1682      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].tilemap);
1683      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].tilemap_size);
1684      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].tile_size);
1685      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].mosaic_enabled);
1686      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].main_window_enabled);
1687      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].sub_window_enabled);
1688      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].main_bg_enabled);
1689      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].sub_bg_enabled);
1690      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].hoffs);
1691      state_save_register_item(machine, "snes_ppu", NULL, i, m_layer[i].voffs);
1692
1693      state_save_register_item_array(machine, "snes_ppu", NULL, i, m_clipmasks[i]);
1694   }
1695
1696   machine.save().save_item(NAME(m_oam.address_low));
1697   machine.save().save_item(NAME(m_oam.address_high));
1698   machine.save().save_item(NAME(m_oam.saved_address_low));
1699   machine.save().save_item(NAME(m_oam.saved_address_high));
1700   machine.save().save_item(NAME(m_oam.address));
1701   machine.save().save_item(NAME(m_oam.priority_rotation));
1702   machine.save().save_item(NAME(m_oam.next_charmap));
1703   machine.save().save_item(NAME(m_oam.next_size));
1704   machine.save().save_item(NAME(m_oam.size));
1705   machine.save().save_item(NAME(m_oam.next_name_select));
1706   machine.save().save_item(NAME(m_oam.name_select));
1707   machine.save().save_item(NAME(m_oam.first_sprite));
1708   machine.save().save_item(NAME(m_oam.flip));
1709   machine.save().save_item(NAME(m_oam.write_latch));
1710
1711   machine.save().save_item(NAME(m_beam.latch_horz));
1712   machine.save().save_item(NAME(m_beam.latch_vert));
1713   machine.save().save_item(NAME(m_beam.current_horz));
1714   machine.save().save_item(NAME(m_beam.current_vert));
1715   machine.save().save_item(NAME(m_beam.last_visible_line));
1716   machine.save().save_item(NAME(m_beam.interlace_count));
1717
1718   machine.save().save_item(NAME(m_mode7.repeat));
1719   machine.save().save_item(NAME(m_mode7.hflip));
1720   machine.save().save_item(NAME(m_mode7.vflip));
1721   machine.save().save_item(NAME(m_mode7.matrix_a));
1722   machine.save().save_item(NAME(m_mode7.matrix_b));
1723   machine.save().save_item(NAME(m_mode7.matrix_c));
1724   machine.save().save_item(NAME(m_mode7.matrix_d));
1725   machine.save().save_item(NAME(m_mode7.origin_x));
1726   machine.save().save_item(NAME(m_mode7.origin_y));
1727   machine.save().save_item(NAME(m_mode7.hor_offset));
1728   machine.save().save_item(NAME(m_mode7.ver_offset));
1729   machine.save().save_item(NAME(m_mode7.extbg));
1730
1731   machine.save().save_item(NAME(m_mosaic_size));
1732   machine.save().save_item(NAME(m_clip_to_black));
1733   machine.save().save_item(NAME(m_prevent_color_math));
1734   machine.save().save_item(NAME(m_sub_add_mode));
1735   machine.save().save_item(NAME(m_bg3_priority_bit));
1736   machine.save().save_item(NAME(m_direct_color));
1737   machine.save().save_item(NAME(m_ppu_last_scroll));
1738   machine.save().save_item(NAME(m_mode7_last_scroll));
1739
1740   machine.save().save_item(NAME(m_ppu1_open_bus));
1741   machine.save().save_item(NAME(m_ppu2_open_bus));
1742   machine.save().save_item(NAME(m_ppu1_version));
1743   machine.save().save_item(NAME(m_ppu2_version));
1744   machine.save().save_item(NAME(m_window1_left));
1745   machine.save().save_item(NAME(m_window1_right));
1746   machine.save().save_item(NAME(m_window2_left));
1747   machine.save().save_item(NAME(m_window2_right));
1748
1749   machine.save().save_item(NAME(m_update_windows));
1750   machine.save().save_item(NAME(m_update_offsets));
1751   machine.save().save_item(NAME(m_update_oam_list));
1752   machine.save().save_item(NAME(m_mode));
1753   machine.save().save_item(NAME(m_interlace));
1754   machine.save().save_item(NAME(m_obj_interlace));
1755   machine.save().save_item(NAME(m_screen_brightness));
1756   machine.save().save_item(NAME(m_screen_disabled));
1757   machine.save().save_item(NAME(m_pseudo_hires));
1758   machine.save().save_item(NAME(m_color_modes));
1759   machine.save().save_item(NAME(m_stat77));
1760   machine.save().save_item(NAME(m_stat78));
1761
1762   machine.save().save_item(NAME(m_htmult));
1763   machine.save().save_item(NAME(m_cgram_address));
1764   machine.save().save_item(NAME(m_read_ophct));
1765   machine.save().save_item(NAME(m_read_opvct));
1766   machine.save().save_item(NAME(m_vram_fgr_high));
1767   machine.save().save_item(NAME(m_vram_fgr_increment));
1768   machine.save().save_item(NAME(m_vram_fgr_count));
1769   machine.save().save_item(NAME(m_vram_fgr_mask));
1770   machine.save().save_item(NAME(m_vram_fgr_shift));
1771   machine.save().save_item(NAME(m_vram_read_buffer));
1772   machine.save().save_item(NAME(m_vmadd));
1773
1774   machine.save().save_item(NAME(m_regs));
1775
1776   machine.save().save_pointer(NAME(m_vram), SNES_VRAM_SIZE);
1777   machine.save().save_pointer(NAME(m_cgram), SNES_CGRAM_SIZE/2);
1778   machine.save().save_pointer(NAME(m_oam_ram), SNES_OAM_SIZE/2);
1779}
1780
1781
1782/* CPU <-> PPU comms */
1783
1784// full graphic variables
1785static const UINT16 vram_fgr_inctab[4] = { 1, 32, 128, 128 };
1786static const UINT16 vram_fgr_inccnts[4] = { 0, 32, 64, 128 };
1787static const UINT16 vram_fgr_shiftab[4] = { 0, 5, 6, 7 };
1788
1789// utility function - latches the H/V counters.  Used by IRQ, writes to WRIO, etc.
1790void snes_ppu_class::latch_counters( running_machine &machine )
1791{
1792   m_beam.current_horz = machine.first_screen()->hpos() / m_htmult;
1793   m_beam.latch_vert = machine.first_screen()->vpos();
1794   m_beam.latch_horz = m_beam.current_horz;
1795   m_stat78 |= 0x40;   // indicate we latched
1796//  m_read_ophct = m_read_opvct = 0;    // clear read flags - 2009-08: I think we must clear these when STAT78 is read...
1797
1798//  printf("latched @ H %d V %d\n", m_beam.latch_horz, m_beam.latch_vert);
1799}
1800
1801void snes_ppu_class::dynamic_res_change( running_machine &machine )
1802{
1803   rectangle visarea = machine.first_screen()->visible_area();
1804   attoseconds_t refresh;
1805
1806   visarea.min_x = visarea.min_y = 0;
1807   visarea.max_y = m_beam.last_visible_line * m_interlace - 1;
1808   visarea.max_x = (SNES_SCR_WIDTH * 2) - 1;
1809
1810   // fixme: should compensate for SNES_DBG_VIDEO
1811   if (m_mode == 5 || m_mode == 6 || m_pseudo_hires)
1812      m_htmult = 2;
1813   else
1814      m_htmult = 1;
1815
1816   /* FIXME: does the timing changes when the gfx mode is equal to 5 or 6? */
1817   if ((m_stat78 & 0x10) == SNES_NTSC)
1818   {
1819      refresh = HZ_TO_ATTOSECONDS(DOTCLK_NTSC) * SNES_HTOTAL * SNES_VTOTAL_NTSC;
1820      machine.first_screen()->configure(SNES_HTOTAL * 2, SNES_VTOTAL_NTSC * m_interlace, visarea, refresh);
1821   }
1822   else
1823   {
1824      refresh = HZ_TO_ATTOSECONDS(DOTCLK_PAL) * SNES_HTOTAL * SNES_VTOTAL_PAL;
1825      machine.first_screen()->configure(SNES_HTOTAL * 2, SNES_VTOTAL_PAL * m_interlace, visarea, refresh);
1826   }
1827}
1828
1829/*************************************************
1830
1831 SNES VRAM accesses:
1832
1833 VRAM accesses during active display are invalid.
1834 Unlike OAM and CGRAM, they will not be written
1835 anywhere at all. Thanks to byuu's researches,
1836 the ranges where writes are invalid have been
1837 validated on hardware, as has the edge case where
1838 the S-CPU open bus can be written if the write
1839 occurs during the very last clock cycle of
1840 vblank.
1841 Our implementation could be not 100% accurate
1842 when interlace is active.
1843*************************************************/
1844
1845inline UINT32 snes_ppu_class::get_vram_address( running_machine &machine )
1846{
1847   UINT32 addr = m_vmadd;
1848
1849   if (m_vram_fgr_count)
1850   {
1851      UINT32 rem = addr & m_vram_fgr_mask;
1852      UINT32 faddr = (addr & ~m_vram_fgr_mask) + (rem >> m_vram_fgr_shift) + ((rem & (m_vram_fgr_count - 1)) << 3);
1853      return faddr << 1;
1854   }
1855
1856   return addr << 1;
1857}
1858
1859READ8_MEMBER( snes_ppu_class::vram_read )
1860{
1861   UINT8 res = 0;
1862   offset &= 0xffff; // only 64KB are present on SNES
1863
1864   if (m_screen_disabled)
1865      res = m_vram[offset];
1866   else
1867   {
1868      UINT16 v = m_screen->vpos();
1869      UINT16 h = m_screen->hpos();
1870      UINT16 ls = (((m_stat78 & 0x10) == SNES_NTSC ? 525 : 625) >> 1) - 1;
1871
1872      if (m_interlace == 2)
1873         ls++;
1874
1875      if (v == ls && h == 1362)
1876         res = 0;
1877      else if (v < m_beam.last_visible_line - 1)
1878         res = 0;
1879      else if (v == m_beam.last_visible_line - 1)
1880      {
1881         if (h == 1362)
1882            res = m_vram[offset];
1883         else
1884         {
1885            //printf("%d %d VRAM read, CHECK!\n",h,v);
1886            res = 0;
1887         }
1888      }
1889      else
1890         res = m_vram[offset];
1891   }
1892   return res;
1893}
1894
1895WRITE8_MEMBER( snes_ppu_class::vram_write )
1896{
1897   offset &= 0xffff; // only 64KB are present on SNES, Robocop 3 relies on this
1898
1899   if (m_screen_disabled)
1900      m_vram[offset] = data;
1901   else
1902   {
1903      UINT16 v = m_screen->vpos();
1904      UINT16 h = m_screen->hpos();
1905      if (v == 0)
1906      {
1907         if (h <= 4)
1908            m_vram[offset] = data;
1909         else if (h == 6)
1910            m_vram[offset] = m_state->snes_open_bus_r(space, 0);
1911         else
1912         {
1913            //printf("%d %d VRAM write, CHECK!\n",h,v);
1914            //no write
1915         }
1916      }
1917      else if (v < m_beam.last_visible_line)
1918      {
1919         //printf("%d %d VRAM write, CHECK!\n",h,v);
1920         //no write
1921      }
1922      else if (v == m_beam.last_visible_line)
1923      {
1924         if (h <= 4)
1925         {
1926            //printf("%d %d VRAM write, CHECK!\n",h,v);
1927            //no write
1928         }
1929         else
1930            m_vram[offset] = data;
1931      }
1932      else
1933         m_vram[offset] = data;
1934   }
1935}
1936
1937/*************************************************
1938
1939 SNES OAM accesses:
1940
1941 OAM accesses during active display are allowed.
1942 The actual address varies during rendering, as the
1943 PPU reads in data itself for processing.
1944 Unfortunately, no one has been able (yet) to
1945 determine how this works. The only known game to
1946 actually access OAM during active display is
1947 Uniracers and it expects accesses to map to
1948 offset 0x0218. Hence, following byuu's choice
1949 we rerouted OAM accesses during active display
1950 to 0x0218 (0x010c in our snes_oam).
1951 This is a hack, but it is more accurate than
1952 writing to the 'expected' address set by
1953 $2102,$2103.
1954
1955 Notice that, since PPU_REG(OAMDATA) is never
1956 read/written directly, we use it as an index
1957 to choose the high/low byte of the snes_oam word.
1958*************************************************/
1959
1960READ8_MEMBER( snes_ppu_class::oam_read )
1961{
1962   offset &= 0x1ff;
1963
1964   if (offset & 0x100)
1965      offset &= 0x10f;
1966
1967   if (!m_screen_disabled)
1968   {
1969      UINT16 v = m_screen->vpos();
1970
1971      if (v < m_beam.last_visible_line)
1972         offset = 0x010c;
1973   }
1974
1975   return (m_oam_ram[offset] >> (PPU_REG(OAMDATA) << 3)) & 0xff;
1976}
1977
1978WRITE8_MEMBER( snes_ppu_class::oam_write )
1979{
1980   offset &= 0x1ff;
1981
1982   if (offset & 0x100)
1983      offset &= 0x10f;
1984
1985   if (!m_screen_disabled)
1986   {
1987      UINT16 v = m_screen->vpos();
1988
1989      if (v < m_beam.last_visible_line)
1990         offset = 0x010c;
1991   }
1992
1993   if (!(PPU_REG(OAMDATA)))
1994      m_oam_ram[offset] = (m_oam_ram[offset] & 0xff00) | (data << 0);
1995   else
1996      m_oam_ram[offset] = (m_oam_ram[offset] & 0x00ff) | (data << 8);
1997}
1998
1999/*************************************************
2000
2001 SNES CGRAM accesses:
2002
2003 CGRAM writes during hblank are valid. During
2004 active display, the actual address the data
2005 is written to varies, as the PPU itself changes
2006 the address. Like OAM, it is not known the exact
2007 algorithm used, but no commercial software seems
2008 to attempt this. While byuu, in his emu, maps
2009 those accesses to 0x01ff, because it is more
2010 accurate to invalidate the 'expected' address
2011 than not, MESS has issues if we don't write to
2012 the expected address (see e.g. Tokimeki Memorial).
2013 This is because writes should work during hblank
2014 (so that the game can produce color fading), but
2015 ends up not working with the conditions below.
2016 Hence, for the moment, we only document the
2017 solution adopted by BSNES without enabling it.
2018*************************************************/
2019
2020READ8_MEMBER( snes_ppu_class::cgram_read )
2021{
2022   UINT8 res = 0;
2023   offset &= 0x1ff;
2024
2025#if 0
2026   if (!m_screen_disabled)
2027   {
2028      UINT16 v = m_screen->vpos();
2029      UINT16 h = m_screen->hpos();
2030
2031      if (v < m_beam.last_visible_line && h >= 128 && h < 1096)
2032         offset = 0x1ff;
2033   }
2034#endif
2035
2036   res = ((UINT8 *)m_cgram)[offset];
2037
2038   // CGRAM palette data format is 15-bits (0,bbbbb,ggggg,rrrrr).
2039   // Highest bit is simply ignored.
2040   if (offset & 0x01)
2041      res &= 0x7f;
2042
2043   return res;
2044}
2045
2046WRITE8_MEMBER( snes_ppu_class::cgram_write )
2047{
2048   offset &= 0x1ff;
2049
2050#if 0
2051   // FIXME: this currently breaks some games (e.g. Tokimeki Memorial),
2052   // even if it's expected to be more accurate than allowing for
2053   // writes to the cgram address
2054   if (!m_screen_disabled)
2055   {
2056      UINT16 v = m_screen->vpos();
2057      UINT16 h = m_screen->hpos();
2058
2059      if (v < m_beam.last_visible_line && h >= 128 && h < 1096)
2060         offset = 0x1ff;
2061   }
2062#endif
2063
2064   // CGRAM palette data format is 15-bits (0,bbbbb,ggggg,rrrrr).
2065   // Highest bit is simply ignored.
2066   if (offset & 0x01)
2067      data &= 0x7f;
2068
2069   ((UINT8 *)m_cgram)[offset] = data;
2070}
2071
2072UINT8 snes_ppu_class::read(address_space &space, UINT32 offset, UINT8 wrio_bit7)
2073{
2074   UINT8 value;
2075
2076   switch (offset)
2077   {
2078      case OAMDATA:   /* 21xy for x=0,1,2 and y=4,5,6,8,9,a returns PPU1 open bus*/
2079      case BGMODE:
2080      case MOSAIC:
2081      case BG2SC:
2082      case BG3SC:
2083      case BG4SC:
2084      case BG4VOFS:
2085      case VMAIN:
2086      case VMADDL:
2087      case VMDATAL:
2088      case VMDATAH:
2089      case M7SEL:
2090      case W34SEL:
2091      case WOBJSEL:
2092      case WH0:
2093      case WH2:
2094      case WH3:
2095      case WBGLOG:
2096         return m_ppu1_open_bus;
2097
2098      case MPYL:      /* Multiplication result (low) */
2099         {
2100            /* Perform 16bit * 8bit multiply */
2101            UINT32 c = (INT16)m_mode7.matrix_a * (INT8)(m_mode7.matrix_b >> 8);
2102            m_ppu1_open_bus = c & 0xff;
2103            return m_ppu1_open_bus;
2104         }
2105      case MPYM:      /* Multiplication result (mid) */
2106         {
2107            /* Perform 16bit * 8bit multiply */
2108            UINT32 c = (INT16)m_mode7.matrix_a * (INT8)(m_mode7.matrix_b >> 8);
2109            m_ppu1_open_bus = (c >> 8) & 0xff;
2110            return m_ppu1_open_bus;
2111         }
2112      case MPYH:      /* Multiplication result (high) */
2113         {
2114            /* Perform 16bit * 8bit multiply */
2115            UINT32 c = (INT16)m_mode7.matrix_a * (INT8)(m_mode7.matrix_b >> 8);
2116            m_ppu1_open_bus = (c >> 16) & 0xff;
2117            return m_ppu1_open_bus;
2118         }
2119      case SLHV:      /* Software latch for H/V counter */
2120         latch_counters(space.machine());
2121         return m_state->snes_open_bus_r(space, 0);       /* Return value is meaningless */
2122      case ROAMDATA:  /* Read data from OAM (DR) */
2123         m_ppu1_open_bus = oam_read(space, m_oam.address);
2124         PPU_REG(OAMDATA) = (PPU_REG(OAMDATA) + 1) % 2;
2125         if (!PPU_REG(OAMDATA))
2126         {
2127            m_oam.address++;
2128            m_oam.address &= 0x1ff;
2129            m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
2130         }
2131         return m_ppu1_open_bus;
2132      case RVMDATAL:  /* Read data from VRAM (low) */
2133         {
2134            UINT32 addr = get_vram_address(space.machine());
2135            m_ppu1_open_bus = m_vram_read_buffer & 0xff;
2136
2137            if (!m_vram_fgr_high)
2138            {
2139               m_vram_read_buffer = vram_read(space, addr);
2140               m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
2141
2142               m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
2143            }
2144
2145            return m_ppu1_open_bus;
2146         }
2147      case RVMDATAH:  /* Read data from VRAM (high) */
2148         {
2149            UINT32 addr = get_vram_address(space.machine());
2150            m_ppu1_open_bus = (m_vram_read_buffer >> 8) & 0xff;
2151
2152            if (m_vram_fgr_high)
2153            {
2154               m_vram_read_buffer = vram_read(space, addr);
2155               m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
2156
2157               m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
2158            }
2159
2160            return m_ppu1_open_bus;
2161         }
2162      case RCGDATA:   /* Read data from CGRAM */
2163         if (!(m_cgram_address & 0x01))
2164            m_ppu2_open_bus = cgram_read(space, m_cgram_address);
2165         else
2166         {
2167            m_ppu2_open_bus &= 0x80;
2168            m_ppu2_open_bus |= cgram_read(space, m_cgram_address) & 0x7f;
2169         }
2170
2171         m_cgram_address = (m_cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
2172         return m_ppu2_open_bus;
2173      case OPHCT:     /* Horizontal counter data by ext/soft latch */
2174         if (m_read_ophct)
2175         {
2176            m_ppu2_open_bus &= 0xfe;
2177            m_ppu2_open_bus |= (m_beam.latch_horz >> 8) & 0x01;
2178         }
2179         else
2180         {
2181            m_ppu2_open_bus = m_beam.latch_horz & 0xff;
2182         }
2183         m_read_ophct ^= 1;
2184         return m_ppu2_open_bus;
2185      case OPVCT:     /* Vertical counter data by ext/soft latch */
2186         if (m_read_opvct)
2187         {
2188            m_ppu2_open_bus &= 0xfe;
2189            m_ppu2_open_bus |= (m_beam.latch_vert >> 8) & 0x01;
2190         }
2191         else
2192         {
2193            m_ppu2_open_bus = m_beam.latch_vert & 0xff;
2194         }
2195         m_read_opvct ^= 1;
2196         return m_ppu2_open_bus;
2197      case STAT77:    /* PPU status flag and version number */
2198         value = m_stat77 & 0xc0; // 0x80 & 0x40 are Time Over / Range Over Sprite flags, set by the video code
2199         // 0x20 - Master/slave mode select. Little is known about this bit. We always seem to read back 0 here.
2200         value |= (m_ppu1_open_bus & 0x10);
2201         value |= (m_ppu1_version & 0x0f);
2202         m_stat77 = value;  // not sure if this is needed...
2203         m_ppu1_open_bus = value;
2204         return m_ppu1_open_bus;
2205      case STAT78:    /* PPU status flag and version number */
2206         m_read_ophct = 0;
2207         m_read_opvct = 0;
2208         if (wrio_bit7)
2209            m_stat78 &= ~0x40; //clear ext latch if bit 7 of WRIO is set
2210         m_stat78 = (m_stat78 & ~0x2f) | (m_ppu2_open_bus & 0x20) | (m_ppu2_version & 0x0f);
2211         m_ppu2_open_bus = m_stat78;
2212         return m_ppu2_open_bus;
2213   }
2214
2215   /* note: remaining registers (Namely TM in Super Kick Boxing) returns MDR open bus, not PPU Open Bus! */
2216   return m_state->snes_open_bus_r(space, 0);
2217}
2218
2219
2220void snes_ppu_class::write(address_space &space, UINT32 offset, UINT8 data)
2221{
2222   switch (offset)
2223   {
2224      case INIDISP:   /* Initial settings for screen */
2225         if ((m_screen_disabled & 0x80) && (!(data & 0x80))) //a 1->0 force blank transition causes a reset OAM address
2226         {
2227            space.write_byte(OAMADDL, m_oam.saved_address_low);
2228            space.write_byte(OAMADDH, m_oam.saved_address_high);
2229            m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
2230         }
2231         m_screen_disabled = data & 0x80;
2232         m_screen_brightness = (data & 0x0f) + 1;
2233         break;
2234      case OBSEL:     /* Object size and data area designation */
2235         m_oam.next_charmap = (data & 0x03) << 1;
2236         m_oam.next_name_select = (((data & 0x18) >> 3) * 0x1000) << 1;
2237         m_oam.next_size = (data & 0xe0) >> 5;
2238         break;
2239      case OAMADDL:   /* Address for accessing OAM (low) */
2240         m_oam.saved_address_low = data;
2241         m_oam.address = (m_oam.address & 0xff00) + data;
2242         m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
2243         PPU_REG(OAMDATA) = 0;
2244         break;
2245      case OAMADDH:   /* Address for accessing OAM (high) */
2246         m_oam.saved_address_high = data;
2247         m_oam.address = (m_oam.address & 0x00ff) | ((data & 0x01) << 8);
2248         m_oam.priority_rotation = BIT(data, 7);
2249         m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
2250         PPU_REG(OAMDATA) = 0;
2251         break;
2252      case OAMDATA:   /* Data for OAM write (DW) */
2253         if (m_oam.address >= 0x100)
2254            oam_write(space, m_oam.address, data);
2255         else
2256         {
2257            if (!PPU_REG(OAMDATA))
2258               m_oam.write_latch = data;
2259            else
2260            {
2261               // in this case, we not only write data to the upper byte of the word,
2262               // but also m_oam.write_latch to the lower byte (recall that
2263               // PPU_REG(OAMDATA) is used to select high/low byte)
2264               oam_write(space, m_oam.address, data);
2265               PPU_REG(OAMDATA) = 0;
2266               oam_write(space, m_oam.address, m_oam.write_latch);
2267               PPU_REG(OAMDATA) = 1;
2268            }
2269         }
2270         PPU_REG(OAMDATA) = (PPU_REG(OAMDATA) + 1) % 2;
2271         if (!PPU_REG(OAMDATA))
2272         {
2273            m_oam.address++;
2274            m_oam.address &= 0x1ff;
2275            m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
2276         }
2277         return;
2278      case BGMODE:    /* BG mode and character size settings */
2279         m_mode = data & 0x07;
2280         dynamic_res_change(space.machine());
2281         m_bg3_priority_bit = BIT(data, 3);
2282         m_layer[SNES_BG1].tile_size = BIT(data, 4);
2283         m_layer[SNES_BG2].tile_size = BIT(data, 5);
2284         m_layer[SNES_BG3].tile_size = BIT(data, 6);
2285         m_layer[SNES_BG4].tile_size = BIT(data, 7);
2286         m_update_offsets = 1;
2287         break;
2288      case MOSAIC:    /* Size and screen designation for mosaic */
2289         m_mosaic_size = (data & 0xf0) >> 4;
2290         m_layer[SNES_BG1].mosaic_enabled = BIT(data, 0);
2291         m_layer[SNES_BG2].mosaic_enabled = BIT(data, 1);
2292         m_layer[SNES_BG3].mosaic_enabled = BIT(data, 2);
2293         m_layer[SNES_BG4].mosaic_enabled = BIT(data, 3);
2294         break;
2295      case BG1SC:     /* Address for storing SC data BG1 SC size designation */
2296      case BG2SC:     /* Address for storing SC data BG2 SC size designation  */
2297      case BG3SC:     /* Address for storing SC data BG3 SC size designation  */
2298      case BG4SC:     /* Address for storing SC data BG4 SC size designation  */
2299         m_layer[offset - BG1SC].tilemap = data & 0xfc;
2300         m_layer[offset - BG1SC].tilemap_size = data & 0x3;
2301         break;
2302      case BG12NBA:   /* Address for BG 1 and 2 character data */
2303         m_layer[SNES_BG1].charmap = (data & 0x0f);
2304         m_layer[SNES_BG2].charmap = (data & 0xf0) >> 4;
2305         break;
2306      case BG34NBA:   /* Address for BG 3 and 4 character data */
2307         m_layer[SNES_BG3].charmap = (data & 0x0f);
2308         m_layer[SNES_BG4].charmap = (data & 0xf0) >> 4;
2309         break;
2310
2311      // Anomie says "H Current = (Byte<<8) | (Prev&~7) | ((Current>>8)&7); V Current = (Current<<8) | Prev;" and Prev is shared by all scrolls but in Mode 7!
2312      case BG1HOFS:   /* BG1 - horizontal scroll (DW) */
2313         /* In Mode 0->6 we use ppu_last_scroll as Prev */
2314         m_layer[SNES_BG1].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG1].hoffs >> 8) & 7);
2315         m_ppu_last_scroll = data;
2316         /* In Mode 7 we use mode7_last_scroll as Prev */
2317         m_mode7.hor_offset = (data << 8) | (m_mode7_last_scroll & ~7) | ((m_mode7.hor_offset >> 8) & 7);
2318         m_mode7_last_scroll = data;
2319         m_update_offsets = 1;
2320         return;
2321      case BG1VOFS:   /* BG1 - vertical scroll (DW) */
2322         /* In Mode 0->6 we use ppu_last_scroll as Prev */
2323         m_layer[SNES_BG1].voffs = (data << 8) | m_ppu_last_scroll;
2324         m_ppu_last_scroll = data;
2325         /* In Mode 7 we use mode7_last_scroll as Prev */
2326         m_mode7.ver_offset = (data << 8) | m_mode7_last_scroll;
2327         m_mode7_last_scroll = data;
2328         m_update_offsets = 1;
2329         return;
2330      case BG2HOFS:   /* BG2 - horizontal scroll (DW) */
2331         m_layer[SNES_BG2].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG2].hoffs >> 8) & 7);
2332         m_ppu_last_scroll = data;
2333         m_update_offsets = 1;
2334         return;
2335      case BG2VOFS:   /* BG2 - vertical scroll (DW) */
2336         m_layer[SNES_BG2].voffs = (data << 8) | (m_ppu_last_scroll);
2337         m_ppu_last_scroll = data;
2338         m_update_offsets = 1;
2339         return;
2340      case BG3HOFS:   /* BG3 - horizontal scroll (DW) */
2341         m_layer[SNES_BG3].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG3].hoffs >> 8) & 7);
2342         m_ppu_last_scroll = data;
2343         m_update_offsets = 1;
2344         return;
2345      case BG3VOFS:   /* BG3 - vertical scroll (DW) */
2346         m_layer[SNES_BG3].voffs = (data << 8) | (m_ppu_last_scroll);
2347         m_ppu_last_scroll = data;
2348         m_update_offsets = 1;
2349         return;
2350      case BG4HOFS:   /* BG4 - horizontal scroll (DW) */
2351         m_layer[SNES_BG4].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG4].hoffs >> 8) & 7);
2352         m_ppu_last_scroll = data;
2353         m_update_offsets = 1;
2354         return;
2355      case BG4VOFS:   /* BG4 - vertical scroll (DW) */
2356         m_layer[SNES_BG4].voffs = (data << 8) | (m_ppu_last_scroll);
2357         m_ppu_last_scroll = data;
2358         m_update_offsets = 1;
2359         return;
2360      case VMAIN:     /* VRAM address increment value designation */
2361         m_vram_fgr_high = (data & 0x80);
2362         m_vram_fgr_increment = vram_fgr_inctab[data & 3];
2363
2364         if (data & 0xc)
2365         {
2366            int md = (data & 0xc) >> 2;
2367
2368            m_vram_fgr_count = vram_fgr_inccnts[md];         // 0x20, 0x40, 0x80
2369            m_vram_fgr_mask = (m_vram_fgr_count * 8) - 1; // 0xff, 0x1ff, 0x2ff
2370            m_vram_fgr_shift = vram_fgr_shiftab[md];         // 5, 6, 7
2371         }
2372         else
2373         {
2374            m_vram_fgr_count = 0;
2375         }
2376//          printf("VMAIN: high %x inc %x count %x mask %x shift %x\n", m_vram_fgr_high, m_vram_fgr_increment, m_vram_fgr_count, m_vram_fgr_mask, m_vram_fgr_shift);
2377         break;
2378      case VMADDL:    /* Address for VRAM read/write (low) */
2379         {
2380            UINT32 addr;
2381            m_vmadd = (m_vmadd & 0xff00) | (data << 0);
2382            addr = get_vram_address(space.machine());
2383            m_vram_read_buffer = vram_read(space, addr);
2384            m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
2385         }
2386         break;
2387      case VMADDH:    /* Address for VRAM read/write (high) */
2388         {
2389            UINT32 addr;
2390            m_vmadd = (m_vmadd & 0x00ff) | (data << 8);
2391            addr = get_vram_address(space.machine());
2392            m_vram_read_buffer = vram_read(space, addr);
2393            m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
2394         }
2395         break;
2396      case VMDATAL:   /* 2118: Data for VRAM write (low) */
2397         {
2398            UINT32 addr = get_vram_address(space.machine());
2399            vram_write(space, addr, data);
2400
2401            if (!m_vram_fgr_high)
2402               m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
2403         }
2404         return;
2405      case VMDATAH:   /* 2119: Data for VRAM write (high) */
2406         {
2407            UINT32 addr = get_vram_address(space.machine());
2408            vram_write(space, addr + 1, data);
2409
2410            if (m_vram_fgr_high)
2411               m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
2412         }
2413         return;
2414      case M7SEL:     /* Mode 7 initial settings */
2415         m_mode7.repeat = (data >> 6) & 3;
2416         m_mode7.vflip  = BIT(data, 1);
2417         m_mode7.hflip  = BIT(data, 0);
2418         break;
2419      /* As per Anomie's doc: Reg = (Current<<8) | Prev; and there is only one Prev, shared by these matrix regs and Mode 7 scroll regs */
2420      case M7A:       /* Mode 7 COS angle/x expansion (DW) */
2421         m_mode7.matrix_a = m_mode7_last_scroll + (data << 8);
2422         m_mode7_last_scroll = data;
2423         break;
2424      case M7B:       /* Mode 7 SIN angle/ x expansion (DW) */
2425         m_mode7.matrix_b = m_mode7_last_scroll + (data << 8);
2426         m_mode7_last_scroll = data;
2427         break;
2428      case M7C:       /* Mode 7 SIN angle/y expansion (DW) */
2429         m_mode7.matrix_c = m_mode7_last_scroll + (data << 8);
2430         m_mode7_last_scroll = data;
2431         break;
2432      case M7D:       /* Mode 7 COS angle/y expansion (DW) */
2433         m_mode7.matrix_d = m_mode7_last_scroll + (data << 8);
2434         m_mode7_last_scroll = data;
2435         break;
2436      case M7X:       /* Mode 7 x center position (DW) */
2437         m_mode7.origin_x = m_mode7_last_scroll + (data << 8);
2438         m_mode7_last_scroll = data;
2439         break;
2440      case M7Y:       /* Mode 7 y center position (DW) */
2441         m_mode7.origin_y = m_mode7_last_scroll + (data << 8);
2442         m_mode7_last_scroll = data;
2443         break;
2444      case CGADD:     /* Initial address for colour RAM writing */
2445         /* CGRAM is 16-bit, but when reading/writing we treat it as 8-bit, so we need to double the address */
2446         m_cgram_address = data << 1;
2447         break;
2448      case CGDATA:    /* Data for colour RAM */
2449         cgram_write(space, m_cgram_address, data);
2450         m_cgram_address = (m_cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
2451         break;
2452      case W12SEL:    /* Window mask settings for BG1-2 */
2453         if (data != PPU_REG(W12SEL))
2454         {
2455            m_layer[SNES_BG1].window1_invert  = BIT(data, 0);
2456            m_layer[SNES_BG1].window1_enabled = BIT(data, 1);
2457            m_layer[SNES_BG1].window2_invert  = BIT(data, 2);
2458            m_layer[SNES_BG1].window2_enabled = BIT(data, 3);
2459            m_layer[SNES_BG2].window1_invert  = BIT(data, 4);
2460            m_layer[SNES_BG2].window1_enabled = BIT(data, 5);
2461            m_layer[SNES_BG2].window2_invert  = BIT(data, 6);
2462            m_layer[SNES_BG2].window2_enabled = BIT(data, 7);
2463            m_update_windows = 1;
2464         }
2465         break;
2466      case W34SEL:    /* Window mask settings for BG3-4 */
2467         if (data != PPU_REG(W34SEL))
2468         {
2469            m_layer[SNES_BG3].window1_invert  = BIT(data, 0);
2470            m_layer[SNES_BG3].window1_enabled = BIT(data, 1);
2471            m_layer[SNES_BG3].window2_invert  = BIT(data, 2);
2472            m_layer[SNES_BG3].window2_enabled = BIT(data, 3);
2473            m_layer[SNES_BG4].window1_invert  = BIT(data, 4);
2474            m_layer[SNES_BG4].window1_enabled = BIT(data, 5);
2475            m_layer[SNES_BG4].window2_invert  = BIT(data, 6);
2476            m_layer[SNES_BG4].window2_enabled = BIT(data, 7);
2477            m_update_windows = 1;
2478         }
2479         break;
2480      case WOBJSEL:   /* Window mask settings for objects */
2481         if (data != PPU_REG(WOBJSEL))
2482         {
2483            m_layer[SNES_OAM].window1_invert  = BIT(data, 0);
2484            m_layer[SNES_OAM].window1_enabled = BIT(data, 1);
2485            m_layer[SNES_OAM].window2_invert  = BIT(data, 2);
2486            m_layer[SNES_OAM].window2_enabled = BIT(data, 3);
2487            m_layer[SNES_COLOR].window1_invert  = BIT(data, 4);
2488            m_layer[SNES_COLOR].window1_enabled = BIT(data, 5);
2489            m_layer[SNES_COLOR].window2_invert  = BIT(data, 6);
2490            m_layer[SNES_COLOR].window2_enabled = BIT(data, 7);
2491            m_update_windows = 1;
2492         }
2493         break;
2494      case WH0:       /* Window 1 left position */
2495         if (data != PPU_REG(WH0))
2496         {
2497            m_window1_left = data;
2498            m_update_windows = 1;
2499         }
2500         break;
2501      case WH1:       /* Window 1 right position */
2502         if (data != PPU_REG(WH1))
2503         {
2504            m_window1_right = data;
2505            m_update_windows = 1;
2506         }
2507         break;
2508      case WH2:       /* Window 2 left position */
2509         if (data != PPU_REG(WH2))
2510         {
2511            m_window2_left = data;
2512            m_update_windows = 1;
2513         }
2514         break;
2515      case WH3:       /* Window 2 right position */
2516         if (data != PPU_REG(WH3))
2517         {
2518            m_window2_right = data;
2519            m_update_windows = 1;
2520         }
2521         break;
2522      case WBGLOG:    /* Window mask logic for BG's */
2523         if (data != PPU_REG(WBGLOG))
2524         {
2525            m_layer[SNES_BG1].wlog_mask = data & 0x03;
2526            m_layer[SNES_BG2].wlog_mask = (data & 0x0c) >> 2;
2527            m_layer[SNES_BG3].wlog_mask = (data & 0x30) >> 4;
2528            m_layer[SNES_BG4].wlog_mask = (data & 0xc0) >> 6;
2529            m_update_windows = 1;
2530         }
2531         break;
2532      case WOBJLOG:   /* Window mask logic for objects */
2533         if (data != PPU_REG(WOBJLOG))
2534         {
2535            m_layer[SNES_OAM].wlog_mask = data & 0x03;
2536            m_layer[SNES_COLOR].wlog_mask = (data & 0x0c) >> 2;
2537            m_update_windows = 1;
2538         }
2539         break;
2540      case TM:        /* Main screen designation */
2541         m_layer[SNES_BG1].main_bg_enabled = BIT(data, 0);
2542         m_layer[SNES_BG2].main_bg_enabled = BIT(data, 1);
2543         m_layer[SNES_BG3].main_bg_enabled = BIT(data, 2);
2544         m_layer[SNES_BG4].main_bg_enabled = BIT(data, 3);
2545         m_layer[SNES_OAM].main_bg_enabled = BIT(data, 4);
2546         break;
2547      case TS:        /* Subscreen designation */
2548         m_layer[SNES_BG1].sub_bg_enabled = BIT(data, 0);
2549         m_layer[SNES_BG2].sub_bg_enabled = BIT(data, 1);
2550         m_layer[SNES_BG3].sub_bg_enabled = BIT(data, 2);
2551         m_layer[SNES_BG4].sub_bg_enabled = BIT(data, 3);
2552         m_layer[SNES_OAM].sub_bg_enabled = BIT(data, 4);
2553         break;
2554      case TMW:       /* Window mask for main screen designation */
2555         m_layer[SNES_BG1].main_window_enabled = BIT(data, 0);
2556         m_layer[SNES_BG2].main_window_enabled = BIT(data, 1);
2557         m_layer[SNES_BG3].main_window_enabled = BIT(data, 2);
2558         m_layer[SNES_BG4].main_window_enabled = BIT(data, 3);
2559         m_layer[SNES_OAM].main_window_enabled = BIT(data, 4);
2560         break;
2561      case TSW:       /* Window mask for subscreen designation */
2562         m_layer[SNES_BG1].sub_window_enabled = BIT(data, 0);
2563         m_layer[SNES_BG2].sub_window_enabled = BIT(data, 1);
2564         m_layer[SNES_BG3].sub_window_enabled = BIT(data, 2);
2565         m_layer[SNES_BG4].sub_window_enabled = BIT(data, 3);
2566         m_layer[SNES_OAM].sub_window_enabled = BIT(data, 4);
2567         break;
2568      case CGWSEL:    /* Initial settings for Fixed colour addition or screen addition */
2569         m_clip_to_black = (data >> 6) & 0x03;
2570         m_prevent_color_math = (data >> 4) & 0x03;
2571         m_sub_add_mode = BIT(data, 1);
2572         m_direct_color = BIT(data, 0);
2573#ifdef SNES_DBG_REG_W
2574         if ((data & 0x2) != (PPU_REG(CGWSEL) & 0x2))
2575            mame_printf_debug("Add/Sub Layer: %s\n", ((data & 0x2) >> 1) ? "Subscreen" : "Fixed colour");
2576#endif
2577         break;
2578      case CGADSUB:   /* Addition/Subtraction designation for each screen */
2579         m_color_modes = data & 0xc0;
2580         m_layer[SNES_BG1].color_math = BIT(data, 0);
2581         m_layer[SNES_BG2].color_math = BIT(data, 1);
2582         m_layer[SNES_BG3].color_math = BIT(data, 2);
2583         m_layer[SNES_BG4].color_math = BIT(data, 3);
2584         m_layer[SNES_OAM].color_math = BIT(data, 4);
2585         m_layer[SNES_COLOR].color_math = BIT(data, 5);
2586         break;
2587      case COLDATA:   /* Fixed colour data for fixed colour addition/subtraction */
2588         {
2589            /* Store it in the extra space we made in the CGRAM. It doesn't really go there, but it's as good a place as any. */
2590            UINT8 r, g, b;
2591
2592            /* Get existing value. */
2593            r = m_cgram[FIXED_COLOUR] & 0x1f;
2594            g = (m_cgram[FIXED_COLOUR] & 0x3e0) >> 5;
2595            b = (m_cgram[FIXED_COLOUR] & 0x7c00) >> 10;
2596            /* Set new value */
2597            if (data & 0x20)
2598               r = data & 0x1f;
2599            if (data & 0x40)
2600               g = data & 0x1f;
2601            if (data & 0x80)
2602               b = data & 0x1f;
2603            m_cgram[FIXED_COLOUR] = (r | (g << 5) | (b << 10));
2604         } break;
2605      case SETINI:    /* Screen mode/video select */
2606         m_interlace = (data & 0x01) ? 2 : 1;
2607         m_obj_interlace = (data & 0x02) ? 2 : 1;
2608         m_beam.last_visible_line = (data & 0x04) ? 240 : 225;
2609         m_pseudo_hires = BIT(data, 3);
2610         m_mode7.extbg = BIT(data, 6);
2611         dynamic_res_change(space.machine());
2612#ifdef SNES_DBG_REG_W
2613         if ((data & 0x8) != (PPU_REG(SETINI) & 0x8))
2614            mame_printf_debug("Pseudo 512 mode: %s\n", (data & 0x8) ? "on" : "off");
2615#endif
2616         break;
2617      }
2618
2619   PPU_REG(offset) = data;
2620}
2621
2622/***** Debug Functions *****/
2623
2624#if SNES_LAYER_DEBUG
2625
2626#define DEBUG_TOGGLE(bit, debug_settings, MSG1, MSG2) \
2627   if (BIT(toggles, bit) && !debug_settings)       \
2628   {                                               \
2629      debug_settings = 1;                       \
2630      popmessage MSG1;                          \
2631   }                                               \
2632   else if (!BIT(toggles, bit) && debug_settings)  \
2633   {                                               \
2634      debug_settings = 0;                       \
2635      popmessage MSG2;                          \
2636   }
2637
2638UINT8 snes_ppu_class::dbg_video( running_machine &machine, UINT16 curline )
2639{
2640   int i;
2641   UINT8 toggles = machine.root_device().ioport("DEBUG1")->read_safe(0);
2642   m_debug_options.select_pri[SNES_BG1] = (toggles & 0x03);
2643   m_debug_options.select_pri[SNES_BG2] = (toggles & 0x0c) >> 2;
2644   m_debug_options.select_pri[SNES_BG3] = (toggles & 0x30) >> 4;
2645   m_debug_options.select_pri[SNES_BG4] = (toggles & 0xc0) >> 6;
2646
2647   toggles = machine.root_device().ioport("DEBUG2")->read_safe(0);
2648   for (i = 0; i < 4; i++)
2649      DEBUG_TOGGLE(i, m_debug_options.bg_disabled[i], ("Debug: Disabled BG%d.\n", i + 1), ("Debug: Enabled BG%d.\n", i + 1))
2650   DEBUG_TOGGLE(4, m_debug_options.bg_disabled[SNES_OAM], ("Debug: Disabled OAM.\n"), ("Debug: Enabled OAM.\n"))
2651   DEBUG_TOGGLE(5, m_debug_options.draw_subscreen, ("Debug: Switched screens.\n"), ("Debug: Switched screens.\n"))
2652   DEBUG_TOGGLE(6, m_debug_options.colormath_disabled, ("Debug: Disabled Color Math.\n"), ("Debug: Enabled Color Math.\n"))
2653   DEBUG_TOGGLE(7, m_debug_options.windows_disabled, ("Debug: Disabled Window Masks.\n"), ("Debug: Enabled Window Masks.\n"))
2654
2655   toggles = machine.root_device().ioport("DEBUG4")->read_safe(0);
2656   for (i = 0; i < 8; i++)
2657      DEBUG_TOGGLE(i, m_debug_options.mode_disabled[i], ("Debug: Disabled Mode %d drawing.\n", i), ("Debug: Enabled Mode %d drawing.\n", i))
2658
2659   toggles = machine.root_device().ioport("DEBUG3")->read_safe(0);
2660   DEBUG_TOGGLE(2, m_debug_options.mosaic_disabled, ("Debug: Disabled Mosaic.\n"), ("Debug: Enabled Mosaic.\n"))
2661   m_debug_options.sprite_reversed = BIT(toggles, 7);
2662   m_debug_options.select_pri[SNES_OAM] = (toggles & 0x70) >> 4;
2663
2664#ifdef MAME_DEBUG
2665   /* Once per frame, log video properties */
2666   if (curline == 1)
2667   {
2668      static const char WINLOGIC[4] = { '|', '&', '^', '!' };
2669
2670      logerror("%s", m_debug_options.windows_disabled?" ":"W");
2671      logerror("%s1 %s%s%s%s%s%c%s%s%d%s %d %4X %4X",
2672            m_debug_options.bg_disabled[0]?" ":"*",
2673            (PPU_REG(TM) & 0x1)?"M":" ",
2674            (PPU_REG(TS) & 0x1)?"S":" ",
2675            (PPU_REG(CGADSUB) & 0x1)?"B":" ",
2676            (PPU_REG(TMW) & 0x1)?"m":" ",
2677            (PPU_REG(TSW) & 0x1)?"s":" ",
2678            WINLOGIC[(PPU_REG(WBGLOG) & 0x3)],
2679            (PPU_REG(W12SEL) & 0x2)?((PPU_REG(W12SEL) & 0x1)?"o":"i"):" ",
2680            (PPU_REG(W12SEL) & 0x8)?((PPU_REG(W12SEL) & 0x4)?"o":"i"):" ",
2681            m_layer[SNES_BG1].tile_size + 1,
2682            (PPU_REG(MOSAIC) & 0x1)?"m":" ",
2683            PPU_REG(BG1SC) & 0x3,
2684            (PPU_REG(BG1SC) & 0xfc) << 9,
2685            m_layer[SNES_BG1].charmap << 13);
2686      logerror("%s2 %s%s%s%s%s%c%s%s%d%s %d %4X %4X",
2687            m_debug_options.bg_disabled[1]?" ":"*",
2688            (PPU_REG(TM) & 0x2)?"M":" ",
2689            (PPU_REG(TS) & 0x2)?"S":" ",
2690            (PPU_REG(CGADSUB) & 0x2)?"B":" ",
2691            (PPU_REG(TMW) & 0x2)?"m":" ",
2692            (PPU_REG(TSW) & 0x2)?"s":" ",
2693            WINLOGIC[(PPU_REG(WBGLOG) & 0xc) >> 2],
2694            (PPU_REG(W12SEL) & 0x20)?((PPU_REG(W12SEL) & 0x10)?"o":"i"):" ",
2695            (PPU_REG(W12SEL) & 0x80)?((PPU_REG(W12SEL) & 0x40)?"o":"i"):" ",
2696            m_layer[SNES_BG2].tile_size + 1,
2697            (PPU_REG(MOSAIC) & 0x2)?"m":" ",
2698            PPU_REG(BG2SC) & 0x3,
2699            (PPU_REG(BG2SC) & 0xfc) << 9,
2700            m_layer[SNES_BG2].charmap << 13);
2701      logerror("%s3 %s%s%s%s%s%c%s%s%d%s%s%d %4X %4X",
2702            m_debug_options.bg_disabled[2]?" ":"*",
2703            (PPU_REG(TM) & 0x4)?"M":" ",
2704            (PPU_REG(TS) & 0x4)?"S":" ",
2705            (PPU_REG(CGADSUB) & 0x4)?"B":" ",
2706            (PPU_REG(TMW) & 0x4)?"m":" ",
2707            (PPU_REG(TSW) & 0x4)?"s":" ",
2708            WINLOGIC[(PPU_REG(WBGLOG) & 0x30)>>4],
2709            (PPU_REG(W34SEL) & 0x2)?((PPU_REG(W34SEL) & 0x1)?"o":"i"):" ",
2710            (PPU_REG(W34SEL) & 0x8)?((PPU_REG(W34SEL) & 0x4)?"o":"i"):" ",
2711            m_layer[SNES_BG3].tile_size + 1,
2712            (PPU_REG(MOSAIC) & 0x4)?"m":" ",
2713            (PPU_REG(BGMODE) & 0x8)?"P":" ",
2714            PPU_REG(BG3SC) & 0x3,
2715            (PPU_REG(BG3SC) & 0xfc) << 9,
2716            m_layer[SNES_BG3].charmap << 13);
2717      logerror("%s4 %s%s%s%s%s%c%s%s%d%s %d %4X %4X",
2718            m_debug_options.bg_disabled[3]?" ":"*",
2719            (PPU_REG(TM) & 0x8)?"M":" ",
2720            (PPU_REG(TS) & 0x8)?"S":" ",
2721            (PPU_REG(CGADSUB) & 0x8)?"B":" ",
2722            (PPU_REG(TMW) & 0x8)?"m":" ",
2723            (PPU_REG(TSW) & 0x8)?"s":" ",
2724            WINLOGIC[(PPU_REG(WBGLOG) & 0xc0)>>6],
2725            (PPU_REG(W34SEL) & 0x20)?((PPU_REG(W34SEL) & 0x10)?"o":"i"):" ",
2726            (PPU_REG(W34SEL) & 0x80)?((PPU_REG(W34SEL) & 0x40)?"o":"i"):" ",
2727            m_layer[SNES_BG4].tile_size + 1,
2728            (PPU_REG(MOSAIC) & 0x8)?"m":" ",
2729            PPU_REG(BG4SC) & 0x3,
2730            (PPU_REG(BG4SC) & 0xfc) << 9,
2731            m_layer[SNES_BG4].charmap << 13 );
2732      logerror("%sO %s%s%s%s%s%c%s%s       %4X",
2733            m_debug_options.bg_disabled[4]?" ":"*",
2734            (PPU_REG(TM) & 0x10)?"M":" ",
2735            (PPU_REG(TS) & 0x10)?"S":" ",
2736            (PPU_REG(CGADSUB) & 0x10)?"B":" ",
2737            (PPU_REG(TMW) & 0x10)?"m":" ",
2738            (PPU_REG(TSW) & 0x10)?"s":" ",
2739            WINLOGIC[(PPU_REG(WOBJLOG) & 0x3)],
2740            (PPU_REG(WOBJSEL) & 0x2)?((PPU_REG(WOBJSEL) & 0x1)?"o":"i"):" ",
2741            (PPU_REG(WOBJSEL) & 0x8)?((PPU_REG(WOBJSEL) & 0x4)?"o":"i"):" ",
2742            m_layer[SNES_OAM].charmap << 13 );
2743      logerror("%sB   %s  %c%s%s",
2744            m_debug_options.colormath_disabled?" ":"*",
2745            (PPU_REG(CGADSUB) & 0x20)?"B":" ",
2746            WINLOGIC[(PPU_REG(WOBJLOG) & 0xc)>>2],
2747            (PPU_REG(WOBJSEL) & 0x20)?((PPU_REG(WOBJSEL) & 0x10)?"o":"i"):" ",
2748            (PPU_REG(WOBJSEL) & 0x80)?((PPU_REG(WOBJSEL) & 0x40)?"o":"i"):" " );
2749      logerror("Flags: %s%s%s %s %2d", (PPU_REG(CGWSEL) & 0x2)?"S":"F", (PPU_REG(CGADSUB) & 0x80)?"-":"+", (PPU_REG(CGADSUB) & 0x40)?" 50%":"100%",(PPU_REG(CGWSEL) & 0x1)?"D":"P", (PPU_REG(MOSAIC) & 0xf0) >> 4 );
2750      logerror("SetINI: %s %s %s %s %s %s", (PPU_REG(SETINI) & 0x1)?" I":"NI", (PPU_REG(SETINI) & 0x2)?"P":"R", (PPU_REG(SETINI) & 0x4)?"240":"225",(PPU_REG(SETINI) & 0x8)?"512":"256",(PPU_REG(SETINI) & 0x40)?"E":"N",(PPU_REG(SETINI) & 0x80)?"ES":"NS" );
2751      logerror("Mode7: A %5d B %5d", m_mode7.matrix_a, m_mode7.matrix_b );
2752      logerror(" %s%s%s   C %5d D %5d", (PPU_REG(M7SEL) & 0xc0)?((PPU_REG(M7SEL) & 0x40)?"0":"C"):"R", (PPU_REG(M7SEL) & 0x1)?"H":" ", (PPU_REG(M7SEL) & 0x2)?"V":" ", m_mode7.matrix_c, m_mode7.matrix_d );
2753      logerror("       X %5d Y %5d", m_mode7.origin_x, m_mode7.origin_y );
2754   }
2755#endif
2756
2757   return 0;
2758}
2759#endif /* SNES_LAYER_DEBUG */
trunk/src/mame/drivers/sfcbox.c
r29509r29510
116116***************************************************************************/
117117
118118#include "emu.h"
119#include "cpu/spc700/spc700.h"
120#include "cpu/g65816/g65816.h"
121119#include "cpu/z180/z180.h"
122120#include "machine/s3520cf.h"
123121#include "video/mb90082.h"
124122#include "includes/snes.h"
125#include "audio/snes_snd.h"
126123#include "rendlay.h"
127124
128125class sfcbox_state : public snes_state
r29509r29510
477474   MCFG_SCREEN_RAW_PARAMS(DOTCLK_NTSC, SNES_HTOTAL, 0, SNES_SCR_WIDTH, SNES_VTOTAL_NTSC, 0, SNES_SCR_HEIGHT_NTSC)
478475   MCFG_SCREEN_UPDATE_DRIVER( snes_state, screen_update )
479476
477   MCFG_DEVICE_ADD("ppu", SNES_PPU, 0)
478   MCFG_SNES_PPU_OPENBUS_CB(READ8(snes_state, snes_open_bus_r))
479   MCFG_VIDEO_SET_SCREEN("screen")
480
480481   // SFCBOX
481482   MCFG_SCREEN_ADD("osd", RASTER)
482483   MCFG_SCREEN_REFRESH_RATE(60)
trunk/src/mame/drivers/snesb.c
r29509r29510
144144
145145
146146#include "emu.h"
147#include "cpu/spc700/spc700.h"
148#include "cpu/g65816/g65816.h"
149147#include "includes/snes.h"
150#include "audio/snes_snd.h"
151148
152149
153150class snesb_state : public snes_state
r29509r29510
634631   MCFG_SCREEN_RAW_PARAMS(DOTCLK_NTSC, SNES_HTOTAL, 0, SNES_SCR_WIDTH, SNES_VTOTAL_NTSC, 0, SNES_SCR_HEIGHT_NTSC)
635632   MCFG_SCREEN_UPDATE_DRIVER( snes_state, screen_update )
636633
634   MCFG_DEVICE_ADD("ppu", SNES_PPU, 0)
635   MCFG_SNES_PPU_OPENBUS_CB(READ8(snes_state, snes_open_bus_r))
636   MCFG_VIDEO_SET_SCREEN("screen")
637
637638   /* sound hardware */
638639   MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
639640   MCFG_SOUND_ADD("spc700", SNES, 0)
trunk/src/mame/drivers/nss.c
r29509r29510
844844   MCFG_SCREEN_RAW_PARAMS(DOTCLK_NTSC, SNES_HTOTAL, 0, SNES_SCR_WIDTH, SNES_VTOTAL_NTSC, 0, SNES_SCR_HEIGHT_NTSC)
845845   MCFG_SCREEN_UPDATE_DRIVER( snes_state, screen_update )
846846
847   MCFG_DEVICE_ADD("ppu", SNES_PPU, 0)
848   MCFG_SNES_PPU_OPENBUS_CB(READ8(snes_state, snes_open_bus_r))
849   MCFG_VIDEO_SET_SCREEN("screen")
850
847851   // NSS
848852   MCFG_SCREEN_ADD("osd", RASTER)
849853   MCFG_SCREEN_REFRESH_RATE(60)
trunk/src/emu/video/video.mak
r29509r29510
447447
448448#-------------------------------------------------
449449#
450#@src/emu/video/snes_ppu.h,VIDEOS += SNES_PPU
451#-------------------------------------------------
452ifneq ($(filter SNES_PPU,$(VIDEOS)),)
453VIDEOOBJS+= $(VIDEOOBJ)/snes_ppu.o
454endif
455
456#-------------------------------------------------
457#
450458#@src/emu/video/stvvdp1.h,VIDEOS += STVVDP
451459#@src/emu/video/stvvdp2.h,VIDEOS += STVVDP
452460#-------------------------------------------------
trunk/src/emu/video/snes_ppu.c
r0r29510
1/***************************************************************************
2
3  snes.c
4
5  Video file to handle emulation of the Nintendo Super NES.
6
7  Anthony Kruize
8  Based on the original code by Lee Hammerton (aka Savoury Snax)
9
10  Some notes on the snes video hardware:
11
12  Object Attribute Memory(OAM) is made up of 128 blocks of 32 bits, followed
13  by 128 blocks of 2 bits. The format for each block is:
14  -First Block----------------------------------------------------------------
15  | x pos  | y pos  |char no.| v flip | h flip |priority|palette |char no msb|
16  +--------+--------+--------+--------+--------+--------+--------+-----------+
17  | 8 bits | 8 bits | 8 bits | 1 bit  | 1 bit  | 2 bits | 3 bits | 1 bit     |
18  -Second Block---------------------------------------------------------------
19  | size  | x pos msb |
20  +-------+-----------+
21  | 1 bit | 1 bit     |
22  ---------------------
23
24  Video RAM contains information for character data and screen maps.
25  Screen maps are made up of 32 x 32 blocks of 16 bits each.
26  The format for each block is:
27  ----------------------------------------------
28  | v flip | x flip |priority|palette |char no.|
29  +--------+--------+--------+--------+--------+
30  | 1 bit  | 1 bit  | 1 bit  | 3 bits |10 bits |
31  ----------------------------------------------
32  Mode 7 is stored differently. Character data and screen map are interleaved.
33  There are two formats:
34  -Normal-----------------  -EXTBG-----------------------------
35  | char data | char no. |  | priority | char data | char no. |
36  +-----------+----------+  +----------+-----------+----------+
37  | 8 bits    | 8 bits   |  | 1 bit    | 7 bits    | 8 bits   |
38  ------------------------  -----------------------------------
39
40  The screen layers are drawn with the following priorities (updated info courtesy of byuu):
41
42  |           |   1   |   2   |   3   |   4   |   5   |   6   |   7   |   8   |   9   |  10   |  11   |  12   |
43  -------------------------------------------------------------------------------------------------------------
44  | Mode 0    |  BG4B |  BG3B |  OAM0 |  BG4A |  BG3A |  OAM1 |  BG2B |  BG1B |  OAM2 |  BG2A |  BG1A |  OAM3 |
45  -------------------------------------------------------------------------------------------------------------
46  | Mode 1 (*)|  BG3B |  OAM0 |  OAM1 |  BG2B |  BG1B |  OAM2 |  BG2A |  BG1A |  OAM3 |  BG3A |       |       |
47  -------------------------------------------------------------------------------------------------------------
48  | Mode 1 (!)|  BG3B |  OAM0 |  BG3A |  OAM1 |  BG2B |  BG1B |  OAM2 |  BG2A |  BG1A |  OAM3 |       |       |
49  -------------------------------------------------------------------------------------------------------------
50  | Mode 2    |  BG2B |  OAM0 |  BG1B |  OAM1 |  BG2A |  OAM2 |  BG1A |  OAM3 |       |       |       |       |
51  -------------------------------------------------------------------------------------------------------------
52  | Mode 3    |  BG2B |  OAM0 |  BG1B |  OAM1 |  BG2A |  OAM2 |  BG1A |  OAM3 |       |       |       |       |
53  -------------------------------------------------------------------------------------------------------------
54  | Mode 4    |  BG2B |  OAM0 |  BG1B |  OAM1 |  BG2A |  OAM2 |  BG1A |  OAM3 |       |       |       |       |
55  -------------------------------------------------------------------------------------------------------------
56  | Mode 5    |  BG2B |  OAM0 |  BG1B |  OAM1 |  BG2A |  OAM2 |  BG1A |  OAM3 |       |       |       |       |
57  -------------------------------------------------------------------------------------------------------------
58  | Mode 6    |  OAM0 |  BG1B |  OAM1 |  OAM2 |  BG1A |  OAM3 |       |       |       |       |       |       |
59  -------------------------------------------------------------------------------------------------------------
60  | Mode 7 (+)|  OAM0 |  BG1n |  OAM1 |  OAM2 |  OAM3 |       |       |       |       |       |       |       |
61  -------------------------------------------------------------------------------------------------------------
62  | Mode 7 (-)|  BG2B |  OAM0 |  BG1n |  OAM1 |  BG2A |  OAM2 |  OAM3 |       |       |       |       |       |
63  -------------------------------------------------------------------------------------------------------------
64
65  Where:
66   - Mode 1 (*) is Mode 1 with bg3_pty = 1
67   - Mode 1 (!) is Mode 1 with bg3_pty = 0
68   - Mode 7 (+) is base Mode 7
69   - Mode 7 (-) is Mode 7 EXTBG
70
71***************************************************************************/
72
73#include "emu.h"
74#include "video/snes_ppu.h"
75
76#define SNES_MAINSCREEN    0
77#define SNES_SUBSCREEN     1
78#define SNES_CLIP_NEVER    0
79#define SNES_CLIP_IN       1
80#define SNES_CLIP_OUT      2
81#define SNES_CLIP_ALWAYS   3
82
83#define SNES_VRAM_SIZE        0x20000   /* 128kb of video ram */
84#define SNES_CGRAM_SIZE       0x202     /* 256 16-bit colours + 1 tacked on 16-bit colour for fixed colour */
85#define SNES_OAM_SIZE         0x440     /* 1088 bytes of Object Attribute Memory */
86#define FIXED_COLOUR          256       /* Position in cgram for fixed colour */
87
88
89/* Definitions for PPU Memory-Mapped registers */
90#define INIDISP        0x2100
91#define OBSEL          0x2101
92#define OAMADDL        0x2102
93#define OAMADDH        0x2103
94#define OAMDATA        0x2104
95#define BGMODE         0x2105   /* abcdefff = abcd: bg4-1 tile size | e: BG3 high priority | f: mode */
96#define MOSAIC         0x2106   /* xxxxabcd = x: pixel size | abcd: affects bg 1-4 */
97#define BG1SC          0x2107
98#define BG2SC          0x2108
99#define BG3SC          0x2109
100#define BG4SC          0x210A
101#define BG12NBA        0x210B
102#define BG34NBA        0x210C
103#define BG1HOFS        0x210D
104#define BG1VOFS        0x210E
105#define BG2HOFS        0x210F
106#define BG2VOFS        0x2110
107#define BG3HOFS        0x2111
108#define BG3VOFS        0x2112
109#define BG4HOFS        0x2113
110#define BG4VOFS        0x2114
111#define VMAIN          0x2115   /* i---ffrr = i: Increment timing | f: Full graphic | r: increment rate */
112#define VMADDL         0x2116   /* aaaaaaaa = a: LSB of vram address */
113#define VMADDH         0x2117   /* aaaaaaaa = a: MSB of vram address */
114#define VMDATAL        0x2118   /* dddddddd = d: data to be written */
115#define VMDATAH        0x2119   /* dddddddd = d: data to be written */
116#define M7SEL          0x211A   /* ab----yx = a: screen over | y: vertical flip | x: horizontal flip */
117#define M7A            0x211B   /* aaaaaaaa = a: COSINE rotate angle / X expansion */
118#define M7B            0x211C   /* aaaaaaaa = a: SINE rotate angle / X expansion */
119#define M7C            0x211D   /* aaaaaaaa = a: SINE rotate angle / Y expansion */
120#define M7D            0x211E   /* aaaaaaaa = a: COSINE rotate angle / Y expansion */
121#define M7X            0x211F
122#define M7Y            0x2120
123#define CGADD          0x2121
124#define CGDATA         0x2122
125#define W12SEL         0x2123
126#define W34SEL         0x2124
127#define WOBJSEL        0x2125
128#define WH0            0x2126   /* pppppppp = p: Left position of window 1 */
129#define WH1            0x2127   /* pppppppp = p: Right position of window 1 */
130#define WH2            0x2128   /* pppppppp = p: Left position of window 2 */
131#define WH3            0x2129   /* pppppppp = p: Right position of window 2 */
132#define WBGLOG         0x212A   /* aabbccdd = a: BG4 params | b: BG3 params | c: BG2 params | d: BG1 params */
133#define WOBJLOG        0x212B   /* ----ccoo = c: Colour window params | o: Object window params */
134#define TM             0x212C
135#define TS             0x212D
136#define TMW            0x212E
137#define TSW            0x212F
138#define CGWSEL         0x2130
139#define CGADSUB        0x2131
140#define COLDATA        0x2132
141#define SETINI         0x2133
142#define MPYL           0x2134
143#define MPYM           0x2135
144#define MPYH           0x2136
145#define SLHV           0x2137
146#define ROAMDATA       0x2138
147#define RVMDATAL       0x2139
148#define RVMDATAH       0x213A
149#define RCGDATA        0x213B
150#define OPHCT          0x213C
151#define OPVCT          0x213D
152#define STAT77         0x213E
153#define STAT78         0x213F
154
155
156#if SNES_LAYER_DEBUG
157/*                                    red   green  blue    purple  yellow cyan    grey    white */
158static const UINT16 dbg_mode_colours[8] = { 0x1f, 0x3e0, 0x7c00, 0x7c1f, 0x3ff, 0x7fe0, 0x4210, 0x7fff };
159#endif /* SNES_LAYER_DEBUG */
160
161static const UINT16 table_obj_offset[8][8] =
162{
163   { (0*32),   (0*32)+32,   (0*32)+64,   (0*32)+96,   (0*32)+128,   (0*32)+160,   (0*32)+192,   (0*32)+224 },
164   { (16*32),  (16*32)+32,  (16*32)+64,  (16*32)+96,  (16*32)+128,  (16*32)+160,  (16*32)+192,  (16*32)+224 },
165   { (32*32),  (32*32)+32,  (32*32)+64,  (32*32)+96,  (32*32)+128,  (32*32)+160,  (32*32)+192,  (32*32)+224 },
166   { (48*32),  (48*32)+32,  (48*32)+64,  (48*32)+96,  (48*32)+128,  (48*32)+160,  (48*32)+192,  (48*32)+224 },
167   { (64*32),  (64*32)+32,  (64*32)+64,  (64*32)+96,  (64*32)+128,  (64*32)+160,  (64*32)+192,  (64*32)+224 },
168   { (80*32),  (80*32)+32,  (80*32)+64,  (80*32)+96,  (80*32)+128,  (80*32)+160,  (80*32)+192,  (80*32)+224 },
169   { (96*32),  (96*32)+32,  (96*32)+64,  (96*32)+96,  (96*32)+128,  (96*32)+160,  (96*32)+192,  (96*32)+224 },
170   { (112*32), (112*32)+32, (112*32)+64, (112*32)+96, (112*32)+128, (112*32)+160, (112*32)+192, (112*32)+224 }
171};
172
173
174enum
175{
176   SNES_COLOR_DEPTH_2BPP = 0,
177   SNES_COLOR_DEPTH_4BPP,
178   SNES_COLOR_DEPTH_8BPP
179};
180
181
182#define PPU_REG(a) m_regs[a - 0x2100]
183
184
185
186//**************************************************************************
187//  DEVICE DEFINITIONS
188//**************************************************************************
189
190const device_type SNES_PPU = &device_creator<snes_ppu_device>;
191
192
193//**************************************************************************
194//  live device
195//**************************************************************************
196
197//-------------------------------------------------
198//  snes_ppu_device - constructor
199//-------------------------------------------------
200
201snes_ppu_device::snes_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
202            : device_t(mconfig, SNES_PPU, "SNES PPU", tag, owner, clock, "snes_ppu", __FILE__),
203               device_video_interface(mconfig, *this),
204               m_openbus_cb(*this)
205{
206}
207
208
209//-------------------------------------------------
210//  device_start - device-specific startup
211//-------------------------------------------------
212
213void snes_ppu_device::device_start()
214{
215   m_openbus_cb.resolve_safe(0);
216
217#if SNES_LAYER_DEBUG
218   memset(&m_debug_options, 0, sizeof(m_debug_options));
219#endif
220   
221   m_vram = auto_alloc_array(machine(), UINT8, SNES_VRAM_SIZE);
222   m_cgram = auto_alloc_array(machine(), UINT16, SNES_CGRAM_SIZE/2);
223   m_oam_ram = auto_alloc_array(machine(), UINT16, SNES_OAM_SIZE/2);
224   
225   /* Inititialize registers/variables */
226   m_update_windows = 1;
227   m_beam.latch_vert = 0;
228   m_beam.latch_horz = 0;
229   m_beam.current_vert = 0;
230   m_beam.current_horz = 0;
231   m_beam.last_visible_line = 225; /* TODO: PAL setting */
232   m_mode = 0;
233   m_ppu1_version = 1;  // 5C77 chip version number, read by STAT77, only '1' is known
234   m_ppu2_version = 3;  // 5C78 chip version number, read by STAT78, only '2' & '3' encountered so far.
235   
236   m_cgram_address = 0;
237   m_read_ophct = 0;
238   m_read_opvct = 0;
239   
240   PPU_REG(VMAIN) = 0x80;
241   // what about other regs?
242   
243   /* Inititialize mosaic table */
244   for (int j = 0; j < 16; j++)
245   {
246      for (int i = 0; i < 4096; i++)
247         m_mosaic_table[j][i] = (i / (j + 1)) * (j + 1);
248   }
249   
250   /* Init VRAM */
251   memset(m_vram, 0, SNES_VRAM_SIZE);
252   
253   /* Init Palette RAM */
254   memset((UINT8 *)m_cgram, 0, SNES_CGRAM_SIZE);
255   
256   /* Init oam RAM */
257   memset((UINT8 *)m_oam_ram, 0xff, SNES_OAM_SIZE);
258
259   for (int i = 0; i < 2; i++)
260   {
261      save_item(NAME(m_scanlines[i].enable), i);
262      save_item(NAME(m_scanlines[i].clip), i);
263      save_item(NAME(m_scanlines[i].buffer), i);
264      save_item(NAME(m_scanlines[i].priority), i);
265      save_item(NAME(m_scanlines[i].layer), i);
266      save_item(NAME(m_scanlines[i].blend_exception), i);
267   }
268   
269   for (int i = 0; i < 6; i++)
270   {
271      save_item(NAME(m_layer[i].window1_enabled), i);
272      save_item(NAME(m_layer[i].window1_invert), i);
273      save_item(NAME(m_layer[i].window2_enabled), i);
274      save_item(NAME(m_layer[i].window2_invert), i);
275      save_item(NAME(m_layer[i].wlog_mask), i);
276      save_item(NAME(m_layer[i].color_math), i);
277      save_item(NAME(m_layer[i].charmap), i);
278      save_item(NAME(m_layer[i].tilemap), i);
279      save_item(NAME(m_layer[i].tilemap_size), i);
280      save_item(NAME(m_layer[i].tile_size), i);
281      save_item(NAME(m_layer[i].mosaic_enabled), i);
282      save_item(NAME(m_layer[i].main_window_enabled), i);
283      save_item(NAME(m_layer[i].sub_window_enabled), i);
284      save_item(NAME(m_layer[i].main_bg_enabled), i);
285      save_item(NAME(m_layer[i].sub_bg_enabled), i);
286      save_item(NAME(m_layer[i].hoffs), i);
287      save_item(NAME(m_layer[i].voffs), i);
288     
289      save_item(NAME(m_clipmasks[i]), i);
290   }
291   
292   save_item(NAME(m_oam.address_low));
293   save_item(NAME(m_oam.address_high));
294   save_item(NAME(m_oam.saved_address_low));
295   save_item(NAME(m_oam.saved_address_high));
296   save_item(NAME(m_oam.address));
297   save_item(NAME(m_oam.priority_rotation));
298   save_item(NAME(m_oam.next_charmap));
299   save_item(NAME(m_oam.next_size));
300   save_item(NAME(m_oam.size));
301   save_item(NAME(m_oam.next_name_select));
302   save_item(NAME(m_oam.name_select));
303   save_item(NAME(m_oam.first_sprite));
304   save_item(NAME(m_oam.flip));
305   save_item(NAME(m_oam.write_latch));
306   
307   save_item(NAME(m_beam.latch_horz));
308   save_item(NAME(m_beam.latch_vert));
309   save_item(NAME(m_beam.current_horz));
310   save_item(NAME(m_beam.current_vert));
311   save_item(NAME(m_beam.last_visible_line));
312   save_item(NAME(m_beam.interlace_count));
313   
314   save_item(NAME(m_mode7.repeat));
315   save_item(NAME(m_mode7.hflip));
316   save_item(NAME(m_mode7.vflip));
317   save_item(NAME(m_mode7.matrix_a));
318   save_item(NAME(m_mode7.matrix_b));
319   save_item(NAME(m_mode7.matrix_c));
320   save_item(NAME(m_mode7.matrix_d));
321   save_item(NAME(m_mode7.origin_x));
322   save_item(NAME(m_mode7.origin_y));
323   save_item(NAME(m_mode7.hor_offset));
324   save_item(NAME(m_mode7.ver_offset));
325   save_item(NAME(m_mode7.extbg));
326   
327   save_item(NAME(m_mosaic_size));
328   save_item(NAME(m_clip_to_black));
329   save_item(NAME(m_prevent_color_math));
330   save_item(NAME(m_sub_add_mode));
331   save_item(NAME(m_bg3_priority_bit));
332   save_item(NAME(m_direct_color));
333   save_item(NAME(m_ppu_last_scroll));
334   save_item(NAME(m_mode7_last_scroll));
335   
336   save_item(NAME(m_ppu1_open_bus));
337   save_item(NAME(m_ppu2_open_bus));
338   save_item(NAME(m_ppu1_version));
339   save_item(NAME(m_ppu2_version));
340   save_item(NAME(m_window1_left));
341   save_item(NAME(m_window1_right));
342   save_item(NAME(m_window2_left));
343   save_item(NAME(m_window2_right));
344   
345   save_item(NAME(m_update_windows));
346   save_item(NAME(m_update_offsets));
347   save_item(NAME(m_update_oam_list));
348   save_item(NAME(m_mode));
349   save_item(NAME(m_interlace));
350   save_item(NAME(m_obj_interlace));
351   save_item(NAME(m_screen_brightness));
352   save_item(NAME(m_screen_disabled));
353   save_item(NAME(m_pseudo_hires));
354   save_item(NAME(m_color_modes));
355   save_item(NAME(m_stat77));
356   save_item(NAME(m_stat78));
357   
358   save_item(NAME(m_htmult));
359   save_item(NAME(m_cgram_address));
360   save_item(NAME(m_read_ophct));
361   save_item(NAME(m_read_opvct));
362   save_item(NAME(m_vram_fgr_high));
363   save_item(NAME(m_vram_fgr_increment));
364   save_item(NAME(m_vram_fgr_count));
365   save_item(NAME(m_vram_fgr_mask));
366   save_item(NAME(m_vram_fgr_shift));
367   save_item(NAME(m_vram_read_buffer));
368   save_item(NAME(m_vmadd));
369   
370   save_item(NAME(m_regs));
371   
372   save_pointer(NAME(m_vram), SNES_VRAM_SIZE);
373   save_pointer(NAME(m_cgram), SNES_CGRAM_SIZE/2);
374   save_pointer(NAME(m_oam_ram), SNES_OAM_SIZE/2);
375}
376
377
378
379/*****************************************
380 * get_bgcolor()
381 *
382 * Get the proper color (direct or from cgram)
383 *****************************************/
384
385inline UINT16 snes_ppu_device::get_bgcolor( UINT8 direct_colors, UINT16 palette, UINT8 color )
386{
387   UINT16 c = 0;
388
389   if (direct_colors)
390   {
391      /* format is  0 | BBb00 | GGGg0 | RRRr0, HW confirms that the data is zero padded. */
392      c = ((color & 0x07) << 2) | ((color & 0x38) << 4) | ((color & 0xc0) << 7);
393      c |= ((palette & 0x04) >> 1) | ((palette & 0x08) << 3) | ((palette & 0x10) << 8);
394   }
395   else
396      c = m_cgram[(palette + color) % FIXED_COLOUR];
397
398   return c;
399}
400
401/*****************************************
402 * set_scanline_pixel()
403 *
404 * Store pixel color, priority, layer and
405 * color math exception (for OAM) in the
406 * proper scanline
407 *****************************************/
408
409inline void snes_ppu_device::set_scanline_pixel( int screen, INT16 x, UINT16 color, UINT8 priority, UINT8 layer, int blend )
410{
411   m_scanlines[screen].buffer[x] = color;
412   m_scanlines[screen].priority[x] = priority;
413   m_scanlines[screen].layer[x] = layer;
414   m_scanlines[screen].blend_exception[x] = blend;
415}
416
417/*************************************************************************************************
418 * SNES tiles
419 *
420 * The way vram is accessed to draw tiles is basically the same for both BG and OAM tiles. Main
421 * differences are bit planes (variable for BG and fixed for OAM) and a few details of the scanline
422 * output (since OAM has neither mosaic, nor hires, nor direct colors).
423 * Hence, we use a common function to take data from VRAM and then we call specific routines for
424 * OAM vs BG vs Hi-Res BG tiles.
425 *************************************************************************************************/
426
427/*****************************************
428 * draw_bgtile_lores()
429 * draw_bgtile_hires()
430 * draw_oamtile_()
431 *
432 * Check if a pixel is clipped or not, and
433 * copy it to the scanline buffer when
434 * appropriate. The actual way to perform
435 * such operations depends on the source
436 * (BG or OAM) and on the resolution (hires
437 * or lores)
438 *****************************************/
439
440inline void snes_ppu_device::draw_bgtile_lores( UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority )
441{
442   int screen;
443   UINT16 c;
444
445   for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
446   {
447      if (ii >= 0 && ii < SNES_SCR_WIDTH && m_scanlines[screen].enable)
448      {
449         if (m_scanlines[screen].priority[ii] <= priority)
450         {
451            UINT8 clr = colour;
452            UINT8 clipmask = m_clipmasks[layer][ii];
453
454#if SNES_LAYER_DEBUG
455            if (m_debug_options.windows_disabled)
456               clipmask = 0xff;
457#endif /* SNES_LAYER_DEBUG */
458
459            /* Clip to windows */
460            if (m_scanlines[screen].clip)
461               clr &= clipmask;
462
463            /* Only draw if we have a colour (0 == transparent) */
464            if (clr)
465            {
466               c = get_bgcolor(direct_colors, pal, clr);
467               set_scanline_pixel(screen, ii, c, priority, layer, 0);
468            }
469         }
470      }
471   }
472}
473
474inline void snes_ppu_device::draw_bgtile_hires( UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority )
475{
476   int screen;
477   UINT16 c;
478
479   for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
480   {
481      // odd pixels to main screen, even pixels to sub screen
482      if (ii >= 0 && ii < (SNES_SCR_WIDTH << 1) && ((ii & 1) ^ screen) && m_scanlines[screen].enable)
483      {
484         if (m_scanlines[screen].priority[ii >> 1] <= priority)
485         {
486            UINT8 clr = colour;
487            UINT8 clipmask = m_clipmasks[layer][ii >> 1];
488
489#if SNES_LAYER_DEBUG
490            if (m_debug_options.windows_disabled)
491               clipmask = 0xff;
492#endif /* SNES_LAYER_DEBUG */
493
494            /* Clip to windows */
495            if (m_scanlines[screen].clip)
496               clr &= clipmask;
497
498            /* Only draw if we have a colour (0 == transparent) */
499            if (clr)
500            {
501               c = get_bgcolor(direct_colors, pal, clr);
502               set_scanline_pixel(screen, ii >> 1, c, priority, layer, 0);
503            }
504         }
505      }
506   }
507}
508
509inline void snes_ppu_device::draw_oamtile( INT16 ii, UINT8 colour, UINT16 pal, UINT8 priority )
510{
511   int screen;
512   int blend;
513   UINT16 c;
514   INT16 pos = ii & 0x1ff;
515
516   for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
517   {
518      if (pos >= 0 && pos < SNES_SCR_WIDTH && m_scanlines[screen].enable)
519      {
520         UINT8 clr = colour;
521         UINT8 clipmask = m_clipmasks[SNES_OAM][pos];
522
523#if SNES_LAYER_DEBUG
524         if (m_debug_options.windows_disabled)
525            clipmask = 0xff;
526#endif /* SNES_LAYER_DEBUG */
527
528         /* Clip to windows */
529         if (m_scanlines[screen].clip)
530            clr &= clipmask;
531
532         /* Only draw if we have a colour (0 == transparent) */
533         if (clr)
534         {
535            c = m_cgram[(pal + clr) % FIXED_COLOUR];
536            blend = (pal + clr < 192) ? 1 : 0;
537            set_scanline_pixel(screen, pos, c, priority, SNES_OAM, blend);
538         }
539      }
540   }
541}
542
543/*****************************************
544 * draw_tile()
545 *
546 * Draw 8 pixels from the expected tile
547 * by reading the color planes from vram
548 * and by calling the appropriate routine
549 * (depending on layer and resolution)
550 *****************************************/
551
552inline void snes_ppu_device::draw_tile( UINT8 planes, UINT8 layer, UINT32 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT8 direct_colors, UINT16 pal, UINT8 hires )
553{
554   UINT8 plane[8];
555   INT16 ii, jj;
556   int x_mos;
557
558   for (ii = 0; ii < planes / 2; ii++)
559   {
560      plane[2 * ii + 0] = m_vram[(tileaddr + 16 * ii + 0) % SNES_VRAM_SIZE];
561      plane[2 * ii + 1] = m_vram[(tileaddr + 16 * ii + 1) % SNES_VRAM_SIZE];
562   }
563
564   for (ii = x; ii < (x + 8); ii++)
565   {
566      UINT8 colour = 0;
567      UINT8 mosaic = m_layer[layer].mosaic_enabled;
568
569#if SNES_LAYER_DEBUG
570      if (m_debug_options.mosaic_disabled)
571         mosaic = 0;
572#endif /* SNES_LAYER_DEBUG */
573
574      if (flip)
575      {
576         for (jj = 0; jj < planes; jj++)
577            colour |= BIT(plane[jj], ii - x) ? (1 << jj) : 0;
578      }
579      else
580      {
581         for (jj = 0; jj < planes; jj++)
582            colour |= BIT(plane[jj], 7 - (ii - x)) ? (1 << jj) : 0;
583      }
584
585      if (layer == SNES_OAM)
586         draw_oamtile(ii, colour, pal, priority);
587      else if (!hires)
588      {
589         if (mosaic)
590         {
591            for (x_mos = 0; x_mos < (m_mosaic_size + 1); x_mos++)
592               draw_bgtile_lores(layer, ii + x_mos, colour, pal, direct_colors, priority);
593            ii += x_mos - 1;
594         }
595         else
596            draw_bgtile_lores(layer, ii, colour, pal, direct_colors, priority);
597      }
598      else /* hires */
599      {
600         if (mosaic)
601         {
602            for (x_mos = 0; x_mos < (m_mosaic_size + 1); x_mos++)
603               draw_bgtile_hires(layer, ii + x_mos, colour, pal, direct_colors, priority);
604            ii += x_mos - 1;
605         }
606         else
607            draw_bgtile_hires(layer, ii, colour, pal, direct_colors, priority);
608      }
609   }
610}
611
612/*************************************************************************************************
613 * SNES BG layers
614 *
615 * BG drawing theory of each scanline is quite easy: depending on the graphics Mode (0-7), there
616 * are up to 4 background layers. Pixels for each BG layer can have two different priorities.
617 * Depending on the line and on the BGHOFS and BGVOFS PPU registers, we first determine the tile
618 * address in m_vram (by determining x,y coord and tile size and by calling get_tmap_addr).
619 * Then, we load the correspondent data and we determine the tile properties: which priority to
620 * use, which palette etc. Finally, for each pixel of the tile appearing on screen, we check if
621 * the tile priority is higher than the BG/OAM already stored in that pixel for that line. If so
622 * we store the pixel in the buffer, otherwise we discard it.
623 *
624 * Of course, depending on the graphics Mode, it might be easier or harder to determine the proper
625 * tile address in vram (Mode 7 uses different registers, Mode 2, 4 and 6 uses OPT effect, etc.),
626 * but in general it works as described.
627 *************************************************************************************************/
628
629/*********************************************
630 * get_tmap_addr()
631 *
632 * Find the address in VRAM of the tile (x,y)
633 *********************************************/
634
635inline UINT32 snes_ppu_device::get_tmap_addr( UINT8 layer, UINT8 tile_size, UINT32 base, UINT32 x, UINT32 y )
636{
637   UINT32 res = base;
638   x  >>= (3 + tile_size);
639   y  >>= (3 + tile_size);
640
641   res += (m_layer[layer].tilemap_size & 2) ? ((y & 0x20) << ((m_layer[layer].tilemap_size & 1) ? 7 : 6)) : 0;
642   /* Scroll vertically */
643   res += (y & 0x1f) << 6;
644   /* Offset horizontally */
645   res += (m_layer[layer].tilemap_size & 1) ? ((x & 0x20) << 6) : 0;
646   /* Scroll horizontally */
647   res += (x & 0x1f) << 1;
648
649   return res;
650}
651
652/*********************************************
653 * update_line()
654 *
655 * Update an entire line of tiles.
656 *********************************************/
657
658inline void snes_ppu_device::update_line( UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a, UINT8 color_depth, UINT8 hires, UINT8 offset_per_tile, UINT8 direct_colors )
659{
660   UINT32 tmap, tile, xoff, yoff, charaddr, addr;
661   UINT16 ii = 0, vflip, hflip, pal, pal_direct, tilemap;
662   UINT8 xscroll, priority;
663   INT8 yscroll;
664   int tile_incr = 0;
665   UINT16 opt_bit = (layer == SNES_BG1) ? 13 : (layer == SNES_BG2) ? 14 : 0;
666   UINT8 tile_size;
667   /* variables depending on color_depth */
668   UINT8 color_planes = 2 << color_depth;
669   /* below we cheat to simplify the code: 8BPP should have 0 pal offset, not 0x100 (but we take care of this by later using pal % FIXED_COLOUR) */
670   UINT8 color_shift = 2 << color_depth;
671
672#if SNES_LAYER_DEBUG
673   if (m_debug_options.bg_disabled[layer])
674      return;
675#endif /* SNES_LAYER_DEBUG */
676
677   m_scanlines[SNES_MAINSCREEN].enable = m_layer[layer].main_bg_enabled;
678   m_scanlines[SNES_SUBSCREEN].enable = m_layer[layer].sub_bg_enabled;
679   m_scanlines[SNES_MAINSCREEN].clip = m_layer[layer].main_window_enabled;
680   m_scanlines[SNES_SUBSCREEN].clip = m_layer[layer].sub_window_enabled;
681
682   if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
683      return;
684
685   /* Handle Mosaic effects */
686   if (m_layer[layer].mosaic_enabled)
687      curline -= (curline % (m_mosaic_size + 1));
688
689   if ((m_interlace == 2) && !hires && !m_pseudo_hires)
690      curline /= 2;
691
692   /* Find the size of the tiles (8x8 or 16x16) */
693   tile_size = m_layer[layer].tile_size;
694
695   /* Find scroll info */
696   xoff = m_layer[layer].hoffs;
697   yoff = m_layer[layer].voffs;
698
699   xscroll = xoff & ((1 << (3 + tile_size)) - 1);
700
701   /* Jump to base map address */
702   tmap = m_layer[layer].tilemap << 9;
703   charaddr = m_layer[layer].charmap << 13;
704
705   while (ii < 256 + (8 << tile_size))
706   {
707      // determine the horizontal position (Bishojo Janshi Suchi Pai & Desert Figther have tile_size & hires == 1)
708      UINT32 xpos = xoff + (ii << (tile_size * hires));
709      UINT32 ypos = yoff + curline;
710
711      if (offset_per_tile != SNES_OPT_NONE)
712      {
713         int opt_x = ii + (xoff & 7);
714         UINT32 haddr = 0, vaddr = 0;
715         UINT16 hval = 0, vval = 0;
716
717         if (opt_x >= 8)
718         {
719            switch (offset_per_tile)
720            {
721            case SNES_OPT_MODE2:
722            case SNES_OPT_MODE6:
723               haddr = get_tmap_addr(SNES_BG3, m_layer[SNES_BG3].tile_size, m_layer[SNES_BG3].tilemap << 9, (opt_x - 8) + ((m_layer[SNES_BG3].hoffs & 0x3ff) & ~7), (m_layer[SNES_BG3].voffs & 0x3ff));
724               vaddr = get_tmap_addr(SNES_BG3, m_layer[SNES_BG3].tile_size, m_layer[SNES_BG3].tilemap << 9, (opt_x - 8) + ((m_layer[SNES_BG3].hoffs & 0x3ff) & ~7), (m_layer[SNES_BG3].voffs & 0x3ff) + 8);
725               hval = m_vram[haddr % SNES_VRAM_SIZE] | (m_vram[(haddr + 1) % SNES_VRAM_SIZE] << 8);
726               vval = m_vram[vaddr % SNES_VRAM_SIZE] | (m_vram[(vaddr + 1) % SNES_VRAM_SIZE] << 8);
727               if (BIT(hval, opt_bit))
728                  xpos = opt_x + (hval & ~7);
729               if (BIT(vval, opt_bit))
730                  ypos = curline + vval;
731               break;
732            case SNES_OPT_MODE4:
733               haddr = get_tmap_addr(SNES_BG3, m_layer[SNES_BG3].tile_size, m_layer[SNES_BG3].tilemap << 9, (opt_x - 8) + ((m_layer[SNES_BG3].hoffs & 0x3ff) & ~7), (m_layer[SNES_BG3].voffs & 0x3ff));
734               hval = m_vram[haddr % SNES_VRAM_SIZE] | (m_vram[(haddr + 1) % SNES_VRAM_SIZE] << 8);
735               if (BIT(hval, opt_bit))
736               {
737                  if (!BIT(hval, 15))
738                     xpos = opt_x + (hval & ~7);
739                  else
740                     ypos = curline + hval;
741               }
742               break;
743            }
744         }
745      }
746
747      addr = get_tmap_addr(layer, tile_size, tmap, xpos, ypos);
748
749      /*
750      Tilemap format
751        vhopppcc cccccccc
752
753        v/h  = Vertical/Horizontal flip this tile.
754        o    = Tile priority.
755        ppp  = Tile palette. The number of entries in the palette depends on the Mode and the BG.
756        cccccccccc = Tile number.
757      */
758      tilemap = m_vram[addr % SNES_VRAM_SIZE] | (m_vram[(addr + 1) % SNES_VRAM_SIZE] << 8);
759      vflip = BIT(tilemap, 15);
760      hflip = BIT(tilemap, 14);
761      priority = BIT(tilemap, 13) ? priority_a : priority_b;
762      pal_direct = ((tilemap & 0x1c00) >> 8);
763      tile = tilemap & 0x03ff;
764
765      pal = ((pal_direct >> 2) << color_shift);
766
767      /* Mode 0 palettes are layer specific */
768      if (m_mode == 0)
769      {
770         pal += (layer << 5);
771      }
772
773#if SNES_LAYER_DEBUG
774      /* if we want to draw only one of the priorities of this layer */
775      if (((m_debug_options.select_pri[layer] & 0x01) && (priority == priority_a)) ||
776         ((m_debug_options.select_pri[layer] & 0x02) && (priority == priority_b)))
777      {
778         if (!hires && tile_size)
779            ii += 16;
780         else
781            ii += 8;
782         continue;
783      }
784#endif /* SNES_LAYER_DEBUG */
785
786      /* figure out which line to draw */
787      yscroll = ypos & ((8 << tile_size) - 1);
788
789      if (tile_size)
790         if (BIT(yscroll, 3) != vflip)
791            tile += 16;
792
793      if (yscroll > 7)
794         yscroll &= 7;
795
796      if (vflip)
797         yscroll = 7 - yscroll;
798
799      yscroll <<= 1;
800
801      /* if we have to draw 16 pixels, set tile_incr and adjust tile for horizontal flip */
802      if (tile_size || hires)
803      {
804         if (hflip)
805         {
806            tile += 1;
807            tile_incr = -1; // next 8 pixels from previous tile (because of hflip)
808         }
809         else
810            tile_incr = 1;  // next 8 pixels from next tile
811      }
812
813      if (hires)
814      {
815         /* draw 16 pixels (the routine will automatically send half of them to the mainscreen scanline and half to the subscreen one) */
816         draw_tile(color_planes, layer, charaddr + (((tile + 0)         & 0x3ff) * 8 * color_planes) + yscroll, (ii - xscroll) * 2,     priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
817         draw_tile(color_planes, layer, charaddr + (((tile + tile_incr) & 0x3ff) * 8 * color_planes) + yscroll, (ii - xscroll) * 2 + 8, priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
818         ii += 8;
819      }
820      else
821      {
822         draw_tile(color_planes, layer, charaddr + ((tile & 0x3ff) * 8 * color_planes) + yscroll, ii - xscroll, priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
823         ii += 8;
824
825         if (tile_size)
826         {
827            draw_tile(color_planes, layer, charaddr + (((tile + tile_incr) & 0x3ff) * 8 * color_planes) + yscroll, ii - xscroll, priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
828            ii += 8;
829         }
830      }
831   }
832}
833
834
835/*********************************************
836 * update_line_mode7()
837 *
838 * Update an entire line of mode7 tiles.
839 *********************************************/
840
841#define MODE7_CLIP(x) (((x) & 0x2000) ? ((x) | ~0x03ff) : ((x) & 0x03ff))
842
843void snes_ppu_device::update_line_mode7( UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a )
844{
845   UINT32 tiled;
846   INT16 ma, mb, mc, md;
847   INT32 xc, yc, tx, ty, sx, sy, hs, vs, xpos, xdir, x0, y0;
848   UINT8 priority = priority_b;
849   UINT8 colour = 0;
850   UINT16 *mosaic_x, *mosaic_y;
851   UINT16 c;
852   int screen;
853
854#if SNES_LAYER_DEBUG
855   if (m_debug_options.bg_disabled[layer])
856      return;
857#endif /* SNES_LAYER_DEBUG */
858
859   m_scanlines[SNES_MAINSCREEN].enable = m_layer[layer].main_bg_enabled;
860   m_scanlines[SNES_SUBSCREEN].enable = m_layer[layer].sub_bg_enabled;
861   m_scanlines[SNES_MAINSCREEN].clip = m_layer[layer].main_window_enabled;
862   m_scanlines[SNES_SUBSCREEN].clip = m_layer[layer].sub_window_enabled;
863
864   if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
865      return;
866
867   ma = m_mode7.matrix_a;
868   mb = m_mode7.matrix_b;
869   mc = m_mode7.matrix_c;
870   md = m_mode7.matrix_d;
871   xc = m_mode7.origin_x;
872   yc = m_mode7.origin_y;
873   hs = m_mode7.hor_offset;
874   vs = m_mode7.ver_offset;
875
876   /* Sign extend */
877   xc <<= 19;
878   xc >>= 19;
879   yc <<= 19;
880   yc >>= 19;
881   hs <<= 19;
882   hs >>= 19;
883   vs <<= 19;
884   vs >>= 19;
885
886   /* Vertical flip */
887   if (m_mode7.vflip)
888      sy = 255 - curline;
889   else
890      sy = curline;
891
892   /* Horizontal flip */
893   if (m_mode7.hflip)
894   {
895      xpos = 255;
896      xdir = -1;
897   }
898   else
899   {
900      xpos = 0;
901      xdir = 1;
902   }
903
904   /* MOSAIC - to be verified */
905   if (layer == SNES_BG2)  // BG2 use two different bits for horizontal and vertical mosaic
906   {
907      mosaic_x = m_mosaic_table[m_layer[SNES_BG2].mosaic_enabled ? m_mosaic_size : 0];
908      mosaic_y = m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
909   }
910   else    // BG1 works as usual
911   {
912      mosaic_x =  m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
913      mosaic_y =  m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
914   }
915
916#if SNES_LAYER_DEBUG
917   if (m_debug_options.mosaic_disabled)
918   {
919      mosaic_x =  m_mosaic_table[0];
920      mosaic_y =  m_mosaic_table[0];
921   }
922#endif /* SNES_LAYER_DEBUG */
923
924   /* Let's do some mode7 drawing huh? */
925   /* These can be computed only once, since they do not depend on sx */
926   x0 = ((ma * MODE7_CLIP(hs - xc)) & ~0x3f) + ((mb * mosaic_y[sy]) & ~0x3f) + ((mb * MODE7_CLIP(vs - yc)) & ~0x3f) + (xc << 8);
927   y0 = ((mc * MODE7_CLIP(hs - xc)) & ~0x3f) + ((md * mosaic_y[sy]) & ~0x3f) + ((md * MODE7_CLIP(vs - yc)) & ~0x3f) + (yc << 8);
928
929   for (sx = 0; sx < 256; sx++, xpos += xdir)
930   {
931      tx = (x0 + (ma * mosaic_x[sx])) >> 8;
932      ty = (y0 + (mc * mosaic_x[sx])) >> 8;
933
934      switch (m_mode7.repeat)
935      {
936         case 0x00:  /* Repeat if outside screen area */
937         case 0x01:  /* Repeat if outside screen area */
938            tx &= 0x3ff;
939            ty &= 0x3ff;
940            tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
941            colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
942            break;
943         case 0x02:  /* Single colour backdrop screen if outside screen area */
944            if ((tx >= 0) && (tx < 1024) && (ty >= 0) && (ty < 1024))
945            {
946               tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
947               colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
948            }
949            else
950               colour = 0;
951            break;
952         case 0x03:  /* Character 0x00 repeat if outside screen area */
953            if ((tx >= 0) && (tx < 1024) && (ty >= 0) && (ty < 1024))
954               tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
955            else
956               tiled = 0;
957
958            colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
959            break;
960      }
961
962      /* The last bit is for priority in EXTBG mode (used only for BG2) */
963      if (layer == SNES_BG2)
964      {
965         priority = ((colour & 0x80) >> 7) ? priority_a : priority_b;
966         colour &= 0x7f;
967
968#if SNES_LAYER_DEBUG
969      /* if we want to draw only one of the priorities of this layer */
970      if (((m_debug_options.select_pri[layer] & 0x01) && (priority == priority_a)) ||
971         ((m_debug_options.select_pri[layer] & 0x02) && (priority == priority_b)))
972         continue;
973#endif /* SNES_LAYER_DEBUG */
974      }
975
976      for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
977      {
978         if (m_scanlines[screen].enable)
979         {
980            UINT8 clr = colour;
981            UINT8 clipmask = m_clipmasks[layer][xpos];
982
983#if SNES_LAYER_DEBUG
984            if (m_debug_options.windows_disabled)
985               clipmask = 0xff;
986#endif /* SNES_LAYER_DEBUG */
987
988            /* Clip to windows */
989            if (m_scanlines[screen].clip)
990               clr &= clipmask;
991
992            /* Draw pixel if appropriate */
993            if (m_scanlines[screen].priority[xpos] <= priority && clr > 0)
994            {
995               /* Direct select, but only outside EXTBG! */
996               // Direct color format is: 0 | BB000 | GGG00 | RRR00, HW confirms that the data is zero padded.
997               // In other words, like normal direct color, with pal = 0
998               c = get_bgcolor(m_direct_color && layer == SNES_BG1, 0, clr);
999               set_scanline_pixel(screen, xpos, c, priority, layer, 0);
1000            }
1001         }
1002      }
1003   }
1004}
1005
1006/*************************************************************************************************
1007 * SNES Sprites
1008 *
1009 * 1. First of all: sprites are drawn one line in advance. We emulate this by caching the
1010 * starting vram address, the sprite size and the "name select" at each line, and by using
1011 * them the next line to output the proper sprites - see update_obsel.
1012 *
1013 * 2. Each line can select its sprites among 128 available ones in oam_ram, hence we start
1014 * by creating a list of the available objects (each one with its x,y coordinate, its size,
1015 * its tile address, etc.) - see oam_list_build.
1016 *
1017 * 3. Next, we start finding out which sprites will appear in the line: starting from
1018 * FirstSprite, we count 32 OBJs which intersect our line and we store their indexes in the
1019 * oam_itemlist array (if more than 32 sprites intersect our line, we set the Range Over
1020 * flag); then, selecting among these sprites, we count 34 8x8 tiles which are visible
1021 * in our line (i.e. whose x coord is between -size and 256) and we store the corresponding
1022 * coordinates/priorities/palettes/etc. in the oam_tilelist array (if more than 34 tiles would
1023 * appear on screen, we set the Time Over flag).
1024 * Notice that when we populate oam_tilelist, we proceed from oam_itemlist[31] (or from the last
1025 * item which intersects the scanline), towards oam_itemlist[0], i.e. the higher tiles (say
1026 * oam_tilelist[34], or the last tile which appear on screen) will contain FirstSprite object,
1027 * or the sprites with closer index to FirstSprite which get displayed. This will play an
1028 * important role for sprite priority - see update_objects_rto.
1029 *
1030 * 4. All the above happens at the beginning of each VIDEO_UPDATE. When we finally draw the
1031 * scanline, we pass through the oam_tilelist and we store the displayed pixels in our scanline
1032 * buffer. Notice that, for each pixel of a SNES sprite, only the priority of the topmost sprite
1033 * is tested against the priority of the BG pixel (because FirstSprite is on top of FirstSprite+1,
1034 * which is on top of FirstSprite+2, etc., and therefore other sprites are already covered by the
1035 * topmost one). To emulate this, we draw each tile over the previous ones no matter what
1036 * priorities are (differently from what we did with BGs): in the end, we will have in each pixel z
1037 * its topmost sprite and scanline.priority[z] will be the topmost sprite priority as expected.
1038 * Of course, sprite drawing must happen before BG drawing, so that afterwords BG pixels properly
1039 * test their priority with the one of the correct sprite - see update_objects.
1040 *************************************************************************************************/
1041
1042
1043/*********************************************
1044 * update_obsel()
1045 *
1046 * Update sprite settings for next line.
1047 *********************************************/
1048
1049void snes_ppu_device::update_obsel( void )
1050{
1051   m_layer[SNES_OAM].charmap = m_oam.next_charmap;
1052   m_oam.name_select = m_oam.next_name_select;
1053
1054   if (m_oam.size != m_oam.next_size)
1055   {
1056      m_oam.size = m_oam.next_size;
1057      m_update_oam_list = 1;
1058   }
1059}
1060
1061/*********************************************
1062 * oam_list_build()
1063 *
1064 * Build a list of the available obj in OAM ram.
1065 *********************************************/
1066
1067void snes_ppu_device::oam_list_build( void )
1068{
1069   UINT8 *oamram = (UINT8 *)m_oam_ram;
1070   INT16 oam = 0x1ff;
1071   UINT16 oam_extra = oam + 0x20;
1072   UINT16 extra = 0;
1073   int ii;
1074
1075   m_update_oam_list = 0;       // eventually, we can optimize the code by only calling this function when there is a change in size
1076
1077   for (ii = 127; ii >= 0; ii--)
1078   {
1079      if (((ii + 1) % 4) == 0)
1080         extra = oamram[oam_extra--];
1081
1082      m_oam_spritelist[ii].vflip = (oamram[oam] & 0x80) >> 7;
1083      m_oam_spritelist[ii].hflip = (oamram[oam] & 0x40) >> 6;
1084      m_oam_spritelist[ii].priority_bits = (oamram[oam] & 0x30) >> 4;
1085      m_oam_spritelist[ii].pal = 128 + ((oamram[oam] & 0x0e) << 3);
1086      m_oam_spritelist[ii].tile = (oamram[oam--] & 0x1) << 8;
1087      m_oam_spritelist[ii].tile |= oamram[oam--];
1088      m_oam_spritelist[ii].y = oamram[oam--] + 1;
1089      m_oam_spritelist[ii].x = oamram[oam--];
1090      m_oam_spritelist[ii].size = (extra & 0x80) >> 7;
1091      extra <<= 1;
1092      m_oam_spritelist[ii].x |= ((extra & 0x80) << 1);
1093      extra <<= 1;
1094
1095      m_oam_spritelist[ii].y *= m_obj_interlace;
1096      m_oam_spritelist[ii].y &= 0x1ff;
1097
1098      m_oam_spritelist[ii].x &= 0x1ff;
1099
1100      /* Determine object size */
1101      switch (m_oam.size)
1102      {
1103      case 0:         /* 8x8 or 16x16 */
1104         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 2 : 1;
1105         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 2 : 1;
1106         break;
1107      case 1:         /* 8x8 or 32x32 */
1108         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 4 : 1;
1109         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 1;
1110         break;
1111      case 2:         /* 8x8 or 64x64 */
1112         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 8 : 1;
1113         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 1;
1114         break;
1115      case 3:         /* 16x16 or 32x32 */
1116         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 4 : 2;
1117         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 2;
1118         break;
1119      case 4:         /* 16x16 or 64x64 */
1120         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 8 : 2;
1121         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 2;
1122         break;
1123      case 5:         /* 32x32 or 64x64 */
1124         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 8 : 4;
1125         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 4;
1126         break;
1127      case 6:         /* undocumented: 16x32 or 32x64 */
1128         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 4 : 2;
1129         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 4;
1130         if (m_obj_interlace && !m_oam_spritelist[ii].size)
1131            m_oam_spritelist[ii].height = 2;
1132         break;
1133      case 7:         /* undocumented: 16x32 or 32x32 */
1134         m_oam_spritelist[ii].width  = m_oam_spritelist[ii].size ? 4 : 2;
1135         m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 4;
1136         if (m_obj_interlace && !m_oam_spritelist[ii].size)
1137            m_oam_spritelist[ii].height = 2;
1138         break;
1139      default:
1140         /* we should never enter here... */
1141         logerror("Object size unsupported: %d\n", m_oam.size);
1142         break;
1143      }
1144   }
1145}
1146
1147/*********************************************
1148 * is_sprite_on_scanline()
1149 *
1150 * Check if a given sprites intersect current
1151 * scanline
1152 *********************************************/
1153
1154int snes_ppu_device::is_sprite_on_scanline( UINT16 curline, UINT8 sprite )
1155{
1156   //if sprite is entirely offscreen and doesn't wrap around to the left side of the screen,
1157   //then it is not counted. this *should* be 256, and not 255, even though dot 256 is offscreen.
1158   int spr_height = (m_oam_spritelist[sprite].height << 3);
1159
1160   if (m_oam_spritelist[sprite].x > 256 && (m_oam_spritelist[sprite].x + (m_oam_spritelist[sprite].width << 3) - 1) < 512)
1161      return 0;
1162
1163   if (curline >= m_oam_spritelist[sprite].y && curline < (m_oam_spritelist[sprite].y + spr_height))
1164      return 1;
1165
1166   if ((m_oam_spritelist[sprite].y + spr_height) >= 256 && curline < ((m_oam_spritelist[sprite].y + spr_height) & 255))
1167      return 1;
1168
1169   return 0;
1170}
1171
1172/*********************************************
1173 * update_objects_rto()
1174 *
1175 * Determine which OBJs will be drawn on this
1176 * scanline.
1177 *********************************************/
1178
1179void snes_ppu_device::update_objects_rto( UINT16 curline )
1180{
1181   int ii, jj, active_sprite;
1182   UINT8 range_over, time_over;
1183   INT8 xs, ys;
1184   UINT8 line;
1185   UINT8 height, width, vflip, hflip, priority, pal;
1186   UINT16 tile;
1187   INT16 x, y;
1188   UINT32 name_sel = 0;
1189
1190   oam_list_build();
1191
1192   /* initialize counters */
1193   range_over = 0;
1194   time_over = 0;
1195
1196   /* setup the proper line */
1197   curline /= m_interlace;
1198   curline *= m_obj_interlace;
1199
1200   /* reset the list of first 32 objects which intersect current scanline */
1201   memset(m_oam_itemlist, 0xff, 32);
1202
1203   /* populate the list of 32 objects */
1204   for (ii = 0; ii < 128; ii++)
1205   {
1206      active_sprite = (ii + m_oam.first_sprite) & 0x7f;
1207
1208      if (!is_sprite_on_scanline(curline, active_sprite))
1209         continue;
1210
1211      if (range_over++ >= 32)
1212         break;
1213
1214      m_oam_itemlist[range_over - 1] = active_sprite;
1215   }
1216
1217   /* reset the list of first 34 tiles to be drawn */
1218   for (ii = 0; ii < 34; ii++)
1219      m_oam_tilelist[ii].tileaddr = 0xffff;
1220
1221   /* populate the list of 34 tiles */
1222   for (ii = 31; ii >= 0; ii--)
1223   {
1224      if (m_oam_itemlist[ii] == 0xff)
1225         continue;
1226
1227      active_sprite = m_oam_itemlist[ii];
1228
1229      tile = m_oam_spritelist[active_sprite].tile;
1230      x = m_oam_spritelist[active_sprite].x;
1231      y = m_oam_spritelist[active_sprite].y;
1232      height = m_oam_spritelist[active_sprite].height;
1233      width = m_oam_spritelist[active_sprite].width;
1234      vflip = m_oam_spritelist[active_sprite].vflip;
1235      hflip = m_oam_spritelist[active_sprite].hflip;
1236      priority = m_oam_spritelist[active_sprite].priority_bits;
1237      pal = m_oam_spritelist[active_sprite].pal;
1238
1239      /* Adjust y, if past maximum position (for sprites which overlap between top & bottom) */
1240      if (y >= (0x100 - 16) * m_interlace)
1241         y -= (0x100) * m_interlace;
1242
1243      if (curline >= y && curline < (y + (height << 3)))
1244      {
1245         /* Only objects using tiles over 255 use name select */
1246         name_sel = (tile < 256) ? 0 : m_oam.name_select;
1247
1248         ys = (curline - y) >> 3;
1249         line = (curline - y) % 8;
1250         if (vflip)
1251         {
1252            ys = height - ys - 1;
1253            line = 7 - line;
1254         }
1255         line <<= 1;
1256         tile <<= 5;
1257
1258         for (jj = 0; jj < width; jj++)
1259         {
1260            INT16 xx = (x + (jj << 3)) & 0x1ff;
1261
1262            if (x != 256 && xx >= 256 && (xx + 7) < 512)
1263               continue;
1264
1265            if (time_over++ >= 34)
1266               break;
1267
1268            xs = (hflip) ? (width - 1 - jj) : jj;
1269            m_oam_tilelist[time_over - 1].tileaddr = name_sel + tile + table_obj_offset[ys][xs] + line;
1270            m_oam_tilelist[time_over - 1].hflip = hflip;
1271            m_oam_tilelist[time_over - 1].x = xx;
1272            m_oam_tilelist[time_over - 1].pal = pal;
1273            m_oam_tilelist[time_over - 1].priority = priority;
1274         }
1275      }
1276   }
1277
1278   /* set Range Over flag if necessary */
1279   if (range_over > 32)
1280      m_stat77 |= 0x40;
1281
1282   /* set Time Over flag if necessary */
1283   if (time_over > 34)
1284      m_stat77 |= 0x80;
1285}
1286
1287/*********************************************
1288 * update_objects()
1289 *
1290 * Update an entire line of sprites.
1291 *********************************************/
1292
1293void snes_ppu_device::update_objects( UINT8 priority_oam0, UINT8 priority_oam1, UINT8 priority_oam2, UINT8 priority_oam3 )
1294{
1295   UINT8 pri, priority[4];
1296   UINT32 charaddr;
1297   int ii;
1298
1299#if SNES_LAYER_DEBUG
1300   if (m_debug_options.bg_disabled[SNES_OAM])
1301      return;
1302#endif /* SNES_LAYER_DEBUG */
1303
1304   m_scanlines[SNES_MAINSCREEN].enable = m_layer[SNES_OAM].main_bg_enabled;
1305   m_scanlines[SNES_SUBSCREEN].enable = m_layer[SNES_OAM].sub_bg_enabled;
1306   m_scanlines[SNES_MAINSCREEN].clip = m_layer[SNES_OAM].main_window_enabled;
1307   m_scanlines[SNES_SUBSCREEN].clip = m_layer[SNES_OAM].sub_window_enabled;
1308
1309   if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
1310      return;
1311
1312   charaddr = m_layer[SNES_OAM].charmap << 13;
1313
1314   priority[0] = priority_oam0;
1315   priority[1] = priority_oam1;
1316   priority[2] = priority_oam2;
1317   priority[3] = priority_oam3;
1318
1319   /* finally draw the tiles from the tilelist */
1320   for (ii = 0; ii < 34; ii++)
1321   {
1322      int tile = ii;
1323#if SNES_LAYER_DEBUG
1324      if (m_debug_options.sprite_reversed)
1325         tile = 33 - ii;
1326#endif /* SNES_LAYER_DEBUG */
1327
1328      if (m_oam_tilelist[tile].tileaddr == 0xffff)
1329         continue;
1330
1331      pri = priority[m_oam_tilelist[tile].priority];
1332
1333#if SNES_LAYER_DEBUG
1334      if (m_debug_options.select_pri[SNES_OAM])
1335      {
1336         int oam_draw = m_debug_options.select_pri[SNES_OAM] - 1;
1337         if (oam_draw != m_oam_tilelist[tile].priority)
1338            continue;
1339      }
1340#endif /* SNES_LAYER_DEBUG */
1341
1342      /* OAM tiles have fixed planes (4), no direct color and no hires, but otherwise work the same as BG ones */
1343      draw_tile(4, SNES_OAM, charaddr + m_oam_tilelist[tile].tileaddr, m_oam_tilelist[tile].x, pri, m_oam_tilelist[tile].hflip, 0, m_oam_tilelist[tile].pal, 0);
1344   }
1345}
1346
1347
1348/*********************************************
1349 * snes_update_mode_X()
1350 *
1351 * Update Mode X line.
1352 *********************************************/
1353
1354void snes_ppu_device::update_mode_0( UINT16 curline )
1355{
1356#if SNES_LAYER_DEBUG
1357   if (m_debug_options.mode_disabled[0])
1358      return;
1359#endif /* SNES_LAYER_DEBUG */
1360
1361   update_objects(3, 6, 9, 12);
1362   update_line(curline, SNES_BG1, 8, 11, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1363   update_line(curline, SNES_BG2, 7, 10, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1364   update_line(curline, SNES_BG3, 2, 5,  SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1365   update_line(curline, SNES_BG4, 1, 4,  SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1366}
1367
1368void snes_ppu_device::update_mode_1( UINT16 curline )
1369{
1370#if SNES_LAYER_DEBUG
1371   if (m_debug_options.mode_disabled[1])
1372      return;
1373#endif /* SNES_LAYER_DEBUG */
1374
1375   if (!m_bg3_priority_bit)
1376   {
1377      update_objects(2, 4, 7, 10);
1378      update_line(curline, SNES_BG1, 6, 9, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
1379      update_line(curline, SNES_BG2, 5, 8, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
1380      update_line(curline, SNES_BG3, 1, 3, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1381   }
1382   else
1383   {
1384      update_objects(2, 3, 6, 9);
1385      update_line(curline, SNES_BG1, 5, 8,  SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
1386      update_line(curline, SNES_BG2, 4, 7,  SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
1387      update_line(curline, SNES_BG3, 1, 10, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
1388   }
1389}
1390
1391void snes_ppu_device::update_mode_2( UINT16 curline )
1392{
1393#if SNES_LAYER_DEBUG
1394   if (m_debug_options.mode_disabled[2])
1395      return;
1396#endif /* SNES_LAYER_DEBUG */
1397
1398   update_objects(2, 4, 6, 8);
1399   update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_MODE2, 0);
1400   update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_MODE2, 0);
1401}
1402
1403void snes_ppu_device::update_mode_3( UINT16 curline )
1404{
1405#if SNES_LAYER_DEBUG
1406   if (m_debug_options.mode_disabled[3])
1407      return;
1408#endif /* SNES_LAYER_DEBUG */
1409
1410   update_objects(2, 4, 6, 8);
1411   update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_8BPP, 0, SNES_OPT_NONE, m_direct_color);
1412   update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
1413}
1414
1415void snes_ppu_device::update_mode_4( UINT16 curline )
1416{
1417#if SNES_LAYER_DEBUG
1418   if (m_debug_options.mode_disabled[4])
1419      return;
1420#endif /* SNES_LAYER_DEBUG */
1421
1422   update_objects(2, 4, 6, 8);
1423   update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_8BPP, 0, SNES_OPT_MODE4, m_direct_color);
1424   update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_MODE4, 0);
1425}
1426
1427void snes_ppu_device::update_mode_5( UINT16 curline )
1428{
1429#if SNES_LAYER_DEBUG
1430   if (m_debug_options.mode_disabled[5])
1431      return;
1432#endif /* SNES_LAYER_DEBUG */
1433
1434   update_objects(2, 4, 6, 8);
1435   update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_4BPP, 1, SNES_OPT_NONE, 0);
1436   update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_2BPP, 1, SNES_OPT_NONE, 0);
1437}
1438
1439void snes_ppu_device::update_mode_6( UINT16 curline )
1440{
1441#if SNES_LAYER_DEBUG
1442   if (m_debug_options.mode_disabled[6])
1443      return;
1444#endif /* SNES_LAYER_DEBUG */
1445
1446   update_objects(1, 3, 4, 6);
1447   update_line(curline, SNES_BG1, 2, 5, SNES_COLOR_DEPTH_4BPP, 1, SNES_OPT_MODE6, 0);
1448}
1449
1450void snes_ppu_device::update_mode_7( UINT16 curline )
1451{
1452#if SNES_LAYER_DEBUG
1453   if (m_debug_options.mode_disabled[7])
1454      return;
1455#endif /* SNES_LAYER_DEBUG */
1456
1457   if (!m_mode7.extbg)
1458   {
1459      update_objects(1, 3, 4, 5);
1460      update_line_mode7(curline, SNES_BG1, 2, 2);
1461   }
1462   else
1463   {
1464      update_objects(2, 4, 6, 7);
1465      update_line_mode7(curline, SNES_BG1, 3, 3);
1466      update_line_mode7(curline, SNES_BG2, 1, 5);
1467   }
1468}
1469
1470/*********************************************
1471 * snes_draw_screens()
1472 *
1473 * Draw the whole screen (Mode 0 -> 7).
1474 *********************************************/
1475
1476void snes_ppu_device::draw_screens( UINT16 curline )
1477{
1478   switch (m_mode)
1479   {
1480      case 0: update_mode_0(curline); break;     /* Mode 0 */
1481      case 1: update_mode_1(curline); break;     /* Mode 1 */
1482      case 2: update_mode_2(curline); break;     /* Mode 2 - Supports offset per tile */
1483      case 3: update_mode_3(curline); break;     /* Mode 3 - Supports direct colour */
1484      case 4: update_mode_4(curline); break;     /* Mode 4 - Supports offset per tile and direct colour */
1485      case 5: update_mode_5(curline); break;     /* Mode 5 - Supports hires */
1486      case 6: update_mode_6(curline); break;     /* Mode 6 - Supports offset per tile and hires */
1487      case 7: update_mode_7(curline); break;     /* Mode 7 - Supports direct colour */
1488   }
1489}
1490
1491/*********************************************
1492 * update_windowmasks()
1493 *
1494 * An example of how windows work:
1495 * Win1: ...#####......
1496 * Win2: ......#####...
1497 *             IN                 OUT
1498 * OR:   ...########...     ###........###
1499 * AND:  ......##......     ######..######
1500 * XOR:  ...###..###...     ###...##...###
1501 * XNOR: ###...##...###     ...###..###...
1502 *********************************************/
1503
1504void snes_ppu_device::update_windowmasks( void )
1505{
1506   UINT16 ii, jj;
1507   INT8 w1, w2;
1508
1509   m_update_windows = 0;        /* reset the flag */
1510
1511   for (ii = 0; ii < SNES_SCR_WIDTH; ii++)
1512   {
1513      /* update bg 1, 2, 3, 4, obj & color windows */
1514      /* jj = layer */
1515      for (jj = 0; jj < 6; jj++)
1516      {
1517         m_clipmasks[jj][ii] = 0xff;  /* let's start from un-masked */
1518         w1 = w2 = -1;
1519
1520         if (m_layer[jj].window1_enabled)
1521         {
1522            /* Default to mask area inside */
1523            if ((ii < m_window1_left) || (ii > m_window1_right))
1524               w1 = 0;
1525            else
1526               w1 = 1;
1527
1528            /* If mask area is outside then swap */
1529            if (m_layer[jj].window1_invert)
1530               w1 = !w1;
1531         }
1532
1533         if (m_layer[jj].window2_enabled)
1534         {
1535            if ((ii < m_window2_left) || (ii > m_window2_right))
1536               w2 = 0;
1537            else
1538               w2 = 1;
1539            if (m_layer[jj].window2_invert)
1540               w2 = !w2;
1541         }
1542
1543         /* mask if the appropriate expression is true */
1544         if (w1 >= 0 && w2 >= 0)
1545         {
1546            switch (m_layer[jj].wlog_mask)
1547            {
1548               case 0x00:  /* OR */
1549                  m_clipmasks[jj][ii] = (w1 | w2) ? 0x00 : 0xff;
1550                  break;
1551               case 0x01:  /* AND */
1552                  m_clipmasks[jj][ii] = (w1 & w2) ? 0x00 : 0xff;
1553                  break;
1554               case 0x02:  /* XOR */
1555                  m_clipmasks[jj][ii] = (w1 ^ w2) ? 0x00 : 0xff;
1556                  break;
1557               case 0x03:  /* XNOR */
1558                  m_clipmasks[jj][ii] = !(w1 ^ w2) ? 0x00 : 0xff;
1559                  break;
1560            }
1561         }
1562         else if (w1 >= 0)
1563            m_clipmasks[jj][ii] = w1 ? 0x00 : 0xff;
1564         else if (w2 >= 0)
1565            m_clipmasks[jj][ii] = w2 ? 0x00 : 0xff;
1566      }
1567   }
1568}
1569
1570/*********************************************
1571 * update_offsets()
1572 *
1573 * Update the offsets with the latest changes.
1574 * This is currently unused, but it could
1575 * possibly be handy for some minor optimization
1576 *********************************************/
1577
1578void snes_ppu_device::update_offsets( void )
1579{
1580   int ii;
1581   for (ii = 0; ii < 4; ii++)
1582   {
1583   }
1584   m_update_offsets = 0;
1585}
1586
1587/*****************************************
1588 * draw_blend()
1589 *
1590 * Routine for additive/subtractive blending
1591 * between the main and sub screens, i.e.
1592 * color math.
1593 *****************************************/
1594
1595inline void snes_ppu_device::draw_blend( UINT16 offset, UINT16 *colour, UINT8 prevent_color_math, UINT8 black_pen_clip, int switch_screens )
1596{
1597#if SNES_LAYER_DEBUG
1598   if (m_debug_options.colormath_disabled)
1599      return;
1600#endif /* SNES_LAYER_DEBUG */
1601
1602   /* when color math is applied to subscreen pixels, the blending depends on the blending used by the previous mainscreen
1603   pixel, except for subscreen pixel 0 which has no previous mainscreen pixel, see comments in refresh_scanline */
1604   if (switch_screens && offset > 0)
1605      offset -= 1;
1606
1607   if ((black_pen_clip == SNES_CLIP_ALWAYS) ||
1608      (black_pen_clip == SNES_CLIP_IN && m_clipmasks[SNES_COLOR][offset]) ||
1609      (black_pen_clip == SNES_CLIP_OUT && !m_clipmasks[SNES_COLOR][offset]))
1610      *colour = 0; //clip to black before color math
1611
1612   if (prevent_color_math == SNES_CLIP_ALWAYS) // blending mode 3 == always OFF
1613      return;
1614
1615   if ((prevent_color_math == SNES_CLIP_NEVER) ||
1616      (prevent_color_math == SNES_CLIP_IN  && !m_clipmasks[SNES_COLOR][offset]) ||
1617      (prevent_color_math == SNES_CLIP_OUT && m_clipmasks[SNES_COLOR][offset]))
1618   {
1619      UINT16 r, g, b;
1620      struct SNES_SCANLINE *subscreen;
1621      int clip_max = 0;   // if add then clip to 0x1f, if sub then clip to 0
1622
1623#if SNES_LAYER_DEBUG
1624      /* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */
1625      if (m_debug_options.draw_subscreen)
1626      {
1627         subscreen = switch_screens ? &m_scanlines[SNES_SUBSCREEN] : &m_scanlines[SNES_MAINSCREEN];
1628      }
1629      else
1630#endif /* SNES_LAYER_DEBUG */
1631      {
1632         subscreen = switch_screens ? &m_scanlines[SNES_MAINSCREEN] : &m_scanlines[SNES_SUBSCREEN];
1633      }
1634
1635      if (m_sub_add_mode) /* SNES_SUBSCREEN*/
1636      {
1637         if (!BIT(m_color_modes, 7))
1638         {
1639            /* 0x00 add */
1640            r = (*colour & 0x1f) + (subscreen->buffer[offset] & 0x1f);
1641            g = ((*colour & 0x3e0) >> 5) + ((subscreen->buffer[offset] & 0x3e0) >> 5);
1642            b = ((*colour & 0x7c00) >> 10) + ((subscreen->buffer[offset] & 0x7c00) >> 10);
1643            clip_max = 1;
1644         }
1645         else
1646         {
1647            /* 0x80 sub */
1648            r = (*colour & 0x1f) - (subscreen->buffer[offset] & 0x1f);
1649            g = ((*colour & 0x3e0) >> 5) - ((subscreen->buffer[offset] & 0x3e0) >> 5);
1650            b = ((*colour & 0x7c00) >> 10) - ((subscreen->buffer[offset] & 0x7c00) >> 10);
1651            if (r > 0x1f) r = 0;
1652            if (g > 0x1f) g = 0;
1653            if (b > 0x1f) b = 0;
1654         }
1655         /* only halve if the color is not the back colour */
1656         if (BIT(m_color_modes, 6) && (subscreen->buffer[offset] != m_cgram[FIXED_COLOUR]))
1657         {
1658            r >>= 1;
1659            g >>= 1;
1660            b >>= 1;
1661         }
1662      }
1663      else /* Fixed colour */
1664      {
1665         if (!BIT(m_color_modes, 7))
1666         {
1667            /* 0x00 add */
1668            r = (*colour & 0x1f) + (m_cgram[FIXED_COLOUR] & 0x1f);
1669            g = ((*colour & 0x3e0) >> 5) + ((m_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
1670            b = ((*colour & 0x7c00) >> 10) + ((m_cgram[FIXED_COLOUR] & 0x7c00) >> 10);
1671            clip_max = 1;
1672         }
1673         else
1674         {
1675            /* 0x80: sub */
1676            r = (*colour & 0x1f) - (m_cgram[FIXED_COLOUR] & 0x1f);
1677            g = ((*colour & 0x3e0) >> 5) - ((m_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
1678            b = ((*colour & 0x7c00) >> 10) - ((m_cgram[FIXED_COLOUR] & 0x7c00) >> 10);
1679            if (r > 0x1f) r = 0;
1680            if (g > 0x1f) g = 0;
1681            if (b > 0x1f) b = 0;
1682         }
1683         /* halve if necessary */
1684         if (BIT(m_color_modes, 6))
1685         {
1686            r >>= 1;
1687            g >>= 1;
1688            b >>= 1;
1689         }
1690      }
1691
1692      /* according to anomie's docs, after addition has been performed, division by 2 happens *before* clipping to max, hence we clip now */
1693      if (clip_max)
1694      {
1695         if (r > 0x1f) r = 0x1f;
1696         if (g > 0x1f) g = 0x1f;
1697         if (b > 0x1f) b = 0x1f;
1698      }
1699
1700      *colour = ((r & 0x1f) | ((g & 0x1f) << 5) | ((b & 0x1f) << 10));
1701   }
1702}
1703
1704/*********************************************
1705 * refresh_scanline()
1706 *
1707 * Redraw the current line.
1708 *********************************************/
1709/*********************************************
1710 * Notice that in hires and pseudo hires modes,
1711 * i.e. when 512 different pixels are present
1712 * in a scanline, a crt TV monitor would end
1713 * up blending adjacent pixels. To mimic this,
1714 * we add a small (optional) hack which enters
1715 * only in the very last stage of the scanline
1716 * drawing and which simulates the TV by
1717 * replacing the exact pixel color with an
1718 * average of the current and next pixel colors.
1719 * Credits (and thanks) to Blargg and Byuu for
1720 * the optimized averaging algorithm.
1721 *********************************************/
1722
1723void snes_ppu_device::refresh_scanline( running_machine &machine, bitmap_rgb32 &bitmap, UINT16 curline )
1724{
1725   UINT16 ii;
1726   int x;
1727   int fade;
1728   struct SNES_SCANLINE *scanline1, *scanline2;
1729   UINT16 c;
1730   UINT16 prev_colour = 0;
1731   int blurring = machine.root_device().ioport("OPTIONS")->read_safe(0) & 0x01;
1732
1733   g_profiler.start(PROFILER_VIDEO);
1734
1735   if (m_screen_disabled) /* screen is forced blank */
1736      for (x = 0; x < SNES_SCR_WIDTH * 2; x++)
1737         bitmap.pix32(curline, x) = rgb_t::black;
1738   else
1739   {
1740      /* Update clip window masks if necessary */
1741      if (m_update_windows)
1742         update_windowmasks();
1743      /* Update the offsets if necessary */
1744      if (m_update_offsets)
1745         update_offsets();
1746
1747      /* Clear priority */
1748      memset(m_scanlines[SNES_MAINSCREEN].priority, 0, SNES_SCR_WIDTH);
1749      memset(m_scanlines[SNES_SUBSCREEN].priority, 0, SNES_SCR_WIDTH);
1750
1751      /* Clear layers */
1752      memset(m_scanlines[SNES_MAINSCREEN].layer, SNES_COLOR, SNES_SCR_WIDTH);
1753      memset(m_scanlines[SNES_SUBSCREEN].layer, SNES_COLOR, SNES_SCR_WIDTH);
1754
1755      /* Clear blend_exception (only used for OAM) */
1756      memset(m_scanlines[SNES_MAINSCREEN].blend_exception, 0, SNES_SCR_WIDTH);
1757      memset(m_scanlines[SNES_SUBSCREEN].blend_exception, 0, SNES_SCR_WIDTH);
1758
1759      /* Draw back colour */
1760      for (ii = 0; ii < SNES_SCR_WIDTH; ii++)
1761      {
1762         if (m_mode == 5 || m_mode == 6 || m_pseudo_hires)
1763            m_scanlines[SNES_SUBSCREEN].buffer[ii] = m_cgram[0];
1764         else
1765            m_scanlines[SNES_SUBSCREEN].buffer[ii] = m_cgram[FIXED_COLOUR];
1766
1767         m_scanlines[SNES_MAINSCREEN].buffer[ii] = m_cgram[0];
1768      }
1769
1770      /* Prepare OAM for this scanline */
1771      update_objects_rto(curline);
1772
1773      /* Draw scanline */
1774      draw_screens(curline);
1775
1776      update_obsel();
1777
1778#if SNES_LAYER_DEBUG
1779      if (dbg_video(machine, curline))
1780      {
1781         g_profiler.stop();
1782         return;
1783      }
1784
1785      /* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */
1786      if (m_debug_options.draw_subscreen)
1787      {
1788         scanline1 = &m_scanlines[SNES_SUBSCREEN];
1789         scanline2 = &m_scanlines[SNES_MAINSCREEN];
1790      }
1791      else
1792#endif /* SNES_LAYER_DEBUG */
1793      {
1794         scanline1 = &m_scanlines[SNES_MAINSCREEN];
1795         scanline2 = &m_scanlines[SNES_SUBSCREEN];
1796      }
1797
1798      /* Draw the scanline to screen */
1799
1800      fade = m_screen_brightness;
1801
1802      for (x = 0; x < SNES_SCR_WIDTH; x++)
1803      {
1804         int r, g, b, hires;
1805         UINT16 tmp_col[2];
1806         hires = (m_mode != 5 && m_mode != 6 && !m_pseudo_hires) ? 0 : 1;
1807
1808         /* in hires, the first pixel (of 512) is subscreen pixel, then the first mainscreen pixel follows, and so on... */
1809         if (!hires)
1810         {
1811            c = scanline1->buffer[x];
1812
1813            /* perform color math if the layer wants it (except if it's an object > 192) */
1814            if (!scanline1->blend_exception[x] && m_layer[scanline1->layer[x]].color_math)
1815               draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 0);
1816
1817            r = ((c & 0x1f) * fade) >> 4;
1818            g = (((c & 0x3e0) >> 5) * fade) >> 4;
1819            b = (((c & 0x7c00) >> 10) * fade) >> 4;
1820
1821            bitmap.pix32(curline, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
1822            bitmap.pix32(curline, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
1823         }
1824         else
1825         {
1826            /* prepare the pixel from main screen */
1827            c = scanline1->buffer[x];
1828
1829            /* perform color math if the layer wants it (except if it's an object > 192) */
1830            if (!scanline1->blend_exception[x] && m_layer[scanline1->layer[x]].color_math)
1831               draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 0);
1832
1833            tmp_col[1] = c;
1834
1835            /* prepare the pixel from sub screen */
1836            c = scanline2->buffer[x];
1837
1838            /* in hires/pseudo-hires, subscreen pixels are blended as well: for each subscreen pixel, color math
1839            is applied if it had been applied to the previous mainscreen pixel. What happens at subscreen pixel 0
1840            (which has no previous mainscreen pixel) is undocumented. Until more info are discovered, we (arbitrarily)
1841            apply to it the same color math as the *next* mainscreen pixel (i.e. mainscreen pixel 0), which seems as good as
1842            any other choice */
1843            if (x == 0 && !scanline1->blend_exception[0] && m_layer[scanline1->layer[0]].color_math)
1844               draw_blend(0, &c, m_prevent_color_math, m_clip_to_black, 1);
1845            else if (x > 0  && !scanline1->blend_exception[x - 1] && m_layer[scanline1->layer[x - 1]].color_math)
1846               draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 1);
1847
1848            tmp_col[0] = c;
1849
1850            /* average the first pixel if required, or draw it directly*/
1851            if (blurring)
1852               c = (prev_colour + tmp_col[0] - ((prev_colour ^ tmp_col[0]) & 0x0421)) >> 1;    // Hack code to mimic TV pixel blurring
1853            else
1854               c = tmp_col[0];
1855
1856            r = ((c & 0x1f) * fade) >> 4;
1857            g = (((c & 0x3e0) >> 5) * fade) >> 4;
1858            b = (((c & 0x7c00) >> 10) * fade) >> 4;
1859
1860            bitmap.pix32(curline, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
1861            prev_colour = tmp_col[0];
1862
1863            /* average the second pixel if required, or draw it directly*/
1864            if (blurring)
1865               c = (prev_colour + tmp_col[1] - ((prev_colour ^ tmp_col[1]) & 0x0421)) >> 1;    // Hack code to mimic TV pixel blurring
1866            else
1867               c = tmp_col[1];
1868
1869            r = ((c & 0x1f) * fade) >> 4;
1870            g = (((c & 0x3e0) >> 5) * fade) >> 4;
1871            b = (((c & 0x7c00) >> 10) * fade) >> 4;
1872
1873            bitmap.pix32(curline, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
1874            prev_colour = tmp_col[1];
1875         }
1876      }
1877   }
1878
1879   g_profiler.stop();
1880}
1881
1882
1883/* CPU <-> PPU comms */
1884
1885// full graphic variables
1886static const UINT16 vram_fgr_inctab[4] = { 1, 32, 128, 128 };
1887static const UINT16 vram_fgr_inccnts[4] = { 0, 32, 64, 128 };
1888static const UINT16 vram_fgr_shiftab[4] = { 0, 5, 6, 7 };
1889
1890// utility function - latches the H/V counters.  Used by IRQ, writes to WRIO, etc.
1891void snes_ppu_device::latch_counters( running_machine &machine )
1892{
1893   m_beam.current_horz = machine.first_screen()->hpos() / m_htmult;
1894   m_beam.latch_vert = machine.first_screen()->vpos();
1895   m_beam.latch_horz = m_beam.current_horz;
1896   m_stat78 |= 0x40;   // indicate we latched
1897//  m_read_ophct = m_read_opvct = 0;    // clear read flags - 2009-08: I think we must clear these when STAT78 is read...
1898
1899//  printf("latched @ H %d V %d\n", m_beam.latch_horz, m_beam.latch_vert);
1900}
1901
1902void snes_ppu_device::dynamic_res_change( running_machine &machine )
1903{
1904   rectangle visarea = machine.first_screen()->visible_area();
1905   attoseconds_t refresh;
1906
1907   visarea.min_x = visarea.min_y = 0;
1908   visarea.max_y = m_beam.last_visible_line * m_interlace - 1;
1909   visarea.max_x = (SNES_SCR_WIDTH * 2) - 1;
1910
1911   // fixme: should compensate for SNES_DBG_VIDEO
1912   if (m_mode == 5 || m_mode == 6 || m_pseudo_hires)
1913      m_htmult = 2;
1914   else
1915      m_htmult = 1;
1916
1917   /* FIXME: does the timing changes when the gfx mode is equal to 5 or 6? */
1918   if ((m_stat78 & 0x10) == SNES_NTSC)
1919   {
1920      refresh = HZ_TO_ATTOSECONDS(DOTCLK_NTSC) * SNES_HTOTAL * SNES_VTOTAL_NTSC;
1921      machine.first_screen()->configure(SNES_HTOTAL * 2, SNES_VTOTAL_NTSC * m_interlace, visarea, refresh);
1922   }
1923   else
1924   {
1925      refresh = HZ_TO_ATTOSECONDS(DOTCLK_PAL) * SNES_HTOTAL * SNES_VTOTAL_PAL;
1926      machine.first_screen()->configure(SNES_HTOTAL * 2, SNES_VTOTAL_PAL * m_interlace, visarea, refresh);
1927   }
1928}
1929
1930/*************************************************
1931
1932 SNES VRAM accesses:
1933
1934 VRAM accesses during active display are invalid.
1935 Unlike OAM and CGRAM, they will not be written
1936 anywhere at all. Thanks to byuu's researches,
1937 the ranges where writes are invalid have been
1938 validated on hardware, as has the edge case where
1939 the S-CPU open bus can be written if the write
1940 occurs during the very last clock cycle of
1941 vblank.
1942 Our implementation could be not 100% accurate
1943 when interlace is active.
1944*************************************************/
1945
1946inline UINT32 snes_ppu_device::get_vram_address( running_machine &machine )
1947{
1948   UINT32 addr = m_vmadd;
1949
1950   if (m_vram_fgr_count)
1951   {
1952      UINT32 rem = addr & m_vram_fgr_mask;
1953      UINT32 faddr = (addr & ~m_vram_fgr_mask) + (rem >> m_vram_fgr_shift) + ((rem & (m_vram_fgr_count - 1)) << 3);
1954      return faddr << 1;
1955   }
1956
1957   return addr << 1;
1958}
1959
1960READ8_MEMBER( snes_ppu_device::vram_read )
1961{
1962   UINT8 res = 0;
1963   offset &= 0xffff; // only 64KB are present on SNES
1964
1965   if (m_screen_disabled)
1966      res = m_vram[offset];
1967   else
1968   {
1969      UINT16 v = m_screen->vpos();
1970      UINT16 h = m_screen->hpos();
1971      UINT16 ls = (((m_stat78 & 0x10) == SNES_NTSC ? 525 : 625) >> 1) - 1;
1972
1973      if (m_interlace == 2)
1974         ls++;
1975
1976      if (v == ls && h == 1362)
1977         res = 0;
1978      else if (v < m_beam.last_visible_line - 1)
1979         res = 0;
1980      else if (v == m_beam.last_visible_line - 1)
1981      {
1982         if (h == 1362)
1983            res = m_vram[offset];
1984         else
1985         {
1986            //printf("%d %d VRAM read, CHECK!\n",h,v);
1987            res = 0;
1988         }
1989      }
1990      else
1991         res = m_vram[offset];
1992   }
1993   return res;
1994}
1995
1996WRITE8_MEMBER( snes_ppu_device::vram_write )
1997{
1998   offset &= 0xffff; // only 64KB are present on SNES, Robocop 3 relies on this
1999
2000   if (m_screen_disabled)
2001      m_vram[offset] = data;
2002   else
2003   {
2004      UINT16 v = m_screen->vpos();
2005      UINT16 h = m_screen->hpos();
2006      if (v == 0)
2007      {
2008         if (h <= 4)
2009            m_vram[offset] = data;
2010         else if (h == 6)
2011            m_vram[offset] = m_openbus_cb(space, 0);
2012         else
2013         {
2014            //printf("%d %d VRAM write, CHECK!\n",h,v);
2015            //no write
2016         }
2017      }
2018      else if (v < m_beam.last_visible_line)
2019      {
2020         //printf("%d %d VRAM write, CHECK!\n",h,v);
2021         //no write
2022      }
2023      else if (v == m_beam.last_visible_line)
2024      {
2025         if (h <= 4)
2026         {
2027            //printf("%d %d VRAM write, CHECK!\n",h,v);
2028            //no write
2029         }
2030         else
2031            m_vram[offset] = data;
2032      }
2033      else
2034         m_vram[offset] = data;
2035   }
2036}
2037
2038/*************************************************
2039
2040 SNES OAM accesses:
2041
2042 OAM accesses during active display are allowed.
2043 The actual address varies during rendering, as the
2044 PPU reads in data itself for processing.
2045 Unfortunately, no one has been able (yet) to
2046 determine how this works. The only known game to
2047 actually access OAM during active display is
2048 Uniracers and it expects accesses to map to
2049 offset 0x0218. Hence, following byuu's choice
2050 we rerouted OAM accesses during active display
2051 to 0x0218 (0x010c in our snes_oam).
2052 This is a hack, but it is more accurate than
2053 writing to the 'expected' address set by
2054 $2102,$2103.
2055
2056 Notice that, since PPU_REG(OAMDATA) is never
2057 read/written directly, we use it as an index
2058 to choose the high/low byte of the snes_oam word.
2059*************************************************/
2060
2061READ8_MEMBER( snes_ppu_device::oam_read )
2062{
2063   offset &= 0x1ff;
2064
2065   if (offset & 0x100)
2066      offset &= 0x10f;
2067
2068   if (!m_screen_disabled)
2069   {
2070      UINT16 v = m_screen->vpos();
2071
2072      if (v < m_beam.last_visible_line)
2073         offset = 0x010c;
2074   }
2075
2076   return (m_oam_ram[offset] >> (PPU_REG(OAMDATA) << 3)) & 0xff;
2077}
2078
2079WRITE8_MEMBER( snes_ppu_device::oam_write )
2080{
2081   offset &= 0x1ff;
2082
2083   if (offset & 0x100)
2084      offset &= 0x10f;
2085
2086   if (!m_screen_disabled)
2087   {
2088      UINT16 v = m_screen->vpos();
2089
2090      if (v < m_beam.last_visible_line)
2091         offset = 0x010c;
2092   }
2093
2094   if (!(PPU_REG(OAMDATA)))
2095      m_oam_ram[offset] = (m_oam_ram[offset] & 0xff00) | (data << 0);
2096   else
2097      m_oam_ram[offset] = (m_oam_ram[offset] & 0x00ff) | (data << 8);
2098}
2099
2100/*************************************************
2101
2102 SNES CGRAM accesses:
2103
2104 CGRAM writes during hblank are valid. During
2105 active display, the actual address the data
2106 is written to varies, as the PPU itself changes
2107 the address. Like OAM, it is not known the exact
2108 algorithm used, but no commercial software seems
2109 to attempt this. While byuu, in his emu, maps
2110 those accesses to 0x01ff, because it is more
2111 accurate to invalidate the 'expected' address
2112 than not, MESS has issues if we don't write to
2113 the expected address (see e.g. Tokimeki Memorial).
2114 This is because writes should work during hblank
2115 (so that the game can produce color fading), but
2116 ends up not working with the conditions below.
2117 Hence, for the moment, we only document the
2118 solution adopted by BSNES without enabling it.
2119*************************************************/
2120
2121READ8_MEMBER( snes_ppu_device::cgram_read )
2122{
2123   UINT8 res = 0;
2124   offset &= 0x1ff;
2125
2126#if 0
2127   if (!m_screen_disabled)
2128   {
2129      UINT16 v = m_screen->vpos();
2130      UINT16 h = m_screen->hpos();
2131
2132      if (v < m_beam.last_visible_line && h >= 128 && h < 1096)
2133         offset = 0x1ff;
2134   }
2135#endif
2136
2137   res = ((UINT8 *)m_cgram)[offset];
2138
2139   // CGRAM palette data format is 15-bits (0,bbbbb,ggggg,rrrrr).
2140   // Highest bit is simply ignored.
2141   if (offset & 0x01)
2142      res &= 0x7f;
2143
2144   return res;
2145}
2146
2147WRITE8_MEMBER( snes_ppu_device::cgram_write )
2148{
2149   offset &= 0x1ff;
2150
2151#if 0
2152   // FIXME: this currently breaks some games (e.g. Tokimeki Memorial),
2153   // even if it's expected to be more accurate than allowing for
2154   // writes to the cgram address
2155   if (!m_screen_disabled)
2156   {
2157      UINT16 v = m_screen->vpos();
2158      UINT16 h = m_screen->hpos();
2159
2160      if (v < m_beam.last_visible_line && h >= 128 && h < 1096)
2161         offset = 0x1ff;
2162   }
2163#endif
2164
2165   // CGRAM palette data format is 15-bits (0,bbbbb,ggggg,rrrrr).
2166   // Highest bit is simply ignored.
2167   if (offset & 0x01)
2168      data &= 0x7f;
2169
2170   ((UINT8 *)m_cgram)[offset] = data;
2171}
2172
2173UINT8 snes_ppu_device::read(address_space &space, UINT32 offset, UINT8 wrio_bit7)
2174{
2175   UINT8 value;
2176
2177   switch (offset)
2178   {
2179      case OAMDATA:   /* 21xy for x=0,1,2 and y=4,5,6,8,9,a returns PPU1 open bus*/
2180      case BGMODE:
2181      case MOSAIC:
2182      case BG2SC:
2183      case BG3SC:
2184      case BG4SC:
2185      case BG4VOFS:
2186      case VMAIN:
2187      case VMADDL:
2188      case VMDATAL:
2189      case VMDATAH:
2190      case M7SEL:
2191      case W34SEL:
2192      case WOBJSEL:
2193      case WH0:
2194      case WH2:
2195      case WH3:
2196      case WBGLOG:
2197         return m_ppu1_open_bus;
2198
2199      case MPYL:      /* Multiplication result (low) */
2200         {
2201            /* Perform 16bit * 8bit multiply */
2202            UINT32 c = (INT16)m_mode7.matrix_a * (INT8)(m_mode7.matrix_b >> 8);
2203            m_ppu1_open_bus = c & 0xff;
2204            return m_ppu1_open_bus;
2205         }
2206      case MPYM:      /* Multiplication result (mid) */
2207         {
2208            /* Perform 16bit * 8bit multiply */
2209            UINT32 c = (INT16)m_mode7.matrix_a * (INT8)(m_mode7.matrix_b >> 8);
2210            m_ppu1_open_bus = (c >> 8) & 0xff;
2211            return m_ppu1_open_bus;
2212         }
2213      case MPYH:      /* Multiplication result (high) */
2214         {
2215            /* Perform 16bit * 8bit multiply */
2216            UINT32 c = (INT16)m_mode7.matrix_a * (INT8)(m_mode7.matrix_b >> 8);
2217            m_ppu1_open_bus = (c >> 16) & 0xff;
2218            return m_ppu1_open_bus;
2219         }
2220      case SLHV:      /* Software latch for H/V counter */
2221         latch_counters(space.machine());
2222         return m_openbus_cb(space, 0);       /* Return value is meaningless */
2223
2224      case ROAMDATA:  /* Read data from OAM (DR) */
2225         m_ppu1_open_bus = oam_read(space, m_oam.address);
2226         PPU_REG(OAMDATA) = (PPU_REG(OAMDATA) + 1) % 2;
2227         if (!PPU_REG(OAMDATA))
2228         {
2229            m_oam.address++;
2230            m_oam.address &= 0x1ff;
2231            m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
2232         }
2233         return m_ppu1_open_bus;
2234      case RVMDATAL:  /* Read data from VRAM (low) */
2235         {
2236            UINT32 addr = get_vram_address(space.machine());
2237            m_ppu1_open_bus = m_vram_read_buffer & 0xff;
2238
2239            if (!m_vram_fgr_high)
2240            {
2241               m_vram_read_buffer = vram_read(space, addr);
2242               m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
2243
2244               m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
2245            }
2246
2247            return m_ppu1_open_bus;
2248         }
2249      case RVMDATAH:  /* Read data from VRAM (high) */
2250         {
2251            UINT32 addr = get_vram_address(space.machine());
2252            m_ppu1_open_bus = (m_vram_read_buffer >> 8) & 0xff;
2253
2254            if (m_vram_fgr_high)
2255            {
2256               m_vram_read_buffer = vram_read(space, addr);
2257               m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
2258
2259               m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
2260            }
2261
2262            return m_ppu1_open_bus;
2263         }
2264      case RCGDATA:   /* Read data from CGRAM */
2265         if (!(m_cgram_address & 0x01))
2266            m_ppu2_open_bus = cgram_read(space, m_cgram_address);
2267         else
2268         {
2269            m_ppu2_open_bus &= 0x80;
2270            m_ppu2_open_bus |= cgram_read(space, m_cgram_address) & 0x7f;
2271         }
2272
2273         m_cgram_address = (m_cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
2274         return m_ppu2_open_bus;
2275      case OPHCT:     /* Horizontal counter data by ext/soft latch */
2276         if (m_read_ophct)
2277         {
2278            m_ppu2_open_bus &= 0xfe;
2279            m_ppu2_open_bus |= (m_beam.latch_horz >> 8) & 0x01;
2280         }
2281         else
2282         {
2283            m_ppu2_open_bus = m_beam.latch_horz & 0xff;
2284         }
2285         m_read_ophct ^= 1;
2286         return m_ppu2_open_bus;
2287      case OPVCT:     /* Vertical counter data by ext/soft latch */
2288         if (m_read_opvct)
2289         {
2290            m_ppu2_open_bus &= 0xfe;
2291            m_ppu2_open_bus |= (m_beam.latch_vert >> 8) & 0x01;
2292         }
2293         else
2294         {
2295            m_ppu2_open_bus = m_beam.latch_vert & 0xff;
2296         }
2297         m_read_opvct ^= 1;
2298         return m_ppu2_open_bus;
2299      case STAT77:    /* PPU status flag and version number */
2300         value = m_stat77 & 0xc0; // 0x80 & 0x40 are Time Over / Range Over Sprite flags, set by the video code
2301         // 0x20 - Master/slave mode select. Little is known about this bit. We always seem to read back 0 here.
2302         value |= (m_ppu1_open_bus & 0x10);
2303         value |= (m_ppu1_version & 0x0f);
2304         m_stat77 = value;  // not sure if this is needed...
2305         m_ppu1_open_bus = value;
2306         return m_ppu1_open_bus;
2307      case STAT78:    /* PPU status flag and version number */
2308         m_read_ophct = 0;
2309         m_read_opvct = 0;
2310         if (wrio_bit7)
2311            m_stat78 &= ~0x40; //clear ext latch if bit 7 of WRIO is set
2312         m_stat78 = (m_stat78 & ~0x2f) | (m_ppu2_open_bus & 0x20) | (m_ppu2_version & 0x0f);
2313         m_ppu2_open_bus = m_stat78;
2314         return m_ppu2_open_bus;
2315   }
2316
2317   /* note: remaining registers (Namely TM in Super Kick Boxing) returns MDR open bus, not PPU Open Bus! */
2318   return m_openbus_cb(space, 0);
2319}
2320
2321
2322void snes_ppu_device::write(address_space &space, UINT32 offset, UINT8 data)
2323{
2324   switch (offset)
2325   {
2326      case INIDISP:   /* Initial settings for screen */
2327         if ((m_screen_disabled & 0x80) && (!(data & 0x80))) //a 1->0 force blank transition causes a reset OAM address
2328         {
2329            space.write_byte(OAMADDL, m_oam.saved_address_low);
2330            space.write_byte(OAMADDH, m_oam.saved_address_high);
2331            m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
2332         }
2333         m_screen_disabled = data & 0x80;
2334         m_screen_brightness = (data & 0x0f) + 1;
2335         break;
2336      case OBSEL:     /* Object size and data area designation */
2337         m_oam.next_charmap = (data & 0x03) << 1;
2338         m_oam.next_name_select = (((data & 0x18) >> 3) * 0x1000) << 1;
2339         m_oam.next_size = (data & 0xe0) >> 5;
2340         break;
2341      case OAMADDL:   /* Address for accessing OAM (low) */
2342         m_oam.saved_address_low = data;
2343         m_oam.address = (m_oam.address & 0xff00) + data;
2344         m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
2345         PPU_REG(OAMDATA) = 0;
2346         break;
2347      case OAMADDH:   /* Address for accessing OAM (high) */
2348         m_oam.saved_address_high = data;
2349         m_oam.address = (m_oam.address & 0x00ff) | ((data & 0x01) << 8);
2350         m_oam.priority_rotation = BIT(data, 7);
2351         m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
2352         PPU_REG(OAMDATA) = 0;
2353         break;
2354      case OAMDATA:   /* Data for OAM write (DW) */
2355         if (m_oam.address >= 0x100)
2356            oam_write(space, m_oam.address, data);
2357         else
2358         {
2359            if (!PPU_REG(OAMDATA))
2360               m_oam.write_latch = data;
2361            else
2362            {
2363               // in this case, we not only write data to the upper byte of the word,
2364               // but also m_oam.write_latch to the lower byte (recall that
2365               // PPU_REG(OAMDATA) is used to select high/low byte)
2366               oam_write(space, m_oam.address, data);
2367               PPU_REG(OAMDATA) = 0;
2368               oam_write(space, m_oam.address, m_oam.write_latch);
2369               PPU_REG(OAMDATA) = 1;
2370            }
2371         }
2372         PPU_REG(OAMDATA) = (PPU_REG(OAMDATA) + 1) % 2;
2373         if (!PPU_REG(OAMDATA))
2374         {
2375            m_oam.address++;
2376            m_oam.address &= 0x1ff;
2377            m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
2378         }
2379         return;
2380      case BGMODE:    /* BG mode and character size settings */
2381         m_mode = data & 0x07;
2382         dynamic_res_change(space.machine());
2383         m_bg3_priority_bit = BIT(data, 3);
2384         m_layer[SNES_BG1].tile_size = BIT(data, 4);
2385         m_layer[SNES_BG2].tile_size = BIT(data, 5);
2386         m_layer[SNES_BG3].tile_size = BIT(data, 6);
2387         m_layer[SNES_BG4].tile_size = BIT(data, 7);
2388         m_update_offsets = 1;
2389         break;
2390      case MOSAIC:    /* Size and screen designation for mosaic */
2391         m_mosaic_size = (data & 0xf0) >> 4;
2392         m_layer[SNES_BG1].mosaic_enabled = BIT(data, 0);
2393         m_layer[SNES_BG2].mosaic_enabled = BIT(data, 1);
2394         m_layer[SNES_BG3].mosaic_enabled = BIT(data, 2);
2395         m_layer[SNES_BG4].mosaic_enabled = BIT(data, 3);
2396         break;
2397      case BG1SC:     /* Address for storing SC data BG1 SC size designation */
2398      case BG2SC:     /* Address for storing SC data BG2 SC size designation  */
2399      case BG3SC:     /* Address for storing SC data BG3 SC size designation  */
2400      case BG4SC:     /* Address for storing SC data BG4 SC size designation  */
2401         m_layer[offset - BG1SC].tilemap = data & 0xfc;
2402         m_layer[offset - BG1SC].tilemap_size = data & 0x3;
2403         break;
2404      case BG12NBA:   /* Address for BG 1 and 2 character data */
2405         m_layer[SNES_BG1].charmap = (data & 0x0f);
2406         m_layer[SNES_BG2].charmap = (data & 0xf0) >> 4;
2407         break;
2408      case BG34NBA:   /* Address for BG 3 and 4 character data */
2409         m_layer[SNES_BG3].charmap = (data & 0x0f);
2410         m_layer[SNES_BG4].charmap = (data & 0xf0) >> 4;
2411         break;
2412
2413      // Anomie says "H Current = (Byte<<8) | (Prev&~7) | ((Current>>8)&7); V Current = (Current<<8) | Prev;" and Prev is shared by all scrolls but in Mode 7!
2414      case BG1HOFS:   /* BG1 - horizontal scroll (DW) */
2415         /* In Mode 0->6 we use ppu_last_scroll as Prev */
2416         m_layer[SNES_BG1].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG1].hoffs >> 8) & 7);
2417         m_ppu_last_scroll = data;
2418         /* In Mode 7 we use mode7_last_scroll as Prev */
2419         m_mode7.hor_offset = (data << 8) | (m_mode7_last_scroll & ~7) | ((m_mode7.hor_offset >> 8) & 7);
2420         m_mode7_last_scroll = data;
2421         m_update_offsets = 1;
2422         return;
2423      case BG1VOFS:   /* BG1 - vertical scroll (DW) */
2424         /* In Mode 0->6 we use ppu_last_scroll as Prev */
2425         m_layer[SNES_BG1].voffs = (data << 8) | m_ppu_last_scroll;
2426         m_ppu_last_scroll = data;
2427         /* In Mode 7 we use mode7_last_scroll as Prev */
2428         m_mode7.ver_offset = (data << 8) | m_mode7_last_scroll;
2429         m_mode7_last_scroll = data;
2430         m_update_offsets = 1;
2431         return;
2432      case BG2HOFS:   /* BG2 - horizontal scroll (DW) */
2433         m_layer[SNES_BG2].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG2].hoffs >> 8) & 7);
2434         m_ppu_last_scroll = data;
2435         m_update_offsets = 1;
2436         return;
2437      case BG2VOFS:   /* BG2 - vertical scroll (DW) */
2438         m_layer[SNES_BG2].voffs = (data << 8) | (m_ppu_last_scroll);
2439         m_ppu_last_scroll = data;
2440         m_update_offsets = 1;
2441         return;
2442      case BG3HOFS:   /* BG3 - horizontal scroll (DW) */
2443         m_layer[SNES_BG3].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG3].hoffs >> 8) & 7);
2444         m_ppu_last_scroll = data;
2445         m_update_offsets = 1;
2446         return;
2447      case BG3VOFS:   /* BG3 - vertical scroll (DW) */
2448         m_layer[SNES_BG3].voffs = (data << 8) | (m_ppu_last_scroll);
2449         m_ppu_last_scroll = data;
2450         m_update_offsets = 1;
2451         return;
2452      case BG4HOFS:   /* BG4 - horizontal scroll (DW) */
2453         m_layer[SNES_BG4].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG4].hoffs >> 8) & 7);
2454         m_ppu_last_scroll = data;
2455         m_update_offsets = 1;
2456         return;
2457      case BG4VOFS:   /* BG4 - vertical scroll (DW) */
2458         m_layer[SNES_BG4].voffs = (data << 8) | (m_ppu_last_scroll);
2459         m_ppu_last_scroll = data;
2460         m_update_offsets = 1;
2461         return;
2462      case VMAIN:     /* VRAM address increment value designation */
2463         m_vram_fgr_high = (data & 0x80);
2464         m_vram_fgr_increment = vram_fgr_inctab[data & 3];
2465
2466         if (data & 0xc)
2467         {
2468            int md = (data & 0xc) >> 2;
2469
2470            m_vram_fgr_count = vram_fgr_inccnts[md];         // 0x20, 0x40, 0x80
2471            m_vram_fgr_mask = (m_vram_fgr_count * 8) - 1; // 0xff, 0x1ff, 0x2ff
2472            m_vram_fgr_shift = vram_fgr_shiftab[md];         // 5, 6, 7
2473         }
2474         else
2475         {
2476            m_vram_fgr_count = 0;
2477         }
2478//          printf("VMAIN: high %x inc %x count %x mask %x shift %x\n", m_vram_fgr_high, m_vram_fgr_increment, m_vram_fgr_count, m_vram_fgr_mask, m_vram_fgr_shift);
2479         break;
2480      case VMADDL:    /* Address for VRAM read/write (low) */
2481         {
2482            UINT32 addr;
2483            m_vmadd = (m_vmadd & 0xff00) | (data << 0);
2484            addr = get_vram_address(space.machine());
2485            m_vram_read_buffer = vram_read(space, addr);
2486            m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
2487         }
2488         break;
2489      case VMADDH:    /* Address for VRAM read/write (high) */
2490         {
2491            UINT32 addr;
2492            m_vmadd = (m_vmadd & 0x00ff) | (data << 8);
2493            addr = get_vram_address(space.machine());
2494            m_vram_read_buffer = vram_read(space, addr);
2495            m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
2496         }
2497         break;
2498      case VMDATAL:   /* 2118: Data for VRAM write (low) */
2499         {
2500            UINT32 addr = get_vram_address(space.machine());
2501            vram_write(space, addr, data);
2502
2503            if (!m_vram_fgr_high)
2504               m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
2505         }
2506         return;
2507      case VMDATAH:   /* 2119: Data for VRAM write (high) */
2508         {
2509            UINT32 addr = get_vram_address(space.machine());
2510            vram_write(space, addr + 1, data);
2511
2512            if (m_vram_fgr_high)
2513               m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
2514         }
2515         return;
2516      case M7SEL:     /* Mode 7 initial settings */
2517         m_mode7.repeat = (data >> 6) & 3;
2518         m_mode7.vflip  = BIT(data, 1);
2519         m_mode7.hflip  = BIT(data, 0);
2520         break;
2521      /* As per Anomie's doc: Reg = (Current<<8) | Prev; and there is only one Prev, shared by these matrix regs and Mode 7 scroll regs */
2522      case M7A:       /* Mode 7 COS angle/x expansion (DW) */
2523         m_mode7.matrix_a = m_mode7_last_scroll + (data << 8);
2524         m_mode7_last_scroll = data;
2525         break;
2526      case M7B:       /* Mode 7 SIN angle/ x expansion (DW) */
2527         m_mode7.matrix_b = m_mode7_last_scroll + (data << 8);
2528         m_mode7_last_scroll = data;
2529         break;
2530      case M7C:       /* Mode 7 SIN angle/y expansion (DW) */
2531         m_mode7.matrix_c = m_mode7_last_scroll + (data << 8);
2532         m_mode7_last_scroll = data;
2533         break;
2534      case M7D:       /* Mode 7 COS angle/y expansion (DW) */
2535         m_mode7.matrix_d = m_mode7_last_scroll + (data << 8);
2536         m_mode7_last_scroll = data;
2537         break;
2538      case M7X:       /* Mode 7 x center position (DW) */
2539         m_mode7.origin_x = m_mode7_last_scroll + (data << 8);
2540         m_mode7_last_scroll = data;
2541         break;
2542      case M7Y:       /* Mode 7 y center position (DW) */
2543         m_mode7.origin_y = m_mode7_last_scroll + (data << 8);
2544         m_mode7_last_scroll = data;
2545         break;
2546      case CGADD:     /* Initial address for colour RAM writing */
2547         /* CGRAM is 16-bit, but when reading/writing we treat it as 8-bit, so we need to double the address */
2548         m_cgram_address = data << 1;
2549         break;
2550      case CGDATA:    /* Data for colour RAM */
2551         cgram_write(space, m_cgram_address, data);
2552         m_cgram_address = (m_cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
2553         break;
2554      case W12SEL:    /* Window mask settings for BG1-2 */
2555         if (data != PPU_REG(W12SEL))
2556         {
2557            m_layer[SNES_BG1].window1_invert  = BIT(data, 0);
2558            m_layer[SNES_BG1].window1_enabled = BIT(data, 1);
2559            m_layer[SNES_BG1].window2_invert  = BIT(data, 2);
2560            m_layer[SNES_BG1].window2_enabled = BIT(data, 3);
2561            m_layer[SNES_BG2].window1_invert  = BIT(data, 4);
2562            m_layer[SNES_BG2].window1_enabled = BIT(data, 5);
2563            m_layer[SNES_BG2].window2_invert  = BIT(data, 6);
2564            m_layer[SNES_BG2].window2_enabled = BIT(data, 7);
2565            m_update_windows = 1;
2566         }
2567         break;
2568      case W34SEL:    /* Window mask settings for BG3-4 */
2569         if (data != PPU_REG(W34SEL))
2570         {
2571            m_layer[SNES_BG3].window1_invert  = BIT(data, 0);
2572            m_layer[SNES_BG3].window1_enabled = BIT(data, 1);
2573            m_layer[SNES_BG3].window2_invert  = BIT(data, 2);
2574            m_layer[SNES_BG3].window2_enabled = BIT(data, 3);
2575            m_layer[SNES_BG4].window1_invert  = BIT(data, 4);
2576            m_layer[SNES_BG4].window1_enabled = BIT(data, 5);
2577            m_layer[SNES_BG4].window2_invert  = BIT(data, 6);
2578            m_layer[SNES_BG4].window2_enabled = BIT(data, 7);
2579            m_update_windows = 1;
2580         }
2581         break;
2582      case WOBJSEL:   /* Window mask settings for objects */
2583         if (data != PPU_REG(WOBJSEL))
2584         {
2585            m_layer[SNES_OAM].window1_invert  = BIT(data, 0);
2586            m_layer[SNES_OAM].window1_enabled = BIT(data, 1);
2587            m_layer[SNES_OAM].window2_invert  = BIT(data, 2);
2588            m_layer[SNES_OAM].window2_enabled = BIT(data, 3);
2589            m_layer[SNES_COLOR].window1_invert  = BIT(data, 4);
2590            m_layer[SNES_COLOR].window1_enabled = BIT(data, 5);
2591            m_layer[SNES_COLOR].window2_invert  = BIT(data, 6);
2592            m_layer[SNES_COLOR].window2_enabled = BIT(data, 7);
2593            m_update_windows = 1;
2594         }
2595         break;
2596      case WH0:       /* Window 1 left position */
2597         if (data != PPU_REG(WH0))
2598         {
2599            m_window1_left = data;
2600            m_update_windows = 1;
2601         }
2602         break;
2603      case WH1:       /* Window 1 right position */
2604         if (data != PPU_REG(WH1))
2605         {
2606            m_window1_right = data;
2607            m_update_windows = 1;
2608         }
2609         break;
2610      case WH2:       /* Window 2 left position */
2611         if (data != PPU_REG(WH2))
2612         {
2613            m_window2_left = data;
2614            m_update_windows = 1;
2615         }
2616         break;
2617      case WH3:       /* Window 2 right position */
2618         if (data != PPU_REG(WH3))
2619         {
2620            m_window2_right = data;
2621            m_update_windows = 1;
2622         }
2623         break;
2624      case WBGLOG:    /* Window mask logic for BG's */
2625         if (data != PPU_REG(WBGLOG))
2626         {
2627            m_layer[SNES_BG1].wlog_mask = data & 0x03;
2628            m_layer[SNES_BG2].wlog_mask = (data & 0x0c) >> 2;
2629            m_layer[SNES_BG3].wlog_mask = (data & 0x30) >> 4;
2630            m_layer[SNES_BG4].wlog_mask = (data & 0xc0) >> 6;
2631            m_update_windows = 1;
2632         }
2633         break;
2634      case WOBJLOG:   /* Window mask logic for objects */
2635         if (data != PPU_REG(WOBJLOG))
2636         {
2637            m_layer[SNES_OAM].wlog_mask = data & 0x03;
2638            m_layer[SNES_COLOR].wlog_mask = (data & 0x0c) >> 2;
2639            m_update_windows = 1;
2640         }
2641         break;
2642      case TM:        /* Main screen designation */
2643         m_layer[SNES_BG1].main_bg_enabled = BIT(data, 0);
2644         m_layer[SNES_BG2].main_bg_enabled = BIT(data, 1);
2645         m_layer[SNES_BG3].main_bg_enabled = BIT(data, 2);
2646         m_layer[SNES_BG4].main_bg_enabled = BIT(data, 3);
2647         m_layer[SNES_OAM].main_bg_enabled = BIT(data, 4);
2648         break;
2649      case TS:        /* Subscreen designation */
2650         m_layer[SNES_BG1].sub_bg_enabled = BIT(data, 0);
2651         m_layer[SNES_BG2].sub_bg_enabled = BIT(data, 1);
2652         m_layer[SNES_BG3].sub_bg_enabled = BIT(data, 2);
2653         m_layer[SNES_BG4].sub_bg_enabled = BIT(data, 3);
2654         m_layer[SNES_OAM].sub_bg_enabled = BIT(data, 4);
2655         break;
2656      case TMW:       /* Window mask for main screen designation */
2657         m_layer[SNES_BG1].main_window_enabled = BIT(data, 0);
2658         m_layer[SNES_BG2].main_window_enabled = BIT(data, 1);
2659         m_layer[SNES_BG3].main_window_enabled = BIT(data, 2);
2660         m_layer[SNES_BG4].main_window_enabled = BIT(data, 3);
2661         m_layer[SNES_OAM].main_window_enabled = BIT(data, 4);
2662         break;
2663      case TSW:       /* Window mask for subscreen designation */
2664         m_layer[SNES_BG1].sub_window_enabled = BIT(data, 0);
2665         m_layer[SNES_BG2].sub_window_enabled = BIT(data, 1);
2666         m_layer[SNES_BG3].sub_window_enabled = BIT(data, 2);
2667         m_layer[SNES_BG4].sub_window_enabled = BIT(data, 3);
2668         m_layer[SNES_OAM].sub_window_enabled = BIT(data, 4);
2669         break;
2670      case CGWSEL:    /* Initial settings for Fixed colour addition or screen addition */
2671         m_clip_to_black = (data >> 6) & 0x03;
2672         m_prevent_color_math = (data >> 4) & 0x03;
2673         m_sub_add_mode = BIT(data, 1);
2674         m_direct_color = BIT(data, 0);
2675#ifdef SNES_DBG_REG_W
2676         if ((data & 0x2) != (PPU_REG(CGWSEL) & 0x2))
2677            mame_printf_debug("Add/Sub Layer: %s\n", ((data & 0x2) >> 1) ? "Subscreen" : "Fixed colour");
2678#endif
2679         break;
2680      case CGADSUB:   /* Addition/Subtraction designation for each screen */
2681         m_color_modes = data & 0xc0;
2682         m_layer[SNES_BG1].color_math = BIT(data, 0);
2683         m_layer[SNES_BG2].color_math = BIT(data, 1);
2684         m_layer[SNES_BG3].color_math = BIT(data, 2);
2685         m_layer[SNES_BG4].color_math = BIT(data, 3);
2686         m_layer[SNES_OAM].color_math = BIT(data, 4);
2687         m_layer[SNES_COLOR].color_math = BIT(data, 5);
2688         break;
2689      case COLDATA:   /* Fixed colour data for fixed colour addition/subtraction */
2690         {
2691            /* Store it in the extra space we made in the CGRAM. It doesn't really go there, but it's as good a place as any. */
2692            UINT8 r, g, b;
2693
2694            /* Get existing value. */
2695            r = m_cgram[FIXED_COLOUR] & 0x1f;
2696            g = (m_cgram[FIXED_COLOUR] & 0x3e0) >> 5;
2697            b = (m_cgram[FIXED_COLOUR] & 0x7c00) >> 10;
2698            /* Set new value */
2699            if (data & 0x20)
2700               r = data & 0x1f;
2701            if (data & 0x40)
2702               g = data & 0x1f;
2703            if (data & 0x80)
2704               b = data & 0x1f;
2705            m_cgram[FIXED_COLOUR] = (r | (g << 5) | (b << 10));
2706         } break;
2707      case SETINI:    /* Screen mode/video select */
2708         m_interlace = (data & 0x01) ? 2 : 1;
2709         m_obj_interlace = (data & 0x02) ? 2 : 1;
2710         m_beam.last_visible_line = (data & 0x04) ? 240 : 225;
2711         m_pseudo_hires = BIT(data, 3);
2712         m_mode7.extbg = BIT(data, 6);
2713         dynamic_res_change(space.machine());
2714#ifdef SNES_DBG_REG_W
2715         if ((data & 0x8) != (PPU_REG(SETINI) & 0x8))
2716            mame_printf_debug("Pseudo 512 mode: %s\n", (data & 0x8) ? "on" : "off");
2717#endif
2718         break;
2719      }
2720
2721   PPU_REG(offset) = data;
2722}
2723
2724/***** Debug Functions *****/
2725
2726#if SNES_LAYER_DEBUG
2727
2728#define DEBUG_TOGGLE(bit, debug_settings, MSG1, MSG2) \
2729   if (BIT(toggles, bit) && !debug_settings)       \
2730   {                                               \
2731      debug_settings = 1;                       \
2732      popmessage MSG1;                          \
2733   }                                               \
2734   else if (!BIT(toggles, bit) && debug_settings)  \
2735   {                                               \
2736      debug_settings = 0;                       \
2737      popmessage MSG2;                          \
2738   }
2739
2740UINT8 snes_ppu_device::dbg_video( running_machine &machine, UINT16 curline )
2741{
2742   int i;
2743   UINT8 toggles = machine.root_device().ioport("DEBUG1")->read_safe(0);
2744   m_debug_options.select_pri[SNES_BG1] = (toggles & 0x03);
2745   m_debug_options.select_pri[SNES_BG2] = (toggles & 0x0c) >> 2;
2746   m_debug_options.select_pri[SNES_BG3] = (toggles & 0x30) >> 4;
2747   m_debug_options.select_pri[SNES_BG4] = (toggles & 0xc0) >> 6;
2748
2749   toggles = machine.root_device().ioport("DEBUG2")->read_safe(0);
2750   for (i = 0; i < 4; i++)
2751      DEBUG_TOGGLE(i, m_debug_options.bg_disabled[i], ("Debug: Disabled BG%d.\n", i + 1), ("Debug: Enabled BG%d.\n", i + 1))
2752   DEBUG_TOGGLE(4, m_debug_options.bg_disabled[SNES_OAM], ("Debug: Disabled OAM.\n"), ("Debug: Enabled OAM.\n"))
2753   DEBUG_TOGGLE(5, m_debug_options.draw_subscreen, ("Debug: Switched screens.\n"), ("Debug: Switched screens.\n"))
2754   DEBUG_TOGGLE(6, m_debug_options.colormath_disabled, ("Debug: Disabled Color Math.\n"), ("Debug: Enabled Color Math.\n"))
2755   DEBUG_TOGGLE(7, m_debug_options.windows_disabled, ("Debug: Disabled Window Masks.\n"), ("Debug: Enabled Window Masks.\n"))
2756
2757   toggles = machine.root_device().ioport("DEBUG4")->read_safe(0);
2758   for (i = 0; i < 8; i++)
2759      DEBUG_TOGGLE(i, m_debug_options.mode_disabled[i], ("Debug: Disabled Mode %d drawing.\n", i), ("Debug: Enabled Mode %d drawing.\n", i))
2760
2761   toggles = machine.root_device().ioport("DEBUG3")->read_safe(0);
2762   DEBUG_TOGGLE(2, m_debug_options.mosaic_disabled, ("Debug: Disabled Mosaic.\n"), ("Debug: Enabled Mosaic.\n"))
2763   m_debug_options.sprite_reversed = BIT(toggles, 7);
2764   m_debug_options.select_pri[SNES_OAM] = (toggles & 0x70) >> 4;
2765
2766#ifdef MAME_DEBUG
2767   /* Once per frame, log video properties */
2768   if (curline == 1)
2769   {
2770      static const char WINLOGIC[4] = { '|', '&', '^', '!' };
2771
2772      logerror("%s", m_debug_options.windows_disabled?" ":"W");
2773      logerror("%s1 %s%s%s%s%s%c%s%s%d%s %d %4X %4X",
2774            m_debug_options.bg_disabled[0]?" ":"*",
2775            (PPU_REG(TM) & 0x1)?"M":" ",
2776            (PPU_REG(TS) & 0x1)?"S":" ",
2777            (PPU_REG(CGADSUB) & 0x1)?"B":" ",
2778            (PPU_REG(TMW) & 0x1)?"m":" ",
2779            (PPU_REG(TSW) & 0x1)?"s":" ",
2780            WINLOGIC[(PPU_REG(WBGLOG) & 0x3)],
2781            (PPU_REG(W12SEL) & 0x2)?((PPU_REG(W12SEL) & 0x1)?"o":"i"):" ",
2782            (PPU_REG(W12SEL) & 0x8)?((PPU_REG(W12SEL) & 0x4)?"o":"i"):" ",
2783            m_layer[SNES_BG1].tile_size + 1,
2784            (PPU_REG(MOSAIC) & 0x1)?"m":" ",
2785            PPU_REG(BG1SC) & 0x3,
2786            (PPU_REG(BG1SC) & 0xfc) << 9,
2787            m_layer[SNES_BG1].charmap << 13);
2788      logerror("%s2 %s%s%s%s%s%c%s%s%d%s %d %4X %4X",
2789            m_debug_options.bg_disabled[1]?" ":"*",
2790            (PPU_REG(TM) & 0x2)?"M":" ",
2791            (PPU_REG(TS) & 0x2)?"S":" ",
2792            (PPU_REG(CGADSUB) & 0x2)?"B":" ",
2793            (PPU_REG(TMW) & 0x2)?"m":" ",
2794            (PPU_REG(TSW) & 0x2)?"s":" ",
2795            WINLOGIC[(PPU_REG(WBGLOG) & 0xc) >> 2],
2796            (PPU_REG(W12SEL) & 0x20)?((PPU_REG(W12SEL) & 0x10)?"o":"i"):" ",
2797            (PPU_REG(W12SEL) & 0x80)?((PPU_REG(W12SEL) & 0x40)?"o":"i"):" ",
2798            m_layer[SNES_BG2].tile_size + 1,
2799            (PPU_REG(MOSAIC) & 0x2)?"m":" ",
2800            PPU_REG(BG2SC) & 0x3,
2801            (PPU_REG(BG2SC) & 0xfc) << 9,
2802            m_layer[SNES_BG2].charmap << 13);
2803      logerror("%s3 %s%s%s%s%s%c%s%s%d%s%s%d %4X %4X",
2804            m_debug_options.bg_disabled[2]?" ":"*",
2805            (PPU_REG(TM) & 0x4)?"M":" ",
2806            (PPU_REG(TS) & 0x4)?"S":" ",
2807            (PPU_REG(CGADSUB) & 0x4)?"B":" ",
2808            (PPU_REG(TMW) & 0x4)?"m":" ",
2809            (PPU_REG(TSW) & 0x4)?"s":" ",
2810            WINLOGIC[(PPU_REG(WBGLOG) & 0x30)>>4],
2811            (PPU_REG(W34SEL) & 0x2)?((PPU_REG(W34SEL) & 0x1)?"o":"i"):" ",
2812            (PPU_REG(W34SEL) & 0x8)?((PPU_REG(W34SEL) & 0x4)?"o":"i"):" ",
2813            m_layer[SNES_BG3].tile_size + 1,
2814            (PPU_REG(MOSAIC) & 0x4)?"m":" ",
2815            (PPU_REG(BGMODE) & 0x8)?"P":" ",
2816            PPU_REG(BG3SC) & 0x3,
2817            (PPU_REG(BG3SC) & 0xfc) << 9,
2818            m_layer[SNES_BG3].charmap << 13);
2819      logerror("%s4 %s%s%s%s%s%c%s%s%d%s %d %4X %4X",
2820            m_debug_options.bg_disabled[3]?" ":"*",
2821            (PPU_REG(TM) & 0x8)?"M":" ",
2822            (PPU_REG(TS) & 0x8)?"S":" ",
2823            (PPU_REG(CGADSUB) & 0x8)?"B":" ",
2824            (PPU_REG(TMW) & 0x8)?"m":" ",
2825            (PPU_REG(TSW) & 0x8)?"s":" ",
2826            WINLOGIC[(PPU_REG(WBGLOG) & 0xc0)>>6],
2827            (PPU_REG(W34SEL) & 0x20)?((PPU_REG(W34SEL) & 0x10)?"o":"i"):" ",
2828            (PPU_REG(W34SEL) & 0x80)?((PPU_REG(W34SEL) & 0x40)?"o":"i"):" ",
2829            m_layer[SNES_BG4].tile_size + 1,
2830            (PPU_REG(MOSAIC) & 0x8)?"m":" ",
2831            PPU_REG(BG4SC) & 0x3,
2832            (PPU_REG(BG4SC) & 0xfc) << 9,
2833            m_layer[SNES_BG4].charmap << 13 );
2834      logerror("%sO %s%s%s%s%s%c%s%s       %4X",
2835            m_debug_options.bg_disabled[4]?" ":"*",
2836            (PPU_REG(TM) & 0x10)?"M":" ",
2837            (PPU_REG(TS) & 0x10)?"S":" ",
2838            (PPU_REG(CGADSUB) & 0x10)?"B":" ",
2839            (PPU_REG(TMW) & 0x10)?"m":" ",
2840            (PPU_REG(TSW) & 0x10)?"s":" ",
2841            WINLOGIC[(PPU_REG(WOBJLOG) & 0x3)],
2842            (PPU_REG(WOBJSEL) & 0x2)?((PPU_REG(WOBJSEL) & 0x1)?"o":"i"):" ",
2843            (PPU_REG(WOBJSEL) & 0x8)?((PPU_REG(WOBJSEL) & 0x4)?"o":"i"):" ",
2844            m_layer[SNES_OAM].charmap << 13 );
2845      logerror("%sB   %s  %c%s%s",
2846            m_debug_options.colormath_disabled?" ":"*",
2847            (PPU_REG(CGADSUB) & 0x20)?"B":" ",
2848            WINLOGIC[(PPU_REG(WOBJLOG) & 0xc)>>2],
2849            (PPU_REG(WOBJSEL) & 0x20)?((PPU_REG(WOBJSEL) & 0x10)?"o":"i"):" ",
2850            (PPU_REG(WOBJSEL) & 0x80)?((PPU_REG(WOBJSEL) & 0x40)?"o":"i"):" " );
2851      logerror("Flags: %s%s%s %s %2d", (PPU_REG(CGWSEL) & 0x2)?"S":"F", (PPU_REG(CGADSUB) & 0x80)?"-":"+", (PPU_REG(CGADSUB) & 0x40)?" 50%":"100%",(PPU_REG(CGWSEL) & 0x1)?"D":"P", (PPU_REG(MOSAIC) & 0xf0) >> 4 );
2852      logerror("SetINI: %s %s %s %s %s %s", (PPU_REG(SETINI) & 0x1)?" I":"NI", (PPU_REG(SETINI) & 0x2)?"P":"R", (PPU_REG(SETINI) & 0x4)?"240":"225",(PPU_REG(SETINI) & 0x8)?"512":"256",(PPU_REG(SETINI) & 0x40)?"E":"N",(PPU_REG(SETINI) & 0x80)?"ES":"NS" );
2853      logerror("Mode7: A %5d B %5d", m_mode7.matrix_a, m_mode7.matrix_b );
2854      logerror(" %s%s%s   C %5d D %5d", (PPU_REG(M7SEL) & 0xc0)?((PPU_REG(M7SEL) & 0x40)?"0":"C"):"R", (PPU_REG(M7SEL) & 0x1)?"H":" ", (PPU_REG(M7SEL) & 0x2)?"V":" ", m_mode7.matrix_c, m_mode7.matrix_d );
2855      logerror("       X %5d Y %5d", m_mode7.origin_x, m_mode7.origin_y );
2856   }
2857#endif
2858
2859   return 0;
2860}
2861#endif /* SNES_LAYER_DEBUG */
Property changes on: trunk/src/emu/video/snes_ppu.c
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/emu/video/snes_ppu.h
r0r29510
1/***************************************************************************
2
3        SNES PPU
4
5***************************************************************************/
6
7#pragma once
8
9#ifndef __SNES_PPU_H__
10#define __SNES_PPU_H__
11
12
13#define MCLK_NTSC   (21477272)  /* verified */
14#define MCLK_PAL    (21218370)  /* verified */
15
16#define DOTCLK_NTSC (MCLK_NTSC/4)
17#define DOTCLK_PAL  (MCLK_PAL/4)
18
19#define SNES_SCR_WIDTH        256       /* 32 characters 8 pixels wide */
20#define SNES_SCR_HEIGHT_NTSC  225       /* Can be 224 or 240 height */
21#define SNES_SCR_HEIGHT_PAL   240       /* ??? */
22#define SNES_VTOTAL_NTSC      262       /* Maximum number of lines for NTSC systems */
23#define SNES_VTOTAL_PAL       312       /* Maximum number of lines for PAL systems */
24#define SNES_HTOTAL           341       /* Maximum number pixels per line (incl. hblank) */
25
26#define SNES_NTSC             0x00
27#define SNES_PAL              0x10
28
29
30#define SNES_LAYER_DEBUG  0
31
32
33/* offset-per-tile modes */
34enum
35{
36   SNES_OPT_NONE = 0,
37   SNES_OPT_MODE2,
38   SNES_OPT_MODE4,
39   SNES_OPT_MODE6
40};
41
42/* layers */
43enum
44{
45   SNES_BG1 = 0,
46   SNES_BG2,
47   SNES_BG3,
48   SNES_BG4,
49   SNES_OAM,
50   SNES_COLOR
51};
52
53
54struct SNES_SCANLINE
55{
56   int enable, clip;
57   
58   UINT16 buffer[SNES_SCR_WIDTH];
59   UINT8  priority[SNES_SCR_WIDTH];
60   UINT8  layer[SNES_SCR_WIDTH];
61   UINT8  blend_exception[SNES_SCR_WIDTH];
62};
63
64// ======================> snes_ppu_device
65
66class snes_ppu_device :  public device_t,
67                     public device_video_interface
68{
69public:
70   // construction/destruction
71   snes_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
72
73   // inline configuration helpers
74   template<class _Object> static devcb2_base &static_set_open_bus_callback(device_t &device, _Object object) { return downcast<snes_ppu_device &>(device).m_openbus_cb.set_callback(object); }
75
76   UINT8 m_regs[0x40];
77   
78   SNES_SCANLINE m_scanlines[2];
79   
80   struct
81   {
82      /* clipmasks */
83      UINT8 window1_enabled, window1_invert;
84      UINT8 window2_enabled, window2_invert;
85      UINT8 wlog_mask;
86      /* color math enabled */
87      UINT8 color_math;
88     
89      UINT8 charmap;
90      UINT8 tilemap;
91      UINT8 tilemap_size;
92     
93      UINT8 tile_size;
94      UINT8 mosaic_enabled;   // actually used only for layers 0->3!
95     
96      UINT8 main_window_enabled;
97      UINT8 sub_window_enabled;
98      UINT8 main_bg_enabled;
99      UINT8 sub_bg_enabled;
100     
101      UINT16 hoffs;
102      UINT16 voffs;
103   } m_layer[6]; // this is for the BG1 - BG2 - BG3 - BG4 - OBJ - color layers
104   
105   struct
106   {
107      UINT8 address_low;
108      UINT8 address_high;
109      UINT8 saved_address_low;
110      UINT8 saved_address_high;
111      UINT16 address;
112      UINT16 priority_rotation;
113      UINT8 next_charmap;
114      UINT8 next_size;
115      UINT8 size;
116      UINT32 next_name_select;
117      UINT32 name_select;
118      UINT8 first_sprite;
119      UINT8 flip;
120      UINT16 write_latch;
121   } m_oam;
122   
123   struct
124   {
125      UINT16 latch_horz;
126      UINT16 latch_vert;
127      UINT16 current_horz;
128      UINT16 current_vert;
129      UINT8 last_visible_line;
130      UINT8 interlace_count;
131   } m_beam;
132   
133   struct
134   {
135      UINT8 repeat;
136      UINT8 hflip;
137      UINT8 vflip;
138      INT16 matrix_a;
139      INT16 matrix_b;
140      INT16 matrix_c;
141      INT16 matrix_d;
142      INT16 origin_x;
143      INT16 origin_y;
144      UINT16 hor_offset;
145      UINT16 ver_offset;
146      UINT8 extbg;
147   } m_mode7;
148   
149   struct OAM
150   {
151      UINT16 tile;
152      INT16 x, y;
153      UINT8 size, vflip, hflip, priority_bits, pal;
154      int height, width;
155   };
156   
157   struct OAM m_oam_spritelist[SNES_SCR_WIDTH / 2];
158   
159   UINT8 m_oam_itemlist[32];
160   
161   struct TILELIST {
162      INT16 x;
163      UINT16 priority, pal, tileaddr;
164      int hflip;
165   };
166   
167   struct TILELIST m_oam_tilelist[34];
168   
169#if SNES_LAYER_DEBUG
170   struct DEBUGOPTS
171   {
172      UINT8 bg_disabled[5];
173      UINT8 mode_disabled[8];
174      UINT8 draw_subscreen;
175      UINT8 windows_disabled;
176      UINT8 mosaic_disabled;
177      UINT8 colormath_disabled;
178      UINT8 sprite_reversed;
179      UINT8 select_pri[5];
180   };
181   struct DEBUGOPTS m_debug_options;
182#endif
183   
184   UINT8 m_mosaic_size;
185   UINT8 m_clip_to_black;
186   UINT8 m_prevent_color_math;
187   UINT8 m_sub_add_mode;
188   UINT8 m_bg3_priority_bit;
189   UINT8 m_direct_color;
190   UINT8 m_ppu_last_scroll;      /* as per Anomie's doc and Theme Park, all scroll regs shares (but mode 7 ones) the same
191                           'previous' scroll value */
192   UINT8 m_mode7_last_scroll;    /* as per Anomie's doc mode 7 scroll regs use a different value, shared with mode 7 matrix! */
193   
194   UINT8 m_ppu1_open_bus, m_ppu2_open_bus;
195   UINT8 m_ppu1_version, m_ppu2_version;
196   UINT8 m_window1_left, m_window1_right, m_window2_left, m_window2_right;
197   
198   UINT16 m_mosaic_table[16][4096];
199   UINT8 m_clipmasks[6][SNES_SCR_WIDTH];
200   UINT8 m_update_windows;
201   UINT8 m_update_offsets;
202   UINT8 m_update_oam_list;
203   UINT8 m_mode;
204   UINT8 m_interlace; //doubles the visible resolution
205   UINT8 m_obj_interlace;
206   UINT8 m_screen_brightness;
207   UINT8 m_screen_disabled;
208   UINT8 m_pseudo_hires;
209   UINT8 m_color_modes;
210   UINT8 m_stat77;
211   UINT8 m_stat78;
212   
213   UINT16                m_htmult;     /* in 512 wide, we run HTOTAL double and halve it on latching */
214   UINT16                m_cgram_address;  /* CGRAM address */
215   UINT8                 m_read_ophct;
216   UINT8                 m_read_opvct;
217   UINT16                m_vram_fgr_high;
218   UINT16                m_vram_fgr_increment;
219   UINT16                m_vram_fgr_count;
220   UINT16                m_vram_fgr_mask;
221   UINT16                m_vram_fgr_shift;
222   UINT16                m_vram_read_buffer;
223   UINT16                m_vmadd;
224   
225   inline UINT16 get_bgcolor(UINT8 direct_colors, UINT16 palette, UINT8 color);
226   inline void set_scanline_pixel(int screen, INT16 x, UINT16 color, UINT8 priority, UINT8 layer, int blend);
227   inline void draw_bgtile_lores(UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority);
228   inline void draw_bgtile_hires(UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority);
229   inline void draw_oamtile(INT16 ii, UINT8 colour, UINT16 pal, UINT8 priority);
230   inline void draw_tile(UINT8 planes, UINT8 layer, UINT32 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT8 direct_colors, UINT16 pal, UINT8 hires);
231   inline UINT32 get_tmap_addr(UINT8 layer, UINT8 tile_size, UINT32 base, UINT32 x, UINT32 y);
232   inline void update_line(UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a, UINT8 color_depth, UINT8 hires, UINT8 offset_per_tile, UINT8 direct_colors);
233   void update_line_mode7(UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a);
234   void update_obsel(void);
235   void oam_list_build(void);
236   int is_sprite_on_scanline(UINT16 curline, UINT8 sprite);
237   void update_objects_rto(UINT16 curline);
238   void update_objects(UINT8 priority_oam0, UINT8 priority_oam1, UINT8 priority_oam2, UINT8 priority_oam3);
239   void update_mode_0(UINT16 curline);
240   void update_mode_1(UINT16 curline);
241   void update_mode_2(UINT16 curline);
242   void update_mode_3(UINT16 curline);
243   void update_mode_4(UINT16 curline);
244   void update_mode_5(UINT16 curline);
245   void update_mode_6(UINT16 curline);
246   void update_mode_7(UINT16 curline);
247   void draw_screens(UINT16 curline);
248   void update_windowmasks(void);
249   void update_offsets(void);
250   inline void draw_blend(UINT16 offset, UINT16 *colour, UINT8 prevent_color_math, UINT8 black_pen_clip, int switch_screens);
251   void refresh_scanline(running_machine &machine, bitmap_rgb32 &bitmap, UINT16 curline);
252   
253   void latch_counters(running_machine &machine);
254   void dynamic_res_change(running_machine &machine);
255   inline UINT32 get_vram_address(running_machine &machine);
256   UINT8 dbg_video(running_machine &machine, UINT16 curline);
257   
258   UINT8 read(address_space &space, UINT32 offset, UINT8 wrio_bit7);
259   void write(address_space &space, UINT32 offset, UINT8 data);
260   
261   DECLARE_READ8_MEMBER( oam_read );
262   DECLARE_WRITE8_MEMBER( oam_write );
263   DECLARE_READ8_MEMBER( cgram_read );
264   DECLARE_WRITE8_MEMBER( cgram_write );
265   DECLARE_READ8_MEMBER( vram_read );
266   DECLARE_WRITE8_MEMBER( vram_write );
267   UINT16 *m_oam_ram;     /* Object Attribute Memory */
268   UINT16 *m_cgram;   /* Palette RAM */
269   UINT8  *m_vram;    /* Video RAM (TODO: Should be 16-bit, but it's easier this way) */
270
271protected:
272   // device-level overrides
273   virtual void device_start();
274   virtual void device_reset() {};   
275
276private:
277   devcb2_read16  m_openbus_cb;
278};
279
280
281// device type definition
282extern const device_type SNES_PPU;
283
284
285/***************************************************************************
286 INTERFACE CONFIGURATION MACROS
287 ***************************************************************************/
288
289#define MCFG_SNES_PPU_OPENBUS_CB(_read) \
290   devcb = &snes_ppu_device::static_set_open_bus_callback(*device, DEVCB2_##_read);
291
292#endif
Property changes on: trunk/src/emu/video/snes_ppu.h
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/mess/drivers/snes.c
r29509r29510
14611461   INT16 y = ioport(portnames[port][2])->read();
14621462
14631463   /* these are the theoretical boundaries, but we currently are always onscreen... */
1464   if (x < 0 || x >= SNES_SCR_WIDTH || y < 0 || y >= m_ppu.m_beam.last_visible_line)
1464   if (x < 0 || x >= SNES_SCR_WIDTH || y < 0 || y >= m_ppu->m_beam.last_visible_line)
14651465      m_scope[port].offscreen = 1;
14661466   else
14671467      m_scope[port].offscreen = 0;
r29509r29510
14801480
14811481   if (y < 0)
14821482      y = 0;
1483   if (y > (m_ppu.m_beam.last_visible_line - 1))
1484      y = m_ppu.m_beam.last_visible_line - 1;
1483   if (y > (m_ppu->m_beam.last_visible_line - 1))
1484      y = m_ppu->m_beam.last_visible_line - 1;
14851485
1486   m_ppu.m_beam.latch_horz = x;
1487   m_ppu.m_beam.latch_vert = y;
1488   m_ppu.m_stat78 |= 0x40;
1486   m_ppu->m_beam.latch_horz = x;
1487   m_ppu->m_beam.latch_vert = y;
1488   m_ppu->m_stat78 |= 0x40;
14891489}
14901490
14911491void snes_console_state::input_read_sscope(int port)
r29509r29510
19571957   MCFG_SCREEN_RAW_PARAMS(DOTCLK_NTSC * 2, SNES_HTOTAL * 2, 0, SNES_SCR_WIDTH * 2, SNES_VTOTAL_NTSC, 0, SNES_SCR_HEIGHT_NTSC)
19581958   MCFG_SCREEN_UPDATE_DRIVER( snes_state, screen_update )
19591959
1960   MCFG_DEVICE_ADD("ppu", SNES_PPU, 0)
1961   MCFG_SNES_PPU_OPENBUS_CB(READ8(snes_state, snes_open_bus_r))
1962   MCFG_VIDEO_SET_SCREEN("screen")
1963
19601964   /* sound hardware */
19611965   MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
19621966   MCFG_SOUND_ADD("spc700", SNES, 0)
trunk/src/mess/mess.mak
r29509r29510
301301VIDEOS += SED1200
302302VIDEOS += SED1330
303303VIDEOS += SED1520
304VIDEOS += SNES_PPU
304305VIDEOS += STVVDP
305306VIDEOS += T6A04
306307#VIDEOS += TLC34076
r29509r29510
795796   $(MAME_VIDEO)/ppu2c0x.o     \
796797   $(MAME_AUDIO)/snes_snd.o    \
797798   $(MAME_MACHINE)/snes.o      \
798   $(MAME_VIDEO)/snes.o        \
799799   $(MAME_MACHINE)/n64.o       \
800800   $(MAME_VIDEO)/n64.o         \
801801   $(MAME_VIDEO)/rdpblend.o    \

Previous 199869 Revisions Next


© 1997-2024 The MAME Team