1 // license:BSD-3-Clause
2 // copyright-holders:Ernesto Corvi,Brad Oliver
3 /******************************************************************************
4 
5     Nintendo 2C0x PPU emulation.
6 
7     Written by Ernesto Corvi.
8     This code is heavily based on Brad Oliver's MESS implementation.
9 
10 ******************************************************************************/
11 
12 #ifndef MAME_VIDEO_PPU2C0X_H
13 #define MAME_VIDEO_PPU2C0X_H
14 
15 #pragma once
16 
17 ///*************************************************************************
18 //  MACROS / CONSTANTS
19 ///*************************************************************************
20 
21 // mirroring types
22 #define PPU_MIRROR_NONE       0
23 #define PPU_MIRROR_VERT       1
24 #define PPU_MIRROR_HORZ       2
25 #define PPU_MIRROR_HIGH       3
26 #define PPU_MIRROR_LOW        4
27 #define PPU_MIRROR_4SCREEN    5 // Same effect as NONE, but signals that we should never mirror
28 
29 #define PPU_DRAW_BG       0
30 #define PPU_DRAW_OAM      1
31 
32 /* constant definitions */
33 #define VISIBLE_SCREEN_WIDTH         (32*8) /* Visible screen width */
34 #define VISIBLE_SCREEN_HEIGHT        (30*8) /* Visible screen height */
35 #define SPRITERAM_SIZE          0x100   /* spriteram size */
36 
37 ///*************************************************************************
38 //  TYPE DEFINITIONS
39 ///*************************************************************************
40 
41 // ======================> ppu2c0x_device
42 
43 class ppu2c0x_device :  public device_t,
44 						public device_memory_interface,
45 						public device_video_interface
46 {
47 public:
48 	typedef device_delegate<void (int scanline, int vblank, int blanked)> scanline_delegate;
49 	typedef device_delegate<void (int scanline, int vblank, int blanked)> hblank_delegate;
50 	typedef device_delegate<void (int *ppu_regs)> nmi_delegate;
51 	typedef device_delegate<int (int address, int data)> vidaccess_delegate;
52 	typedef device_delegate<void (offs_t offset)> latch_delegate;
53 
54 	enum
55 	{
56 		NTSC_SCANLINES_PER_FRAME     = 262,
57 		PAL_SCANLINES_PER_FRAME      = 312,
58 		VS_CLONE_SCANLINES_PER_FRAME = 280,
59 
60 		BOTTOM_VISIBLE_SCANLINE        = 239,
61 		VBLANK_FIRST_SCANLINE          = 241,
62 		VBLANK_FIRST_SCANLINE_PALC     = 291,
63 		VBLANK_FIRST_SCANLINE_VS_CLONE = 240,
64 		VBLANK_LAST_SCANLINE_NTSC      = 260,
65 		VBLANK_LAST_SCANLINE_PAL       = 310,
66 		VBLANK_LAST_SCANLINE_VS_CLONE  = 279
67 
68 		// Both the scanline immediately before and immediately after VBLANK
69 		// are non-rendering and non-vblank.
70 	};
71 
72 	virtual uint8_t read(offs_t offset);
73 	virtual void write(offs_t offset, uint8_t data);
74 	virtual uint8_t palette_read(offs_t offset);
75 	virtual void palette_write(offs_t offset, uint8_t data);
76 
set_cpu_tag(T && tag)77 	template <typename T> void set_cpu_tag(T &&tag) { m_cpu.set_tag(std::forward<T>(tag)); }
int_callback()78 	auto int_callback() { return m_int_callback.bind(); }
79 
80 	/* routines */
81 	void apply_color_emphasis_and_clamp(bool is_pal_or_dendy, int color_emphasis, double& R, double& G, double& B);
82 	rgb_t nespal_to_RGB(int color_intensity, int color_num, int color_emphasis, bool is_pal_or_dendy);
83 	virtual void init_palette_tables();
84 
85 	virtual void read_tile_plane_data(int address, int color);
86 	virtual void shift_tile_plane_data(uint8_t &pix);
87 	virtual void draw_tile_pixel(uint8_t pix, int color, uint32_t back_pen, uint32_t *&dest);
88 	virtual void draw_tile(uint8_t *line_priority, int color_byte, int color_bits, int address, int start_x, uint32_t back_pen, uint32_t *&dest);
89 	virtual void draw_background( uint8_t *line_priority );
90 	virtual void draw_back_pen(uint32_t* dst, int back_pen);
91 	void draw_background_pen();
92 
93 	virtual void read_sprite_plane_data(int address);
94 	virtual void make_sprite_pixel_data(uint8_t &pixel_data, int flipx);
95 	virtual void draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_rgb32 &bitmap);
96 	virtual bool is_spritepixel_opaque(int pixel_data, int color);
97 	virtual void draw_sprite_pixel_low(bitmap_rgb32& bitmap, int pixel_data, int pixel, int sprite_xpos, int color, int sprite_index, uint8_t* line_priority);
98 	virtual void draw_sprite_pixel_high(bitmap_rgb32& bitmap, int pixel_data, int pixel, int sprite_xpos, int color, int sprite_index, uint8_t* line_priority);
99 	virtual void read_extra_sprite_bits(int sprite_index);
100 
101 	virtual int apply_sprite_pattern_page(int index1, int size);
102 	virtual void draw_sprites(uint8_t *line_priority);
103 	void render_scanline();
104 	virtual void scanline_increment_fine_ycounter();
105 	void update_visible_enabled_scanline();
106 	void update_visible_disabled_scanline();
107 	void update_visible_scanline();
108 	void update_scanline();
109 
110 	void spriteram_dma(address_space &space, const uint8_t page);
111 	void render(bitmap_rgb32 &bitmap, int flipx, int flipy, int sx, int sy, const rectangle &cliprect);
112 	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
113 
get_current_scanline()114 	int get_current_scanline() { return m_scanline; }
set_scanline_callback(T &&...args)115 	template <typename... T> void set_scanline_callback(T &&... args) { m_scanline_callback_proc.set(std::forward<T>(args)...); m_scanline_callback_proc.resolve(); /* FIXME: if this is supposed to be set at config time, it should be resolved on start */ }
set_hblank_callback(T &&...args)116 	template <typename... T> void set_hblank_callback(T &&... args) { m_hblank_callback_proc.set(std::forward<T>(args)...); m_hblank_callback_proc.resolve(); /* FIXME: if this is supposed to be set at config time, it should be resolved on start */ }
set_vidaccess_callback(T &&...args)117 	template <typename... T> void set_vidaccess_callback(T &&... args) { m_vidaccess_callback_proc.set(std::forward<T>(args)...); m_vidaccess_callback_proc.resolve(); /* FIXME: if this is supposed to be set at config time, it should be resolved on start */ }
set_scanlines_per_frame(int scanlines)118 	void set_scanlines_per_frame(int scanlines) { m_scanlines_per_frame = scanlines; }
119 
120 	// MMC5 has to be able to check this
is_sprite_8x16()121 	int is_sprite_8x16() { return m_regs[PPU_CONTROL0] & PPU_CONTROL0_SPRITE_SIZE; }
get_draw_phase()122 	int get_draw_phase() { return m_draw_phase; }
get_tilenum()123 	int get_tilenum() { return m_tilecount; }
124 
125 	//27/12/2002 (HACK!)
set_latch(T &&...args)126 	template <typename... T> void set_latch(T &&... args) { m_latch.set(std::forward<T>(args)...); m_latch.resolve(); /* FIXME: if this is supposed to be set at config time, it should be resolved on start */ }
127 
128 	//  void update_screen(bitmap_t &bitmap, const rectangle &cliprect);
129 
130 	// some bootleg / clone hardware appears to ignore this
use_sprite_write_limitation_disable()131 	void use_sprite_write_limitation_disable() { m_use_sprite_write_limitation = false; }
132 	uint16_t get_vram_dest();
133 	void set_vram_dest(uint16_t dest);
134 
135 	void ppu2c0x(address_map &map);
136 
in_vblanking()137 	bool in_vblanking() { return (m_scanline >= m_vblank_first_scanline - 1); }
138 protected:
139 	ppu2c0x_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock, address_map_constructor internal_map);
140 
141 	// registers definition
142 	enum
143 	{
144 		PPU_CONTROL0 = 0,
145 		PPU_CONTROL1,
146 		PPU_STATUS,
147 		PPU_SPRITE_ADDRESS,
148 		PPU_SPRITE_DATA,
149 		PPU_SCROLL,
150 		PPU_ADDRESS,
151 		PPU_DATA,
152 		PPU_MAX_REG
153 	};
154 
155 	// bit definitions for (some of) the registers
156 	enum
157 	{
158 		PPU_CONTROL0_INC               = 0x04,
159 		PPU_CONTROL0_SPR_SELECT        = 0x08,
160 		PPU_CONTROL0_CHR_SELECT        = 0x10,
161 		PPU_CONTROL0_SPRITE_SIZE       = 0x20,
162 		PPU_CONTROL0_NMI               = 0x80,
163 
164 		PPU_CONTROL1_DISPLAY_MONO      = 0x01,
165 		PPU_CONTROL1_BACKGROUND_L8     = 0x02,
166 		PPU_CONTROL1_SPRITES_L8        = 0x04,
167 		PPU_CONTROL1_BACKGROUND        = 0x08,
168 		PPU_CONTROL1_SPRITES           = 0x10,
169 		PPU_CONTROL1_COLOR_EMPHASIS    = 0xe0,
170 
171 		PPU_STATUS_8SPRITES            = 0x20,
172 		PPU_STATUS_SPRITE0_HIT         = 0x40,
173 		PPU_STATUS_VBLANK              = 0x80
174 	};
175 
176 	// construction/destruction
177 	ppu2c0x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock = 0);
178 
179 	virtual void device_start() override;
180 	virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
181 	virtual void device_config_complete() override;
182 
183 	// device_config_memory_interface overrides
184 	virtual space_config_vector memory_space_config() const override;
185 
186 	// address space configurations
187 	const address_space_config      m_space_config;
188 
189 	required_device<cpu_device> m_cpu;
190 
191 	void start_nopalram();
192 
193 	int                         m_scanlines_per_frame;  /* number of scanlines per frame */
194 	int                         m_security_value;       /* 2C05 protection */
195 	int                         m_vblank_first_scanline;  /* the very first scanline where VBLANK occurs */
196 
197 	// used in rendering
198 	uint8_t m_planebuf[2];
199 	int                         m_scanline;         /* scanline count */
200 	std::unique_ptr<uint8_t[]>  m_spriteram;           /* sprite ram */
201 
202 	int m_videoram_addr_mask;
203 	int m_global_refresh_mask;
204 	int m_line_write_increment_large;
205 	bool m_paletteram_in_ppuspace; // sh6578 doesn't have the palette in PPU space, so various side-effects don't apply
206 	std::vector<uint8_t>        m_palette_ram;          /* shouldn't be in main memory! */
207 	std::unique_ptr<bitmap_rgb32>                m_bitmap;          /* target bitmap */
208 	int                         m_regs[PPU_MAX_REG];        /* registers */
209 	int                         m_tile_page;            /* current tile page */
210 	int                         m_back_color;           /* background color */
211 	int                         m_refresh_data;         /* refresh-related */
212 	int                         m_x_fine;               /* refresh-related */
213 	int                         m_toggle;               /* used to latch hi-lo scroll */
214 	int                         m_tilecount;            /* MMC5 can change attributes to subsets of the 34 visible tiles */
215 	latch_delegate              m_latch;
216 
217 
218 	uint8_t readbyte(offs_t address);
219 
220 	uint32_t m_nespens[0x40*8];
221 private:
222 	static constexpr device_timer_id TIMER_HBLANK = 0;
223 	static constexpr device_timer_id TIMER_NMI = 1;
224 	static constexpr device_timer_id TIMER_SCANLINE = 2;
225 
226 	inline void writebyte(offs_t address, uint8_t data);
227 
228 
229 	scanline_delegate           m_scanline_callback_proc;   /* optional scanline callback */
230 	hblank_delegate             m_hblank_callback_proc; /* optional hblank callback */
231 	vidaccess_delegate          m_vidaccess_callback_proc;  /* optional video access callback */
232 	devcb_write_line            m_int_callback;         /* nmi access callback from interface */
233 
234 	int                         m_refresh_latch;        /* refresh-related */
235 	int                         m_add;              /* vram increment amount */
236 	int                         m_videomem_addr;        /* videomem address pointer */
237 	int                         m_data_latch;           /* latched videomem data */
238 	int                         m_buffered_data;
239 	int                         m_sprite_page;          /* current sprite page */
240 	int                         m_scan_scale;           /* scan scale */
241 	int                         m_draw_phase;           /* MMC5 uses different regs for BG and OAM */
242 
243 	// timers
244 	emu_timer                   *m_hblank_timer;        /* hblank period at end of each scanline */
245 	emu_timer                   *m_nmi_timer;           /* NMI timer */
246 	emu_timer                   *m_scanline_timer;      /* scanline timer */
247 
248 	bool m_use_sprite_write_limitation;
249 };
250 
251 class ppu2c0x_rgb_device : public ppu2c0x_device {
252 protected:
253 	ppu2c0x_rgb_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock = 0);
254 
255 	virtual void init_palette_tables() override;
256 
257 private:
258 	required_region_ptr<uint8_t> m_palette_data;
259 };
260 
261 class ppu2c02_device : public ppu2c0x_device {
262 public:
263 	ppu2c02_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
264 };
265 
266 class ppu2c03b_device : public ppu2c0x_rgb_device {
267 public:
268 	ppu2c03b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
269 };
270 
271 class ppu2c04_device : public ppu2c0x_rgb_device {
272 public:
273 	ppu2c04_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
274 };
275 
276 class ppu2c07_device : public ppu2c0x_device {
277 public:
278 	ppu2c07_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
279 };
280 
281 class ppupalc_device : public ppu2c0x_device {
282 public:
283 	ppupalc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
284 };
285 
286 class ppu2c05_01_device : public ppu2c0x_rgb_device {
287 public:
288 	ppu2c05_01_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
289 };
290 
291 class ppu2c05_02_device : public ppu2c0x_rgb_device {
292 public:
293 	ppu2c05_02_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
294 };
295 
296 class ppu2c05_03_device : public ppu2c0x_rgb_device {
297 public:
298 	ppu2c05_03_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
299 };
300 
301 class ppu2c05_04_device : public ppu2c0x_rgb_device {
302 public:
303 	ppu2c05_04_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
304 };
305 
306 class ppu2c04_clone_device : public ppu2c0x_device {
307 public:
308 	ppu2c04_clone_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
309 
310 	virtual uint8_t read(offs_t offset) override;
311 	virtual void write(offs_t offset, uint8_t data) override;
312 
313 	virtual void draw_background(uint8_t *line_priority) override;
314 	virtual void draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_rgb32 &bitmap) override;
315 	virtual void draw_sprites(uint8_t *line_priority) override;
316 
317 	virtual void init_palette_tables() override;
318 
319 protected:
320 	virtual void device_start() override;
321 
322 private:
323 	required_region_ptr<uint8_t> m_palette_data;
324 
325 	std::unique_ptr<uint8_t[]>   m_spritebuf; /* buffered sprite ram for next frame */
326 };
327 
328 // device type definition
329 //extern const device_type PPU_2C0X;
330 DECLARE_DEVICE_TYPE(PPU_2C02,    ppu2c02_device)       // NTSC NES
331 DECLARE_DEVICE_TYPE(PPU_2C03B,   ppu2c03b_device)      // Playchoice 10
332 DECLARE_DEVICE_TYPE(PPU_2C04,    ppu2c04_device)       // Vs. Unisystem
333 DECLARE_DEVICE_TYPE(PPU_2C07,    ppu2c07_device)       // PAL NES
334 DECLARE_DEVICE_TYPE(PPU_PALC,    ppupalc_device)       // PAL Clones
335 DECLARE_DEVICE_TYPE(PPU_2C05_01, ppu2c05_01_device)    // Vs. Unisystem (Ninja Jajamaru Kun)
336 DECLARE_DEVICE_TYPE(PPU_2C05_02, ppu2c05_02_device)    // Vs. Unisystem (Mighty Bomb Jack)
337 DECLARE_DEVICE_TYPE(PPU_2C05_03, ppu2c05_03_device)    // Vs. Unisystem (Gumshoe)
338 DECLARE_DEVICE_TYPE(PPU_2C05_04, ppu2c05_04_device)    // Vs. Unisystem (Top Gun)
339 DECLARE_DEVICE_TYPE(PPU_2C04C,   ppu2c04_clone_device) // Vs. Unisystem (Super Mario Bros. bootlegs)
340 
341 #endif // MAME_VIDEO_PPU2C0X_H
342