summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/gime.h
blob: 89f692f72546f9c8222245646c99aa987e857db5 (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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/*********************************************************************

    gime.h

    Implementation of CoCo GIME (Graphics Interrupt Memory Enhancement)
    video chip.

***************************************************************************/

#ifndef MAME_VIDEO_GIME_H
#define MAME_VIDEO_GIME_H

#pragma once

#include "video/mc6847.h"
#include "machine/6883sam.h"
#include "machine/ram.h"


//**************************************************************************
//  GIME CORE
//**************************************************************************

class cococart_slot_device;

class gime_device : public mc6847_friend_device, public sam6883_friend_device_interface
{
public:
	auto irq_wr_callback() { return m_write_irq.bind(); }
	auto firq_wr_callback() { return m_write_firq.bind(); }
	auto floating_bus_rd_callback() { return m_read_floating_bus.bind(); }

	// read/write
	DECLARE_READ8_MEMBER( bus_r ) { return read(offset); }
	DECLARE_WRITE8_MEMBER( bus_w ) { write(offset, data); }

	// used to turn on/off reading/writing to $FF40-$FF5F
	bool spare_chip_select_enabled(void) { return m_gime_registers[0] & 0x04 ? true : false; }

	// the GIME seems to intercept writes to $FF22 (not precisely sure how)
	void ff22_write(uint8_t data) { m_ff22_value = data; }

	// updates the cart ROM
	void update_cart_rom(void);

	/* updates the screen -- this will call begin_update(),
	   followed by update_row() repeatedly and after all row
	   updating is complete, end_update() */
	bool update_composite(bitmap_rgb32 &bitmap, const rectangle &cliprect);
	bool update_rgb(bitmap_rgb32 &bitmap, const rectangle &cliprect);

	// interrupt outputs
	bool firq_r() const { return m_firq != 0x00; }
	bool irq_r() const { return m_irq != 0x00; }

	// interrupt inputs
	void set_il0(bool value) { set_interrupt_value(INTERRUPT_EI0, value); }
	void set_il1(bool value) { set_interrupt_value(INTERRUPT_EI1, value); }
	void set_il2(bool value) { set_interrupt_value(INTERRUPT_EI2, value); }

protected:
	gime_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, const uint8_t *fontdata);

	// device-level overrides
	virtual void device_start(void) override;
	virtual void device_reset(void) override;
	virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
	virtual void device_pre_save(void) override;
	virtual void device_post_load(void) override;
	virtual ioport_constructor device_input_ports() const override;

	// other overrides
	virtual void new_frame(void) override;
	virtual void horizontal_sync_changed(bool line) override;
	virtual void enter_bottom_border(void) override;
	virtual void record_border_scanline(uint16_t physical_scanline) override;
	virtual void record_body_scanline(uint16_t physical_scanline, uint16_t logical_scanline) override;
	virtual void record_partial_body_scanline(uint16_t physical_scanline, uint16_t logical_scanline, int32_t start_clock, int32_t end_clock) override;

protected:
	typedef mc6847_friend_device super;

	struct scanline_record
	{
		uint8_t m_border;
		uint8_t m_line_in_row;
		uint8_t m_ff22_value;
		uint8_t m_ff98_value;
		uint8_t m_ff99_value;
		uint8_t m_mode[160];
		uint8_t m_data[160];
		uint16_t m_palette[160];
	};

	typedef uint32_t (gime_device::*get_data_func)(uint32_t, uint8_t *, uint8_t *);

	class palette_resolver
	{
	public:
		palette_resolver(gime_device &gime, const pixel_t *palette);
		const pixel_t *get_palette(uint16_t palette_rotation);
		pixel_t lookup(uint8_t color);

	private:
		gime_device &m_gime;
		const pixel_t *m_palette;
		pixel_t m_resolved_palette[16];
		int m_current_resolved_palette;
	};

	enum
	{
		INTERRUPT_TMR       = 0x20,
		INTERRUPT_HBORD     = 0x10,
		INTERRUPT_VBORD     = 0x08,
		INTERRUPT_EI2       = 0x04,
		INTERRUPT_EI1       = 0x02,
		INTERRUPT_EI0       = 0x01
	};

	enum timer_type_t
	{
		GIME_TIMER_HBORD,
		GIME_TIMER_CLOCK
	};

	// timer constants
	static const device_timer_id TIMER_GIME_CLOCK = 4;

	// statics
	static const uint8_t hires_font[128][12];

	// callbacks
	devcb_write_line   m_write_irq;
	devcb_write_line   m_write_firq;
	devcb_read8        m_read_floating_bus;

	// device state
	uint8_t                     m_gime_registers[16];
	uint8_t                     m_mmu[16];
	uint8_t                     m_ff22_value;
	uint8_t                     m_interrupt_value;
	uint8_t                     m_irq;
	uint8_t                     m_firq;
	uint16_t                    m_timer_value;
	bool                        m_is_blinking;
	bool                        m_composite_phase_invert;

	// video state
	bool                        m_legacy_video;
	uint32_t                    m_video_position;
	uint8_t                     m_line_in_row;
	scanline_record             m_scanlines[25+192+26];
	bool                        m_displayed_rgb;

	// palette state
	uint8_t                     m_palette_rotated[1024][16];
	uint16_t                    m_palette_rotated_position;
	bool                        m_palette_rotated_position_used;

	// incidentals
	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	emu_timer *                 m_gime_clock_timer;
	required_device<cococart_slot_device> m_cart_device;
	memory_bank *               m_read_banks[9];
	memory_bank *               m_write_banks[9];
	uint8_t *                   m_rom;
	required_memory_region      m_rom_region;
	uint8_t *                   m_cart_rom;
	uint32_t                    m_cart_size;
	pixel_t                     m_composite_palette[64];
	pixel_t                     m_composite_bw_palette[64];
	pixel_t                     m_rgb_palette[64];
	uint8_t                     m_dummy_bank[0x2000];

	// timer constants
	static const device_timer_id TIMER_FRAME = 0;
	static const device_timer_id TIMER_HSYNC_OFF = 1;
	static const device_timer_id TIMER_HSYNC_ON = 2;
	static const device_timer_id TIMER_FSYNC_OFF = 3;
	static const device_timer_id TIMER_FSYNC_ON = 4;

	// read/write
	uint8_t read(offs_t offset);
	uint8_t read_gime_register(offs_t offset);
	uint8_t read_mmu_register(offs_t offset);
	uint8_t read_palette_register(offs_t offset);
	uint8_t read_floating_bus(void);
	void write(offs_t offset, uint8_t data);
	void write_gime_register(offs_t offset, uint8_t data);
	void write_mmu_register(offs_t offset, uint8_t data);
	void write_palette_register(offs_t offset, uint8_t data);
	void write_sam_register(offs_t offset);

	// memory
	void update_memory(void);
	void update_memory(int bank);
	uint8_t *memory_pointer(uint32_t address);

	// interrupts
	void interrupt_rising_edge(uint8_t interrupt);
	void change_gime_irq(uint8_t data);
	void change_gime_firq(uint8_t data);

	ATTR_FORCE_INLINE void set_interrupt_value(uint8_t interrupt, bool value)
	{
		/* save the old interrupt value */
		uint8_t old_interrupt_value = m_interrupt_value;

		/* update the interrupt value */
		if (value)
			m_interrupt_value |= interrupt;
		else
			m_interrupt_value &= ~interrupt;

		/* was this a rising edge? */
		if (value && ((old_interrupt_value & interrupt) == 0))
		{
			interrupt_rising_edge(interrupt);
		}
	}

	// timer
	timer_type_t timer_type(void);
	const char *timer_type_string(void);
	void reset_timer(void);
	void timer_elapsed(void);

	// video
	bool update_screen(bitmap_rgb32 &bitmap, const rectangle &cliprect, const pixel_t *palette);
	void update_geometry(void);
	void update_rgb_palette(void);
	void update_composite_palette(void);
	void update_border(uint16_t physical_scanline);
	pixel_t get_composite_color(int color);
	pixel_t get_rgb_color(int color);
	offs_t get_video_base(void);
	uint16_t get_lines_per_row(void);
	uint32_t get_data_mc6847(uint32_t video_position, uint8_t *data, uint8_t *mode);
	uint32_t get_data_without_attributes(uint32_t video_position, uint8_t *data, uint8_t *mode);
	uint32_t get_data_with_attributes(uint32_t video_position, uint8_t *data, uint8_t *mode);

	// template function for doing video update collection
	template<uint8_t xres, get_data_func get_data, bool record_mode>
	uint32_t record_scanline_res(int scanline);

	// rendering sampled graphics
	typedef uint32_t (gime_device::*emit_samples_proc)(const scanline_record *scanline, int sample_start, int sample_count, pixel_t *pixels, const pixel_t *palette);
	uint32_t emit_dummy_samples(const scanline_record *scanline, int sample_start, int sample_count, pixel_t *pixels, const pixel_t *palette);
	uint32_t emit_mc6847_samples(const scanline_record *scanline, int sample_start, int sample_count, pixel_t *pixels, const pixel_t *palette);
	template<int xscale>
	uint32_t emit_gime_text_samples(const scanline_record *scanline, int sample_start, int sample_count, pixel_t *pixels, const pixel_t *palette);
	template<int xscale, int bits_per_pixel>
	uint32_t emit_gime_graphics_samples(const scanline_record *scanline, int sample_start, int sample_count, pixel_t *pixels, const pixel_t *palette);
	template<int sample_count, emit_samples_proc emit_samples>
	void render_scanline(const scanline_record *scanline, pixel_t *pixels, int min_x, int max_x, palette_resolver *resolver);
};


