summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/pwm.h
blob: 4fba10e00d4f11f411274bde901a5aa4b4536858 (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
// license:BSD-3-Clause
// copyright-holders:hap
/*

  Generic PWM display device

*/

#ifndef MAME_VIDEO_PWM_H
#define MAME_VIDEO_PWM_H

#pragma once

class pwm_display_device : public device_t
{
public:
	pwm_display_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);

	// configuration helpers
	pwm_display_device &set_size(u8 numrows, u8 rowbits) { m_height = numrows; m_width = rowbits; return *this; } // max 64 * 64
	pwm_display_device &set_refresh(attotime duration) { m_framerate_set = duration; return *this; } // time between each outputs refresh
	pwm_display_device &set_interpolation(double factor) { m_interpolation = factor; return *this; } // frame interpolation (0.0 - 1.0)
	pwm_display_device &set_segmask(u64 digits, u64 mask); // mask for multi-state outputs, eg. 7seg led
	pwm_display_device &reset_segmask() { std::fill_n(m_segmask, ARRAY_LENGTH(m_segmask), 0); return *this; }
	pwm_display_device &set_bri_levels(double l0, double l1 = 1.0, double l2 = 1.0, double l3 = 1.0); // brightness threshold per level (0.0 - 1.0)
	pwm_display_device &set_bri_minimum(u8 i) { m_level_min = i; return *this; } // minimum level index for element to be considered "on"
	pwm_display_device &set_bri_maximum(double b) { m_level_max = b; return *this; } // maximum brightness level, 0.0 for auto

	// output callbacks when not using the default output tags
	auto output_x() { return m_output_x_cb.bind(); } // x = offset >> 6, y = offset & 0x3f
	auto output_a() { return m_output_a_cb.bind(); }
	auto output_digit() { return m_output_digit_cb.bind(); }

	void reset_bri_levels() { std::fill_n(m_levels, ARRAY_LENGTH(m_levels), 1.0); }
	void set_bri_one(u8 i, double level) { m_levels[i] = level; }
	void segmask_one(u8 y, u64 mask) { m_segmask[y] = mask; }

	void matrix_partial(u8 start, u8 height, u64 rowsel, u64 rowdata, bool upd = true);
	void matrix(u64 rowsel, u64 rowdata, bool upd = true) { matrix_partial(0, m_height, rowsel, rowdata, upd); }
	void update(); // apply changes to m_rowdata
	void clear() { matrix(0, 0); }

	// directly handle individual element (does not affect m_rowsel), y = row num, x = row bit
	int read_element(u8 y, u8 x) { return BIT(m_rowdata[y], x); }
	void write_element(u8 y, u8 x, int state) { m_rowdata[y] = (m_rowdata[y] & ~(u64(1) << x)) | (u64(state ? 1 : 0) << x); }

	// directly handle row data
	u64 read_row(offs_t offset) { return m_rowdata[offset]; }
	void write_row(offs_t offset, u64 data) { m_rowdata[offset] = data; m_rowsel |= u64(1) << offset; }
	void clear_row(offs_t offset, u64 data = 0) { m_rowdata[offset] = 0; m_rowsel &= ~(u64(1) << offset); }

	// directly handle element current brightness
	double read_element_bri(u8 y, u8 x) { return m_bri[y][x]; }
	void write_element_bri(u8 y, u8 x, double b) { m_bri[y][x] = b; }
	bool element_on(u8 y, u8 x) { return (read_element_bri(y, x) > m_levels[m_level_min]); }
	bool row_on(u8 y) { return element_on(y, m_width); }

protected:
	// device-level overrides
	virtual void device_start() override;
	virtual void device_reset() override;
	virtual void device_pre_save() override;
	virtual void device_post_load() override;

private:
	output_finder<0x40, 0x40> m_out_x;
	output_finder<0x40> m_out_a;
	output_finder<0x40> m_out_digit;

	devcb_write8 m_output_x_cb;
	devcb_write8 m_output_a_cb;
	devcb_write64 m_output_digit_cb;
	bool m_external_output;

	u8 m_width;
	u8 m_height;
	attotime m_framerate_set;
	attotime m_framerate;
	double m_interpolation;
	double m_levels[0x41];
	u8 m_level_min;
	double m_level_max;

	u64 m_segmask[0x40];
	u64 m_rowsel;
	u64 m_rowsel_prev;
	u64 m_rowdata[0x40];
	u64 m_rowdata_prev[0x40];

	double m_bri[0x40][0x41];
	attotime m_update_time;
	attotime m_acc[0x40][0x41];
	attoseconds_t m_acc_attos[0x40][0x41];
	seconds_t m_acc_secs[0x40][0x41];

	emu_timer *m_frame_timer;
	TIMER_CALLBACK_MEMBER(frame_tick);
	void schedule_frame();
};


DECLARE_DEVICE_TYPE(PWM_DISPLAY, pwm_display_device)

#endif // MAME_VIDEO_PWM_H