summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/315_5313.h
blob: be71b90dd6bff7d5a1fe15ab2b71527cd203b8af (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// license:BSD-3-Clause
// copyright-holders:David Haywood
/* Sega Megadrive / Genesis VDP */
#ifndef MAME_VIDEO_315_5313_H
#define MAME_VIDEO_315_5313_H

#pragma once

#include "video/315_5124.h"
#include "cpu/m68000/m68000.h"
#include "machine/timer.h"


#define MCFG_SEGA315_5313_IS_PAL(_bool) \
	sega315_5313_device::set_signal_type(*device, _bool);

#define MCFG_SEGA315_5313_INT_CB(_devcb) \
	devcb = &sega315_5313_device::set_int_callback(*device, DEVCB_##_devcb);

#define MCFG_SEGA315_5313_PAUSE_CB(_devcb) \
	devcb = &sega315_5313_device::set_pause_callback(*device, DEVCB_##_devcb);

#define MCFG_SEGA315_5313_SND_IRQ_CALLBACK(_write) \
	devcb = &sega315_5313_device::set_sndirqline_callback(*device, DEVCB_##_write);

#define MCFG_SEGA315_5313_LV6_IRQ_CALLBACK(_write) \
	devcb = &sega315_5313_device::set_lv6irqline_callback(*device, DEVCB_##_write);

#define MCFG_SEGA315_5313_LV4_IRQ_CALLBACK(_write) \
	devcb = &sega315_5313_device::set_lv4irqline_callback(*device, DEVCB_##_write);

#define MCFG_SEGA315_5313_ALT_TIMING(_data) \
	sega315_5313_device::set_alt_timing(*device, _data);

#define MCFG_SEGA315_5313_PAL_WRITE_BASE(_data) \
	sega315_5313_device::set_palwrite_base(*device, _data);

#define MCFG_SEGA315_5313_PALETTE(_palette_tag) \
	sega315_5313_device::static_set_palette_tag(*device, "^" _palette_tag);


// Temporary solution while 32x VDP mixing and scanline interrupting is moved outside MD VDP
#define MCFG_SEGA315_5313_32X_SCANLINE_CB(_class, _method) \
	sega315_5313_device::set_md_32x_scanline(*device, sega315_5313_device::md_32x_scanline_delegate(&_class::_method, #_class "::" #_method, this));

#define MCFG_SEGA315_5313_32X_INTERRUPT_CB(_class, _method) \
	sega315_5313_device::set_md_32x_interrupt(*device, sega315_5313_device::md_32x_interrupt_delegate(&_class::_method, #_class "::" #_method, this));

#define MCFG_SEGA315_5313_32X_SCANLINE_HELPER_CB(_class, _method) \
	sega315_5313_device::set_md_32x_scanline_helper(*device, sega315_5313_device::md_32x_scanline_helper_delegate(&_class::_method, #_class "::" #_method, this));


class sega315_5313_device : public sega315_5124_device
{
public:
	typedef device_delegate<void (int x, uint32_t priority, uint16_t &lineptr)> md_32x_scanline_delegate;
	typedef device_delegate<void (int scanline, int irq6)> md_32x_interrupt_delegate;
	typedef device_delegate<void (int scanline)> md_32x_scanline_helper_delegate;

	sega315_5313_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	template <class Object> static devcb_base &set_sndirqline_callback(device_t &device, Object &&cb) { return downcast<sega315_5313_device &>(device).m_sndirqline_callback.set_callback(std::forward<Object>(cb)); }
	template <class Object> static devcb_base &set_lv6irqline_callback(device_t &device, Object &&cb) { return downcast<sega315_5313_device &>(device).m_lv6irqline_callback.set_callback(std::forward<Object>(cb)); }
	template <class Object> static devcb_base &set_lv4irqline_callback(device_t &device, Object &&cb) { return downcast<sega315_5313_device &>(device).m_lv4irqline_callback.set_callback(std::forward<Object>(cb)); }
	static void set_alt_timing(device_t &device, int use_alt_timing);
	static void set_palwrite_base(device_t &device, int palwrite_base);
	static void static_set_palette_tag(device_t &device, const char *tag);

	static void set_md_32x_scanline(device_t &device, md_32x_scanline_delegate &&cb) { downcast<sega315_5313_device &>(device).m_32x_scanline_func = std::move(cb); }
	static void set_md_32x_interrupt(device_t &device, md_32x_interrupt_delegate &&cb) { downcast<sega315_5313_device &>(device).m_32x_interrupt_func = std::move(cb); }
	static void set_md_32x_scanline_helper(device_t &device, md_32x_scanline_helper_delegate &&cb) { downcast<sega315_5313_device &>(device).m_32x_scanline_helper_func = std::move(cb); }

	int m_use_alt_timing; // use MAME scanline timer instead, render only one scanline to a single line buffer, to be rendered by a partial update call.. experimental

	int m_palwrite_base; // if we want to write to the actual MAME palette..

	DECLARE_READ16_MEMBER( vdp_r );
	DECLARE_WRITE16_MEMBER( vdp_w );

	int get_scanline_counter();

	TIMER_CALLBACK_MEMBER(render_scanline);
	void vdp_handle_scanline_callback(int scanline);
	TIMER_CALLBACK_MEMBER(irq6_on_timer_callback);
	TIMER_CALLBACK_MEMBER(irq4_on_timer_callback);
	void vdp_handle_eof();
	void device_reset_old();
	void vdp_clear_irq6_pending() { m_irq6_pending = 0; };
	void vdp_clear_irq4_pending() { m_irq4_pending = 0; };

	// set some VDP variables at start (shall be moved to a device interface?)
	void set_scanline_counter(int scanline) { m_scanline_counter = scanline; }
	void set_total_scanlines(int total) { m_base_total_scanlines = total; }
	void set_framerate(int rate) { m_framerate = rate; }
	void set_vdp_pal(bool pal) { m_vdp_pal = pal ? 1 : 0; }
	void set_use_cram(int cram) { m_use_cram = cram; }
	void set_dma_delay(int delay) { m_dma_delay = delay; }
	int get_framerate() { return m_framerate; }
	int get_imode() { return m_imode; }


	void vdp_clear_bitmap()
	{
		if (m_render_bitmap)
			m_render_bitmap->fill(0);
	}

	std::unique_ptr<bitmap_ind16> m_render_bitmap;
	std::unique_ptr<uint16_t[]> m_render_line;
	std::unique_ptr<uint16_t[]> m_render_line_raw;

	TIMER_DEVICE_CALLBACK_MEMBER( megadriv_scanline_timer_callback_alt_timing );
	TIMER_DEVICE_CALLBACK_MEMBER( megadriv_scanline_timer_callback );
	timer_device* m_megadriv_scanline_timer;

	inline uint16_t vdp_get_word_from_68k_mem(uint32_t source);

protected:
	virtual void device_start() override;
	virtual void device_reset() override;
	virtual void device_add_mconfig(machine_config &config) override;

	// called when we hit 240 and 241 (used to control the z80 irq line on genesis, or the main irq on c2)
	devcb_write_line m_sndirqline_callback;
	devcb_write_line m_lv6irqline_callback;
	devcb_write_line m_lv4irqline_callback;

	md_32x_scanline_delegate m_32x_scanline_func;
	md_32x_interrupt_delegate m_32x_interrupt_func;
	md_32x_scanline_helper_delegate m_32x_scanline_helper_func;

private:
	int m_command_pending; // 2nd half of command pending..
	uint16_t m_command_part1;
	uint16_t m_command_part2;
	uint8_t  m_vdp_code;
	uint16_t m_vdp_address;
	uint8_t m_vram_fill_pending;
	uint16_t m_vram_fill_length;
	int m_irq4counter;
	int m_imode_odd_frame;
	int m_sprite_collision;
	int m_irq6_pending;
	int m_irq4_pending;
	int m_scanline_counter;
	int m_vblank_flag;

	int m_imode;

	int m_visible_scanlines;
	int m_irq6_scanline;
	int m_z80irq_scanline;
	int m_total_scanlines;
	// this is only set at init: 262 for PAL, 313 for NTSC
	int m_base_total_scanlines;

	int m_framerate;
	int m_vdp_pal;
	int m_use_cram; // c2 uses it's own palette ram, so it sets this to 0
	int m_dma_delay;    // SVP and SegaCD have some 'lag' in DMA transfers

	std::unique_ptr<uint16_t[]> m_regs;
	std::unique_ptr<uint16_t[]> m_vram;
	std::unique_ptr<uint16_t[]> m_cram;
	std::unique_ptr<uint16_t[]> m_vsram;
	/* The VDP keeps a 0x400 byte on-chip cache of the Sprite Attribute Table
	   to speed up processing, Castlevania Bloodlines abuses this on the upside down level */
	std::unique_ptr<uint16_t[]> m_internal_sprite_attribute_table;

	// these are used internally by the VDP to schedule when after the start of a scanline
	// to trigger the various interrupts / rendering to our bitmap, bit of a hack really
	emu_timer* m_irq6_on_timer;
	emu_timer* m_irq4_on_timer;
	emu_timer* m_render_timer;

	uint16_t vdp_vram_r(void);
	uint16_t vdp_vsram_r(void);
	uint16_t vdp_cram_r(void);

	void insta_68k_to_cram_dma(uint32_t source,uint16_t length);
	void insta_68k_to_vsram_dma(uint32_t source,uint16_t length);
	void insta_68k_to_vram_dma(uint32_t source,int length);
	void insta_vram_copy(uint32_t source, uint16_t length);

	void vdp_vram_write(uint16_t data);
	void vdp_cram_write(uint16_t data);
	void write_cram_value(int offset, int data);
	void vdp_vsram_write(uint16_t data);

	void vdp_set_register(int regnum, uint8_t value);

	void handle_dma_bits();

	uint16_t get_hposition();
	uint16_t megadriv_read_hv_counters();

	uint16_t ctrl_port_r();
	uint16_t data_port_r();
	void data_port_w(int data);
	void ctrl_port_w(int data);
	void update_code_and_address(void);


	void render_spriteline_to_spritebuffer(int scanline);
	void render_videoline_to_videobuffer(int scanline);
	void render_videobuffer_to_screenbuffer(int scanline);

	/* variables used during emulation - not saved */
	std::unique_ptr<uint8_t[]> m_sprite_renderline;
	std::unique_ptr<uint8_t[]> m_highpri_renderline;
	std::unique_ptr<uint32_t[]> m_video_renderline;
	std::unique_ptr<uint16_t[]> m_palette_lookup;
	std::unique_ptr<uint16_t[]> m_palette_lookup_sprite; // for C2
	std::unique_ptr<uint16_t[]> m_palette_lookup_shadow;
	std::unique_ptr<uint16_t[]> m_palette_lookup_highlight;

	address_space *m_space68k;
	m68000_base_device* m_cpu68k;
};


DECLARE_DEVICE_TYPE(SEGA315_5313, sega315_5313_device)

#endif // MAME_VIDEO_315_5313_H