//**************************************************************************
//  VARIATIONS
//**************************************************************************

class gime_ntsc_device : public gime_device
{
public:
	template <typename T, typename U, typename V, typename W>
	gime_ntsc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&cpu_tag, U &&ram_tag, V &&ext_tag, W &&region_tag)
		: gime_ntsc_device(mconfig, tag, owner, clock)
	{
		m_maincpu.set_tag(std::forward<T>(cpu_tag));
		m_cpu.set_tag(std::forward<T>(cpu_tag));
		m_ram.set_tag(std::forward<U>(ram_tag));
		m_cart_device.set_tag(std::forward<V>(ext_tag));
		m_rom_region.set_tag(std::forward<W>(region_tag));
	}

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

class gime_pal_device : public gime_device
{
public:
	template <typename T, typename U, typename V, typename W>
	gime_pal_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&cpu_tag, U &&ram_tag, V &&ext_tag, W &&region_tag)
		: gime_pal_device(mconfig, tag, owner, clock)
	{
		m_maincpu.set_tag(std::forward<T>(cpu_tag));
		m_cpu.set_tag(std::forward<T>(cpu_tag));
		m_ram.set_tag(std::forward<U>(ram_tag));
		m_cart_device.set_tag(std::forward<V>(ext_tag));
		m_rom_region.set_tag(std::forward<W>(region_tag));
	}

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

DECLARE_DEVICE_TYPE(GIME_NTSC, gime_ntsc_device)
DECLARE_DEVICE_TYPE(GIME_PAL, gime_pal_device)

#endif //MAME_VIDEO_GIME_H