1 // license:BSD-3-Clause
2 // copyright-holders:Ryan Holtz
3 /**********************************************************************
4 
5  Phasor, an ATmega88-based demo by Linus Åkesson
6 
7 **********************************************************************/
8 
9 #include "emu.h"
10 #include "cpu/avr8/avr8.h"
11 #include "sound/dac.h"
12 #include "screen.h"
13 #include "emupal.h"
14 #include "speaker.h"
15 
16 #define MASTER_CLOCK        17734470
17 
18 #define SAMPLES_PER_FRAME    (355255)
19 
20 class lft_phasor_state : public driver_device
21 {
22 public:
lft_phasor_state(const machine_config & mconfig,device_type type,const char * tag)23 	lft_phasor_state(const machine_config &mconfig, device_type type, const char *tag)
24 		: driver_device(mconfig, type, tag)
25 		, m_maincpu(*this, "maincpu")
26 		, m_dac(*this, "dac")
27 		, m_screen(*this, "screen")
28 		, m_palette(*this, "palette")
29 	{
30 	}
31 
32 	void phasor(machine_config &config);
33 
34 protected:
35 	virtual void machine_start() override;
36 	virtual void machine_reset() override;
37 
38 	void prg_map(address_map &map);
39 	void data_map(address_map &map);
40 	void io_map(address_map &map);
41 
42 	uint8_t port_r(offs_t offset);
43 	void port_w(offs_t offset, uint8_t data);
44 
45 	void init_palette(palette_device &palette) const;
46 	void video_update();
47 	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
48 
49 	required_device<avr8_device> m_maincpu;
50 	required_device<dac_byte_interface> m_dac;
51 	required_device<screen_device> m_screen;
52 	required_device<palette_device> m_palette;
53 
54 	enum port : uint8_t
55 	{
56 		PORT_A,
57 		PORT_B,
58 		PORT_C,
59 		PORT_D,
60 
61 		PORT_COUNT
62 	};
63 
64 	uint8_t m_ports[PORT_COUNT];
65 
66 	uint64_t m_last_cycles;
67 	uint64_t m_frame_start_cycle;
68 
69 	uint8_t m_latched_sample;
70 	bool m_in_blanking;
71 	uint64_t m_blanking_start;
72 	uint32_t m_sample_x;
73 	uint32_t m_sample_y;
74 	std::unique_ptr<uint8_t[]> m_samples;
75 };
76 
77 //**************************************************************************
78 //  GPIO
79 //**************************************************************************
80 
port_r(offs_t offset)81 uint8_t lft_phasor_state::port_r(offs_t offset)
82 {
83 	return m_ports[offset];
84 }
85 
port_w(offs_t offset,uint8_t data)86 void lft_phasor_state::port_w(offs_t offset, uint8_t data)
87 {
88 	m_ports[offset] = data;
89 
90 	switch (offset)
91 	{
92 	case AVR8_IO_PORTB:
93 		video_update();
94 		break;
95 
96 	case AVR8_IO_PORTC:
97 		m_dac->write(data & 0x3f);
98 		break;
99 
100 	case AVR8_IO_PORTD:
101 		//video_update();
102 		m_latched_sample = data;
103 		break;
104 	}
105 }
106 
107 //**************************************************************************
108 //  MEMORY
109 //**************************************************************************
110 
prg_map(address_map & map)111 void lft_phasor_state::prg_map(address_map &map)
112 {
113 	map(0x0000, 0x1fff).rom();
114 }
115 
data_map(address_map & map)116 void lft_phasor_state::data_map(address_map &map)
117 {
118 	map(0x0100, 0x04ff).ram();
119 }
120 
io_map(address_map & map)121 void lft_phasor_state::io_map(address_map &map)
122 {
123 	map(AVR8_IO_PORTA, AVR8_IO_PORTD).rw(FUNC(lft_phasor_state::port_r), FUNC(lft_phasor_state::port_w));
124 }
125 
126 //**************************************************************************
127 //  VIDEO
128 //**************************************************************************
129 
init_palette(palette_device & palette) const130 void lft_phasor_state::init_palette(palette_device &palette) const
131 {
132 	for (int i = 0; i < 0x10; i++)
133 	{
134 		uint8_t gray = (uint8_t)i;
135 		gray |= gray << 4;
136 		palette.set_pen_color(i, rgb_t(gray, gray, gray));
137 	}
138 }
139 
video_update()140 void lft_phasor_state::video_update()
141 {
142 	const uint64_t cycles = machine().time().as_ticks(MASTER_CLOCK);
143 
144 	if (cycles == m_last_cycles)
145 		return;
146 
147 	if (m_latched_sample == 0 && !m_in_blanking)
148 	{
149 		m_in_blanking = true;
150 		m_blanking_start = cycles;
151 	}
152 	else if (m_latched_sample != 0 && m_in_blanking)
153 	{
154 		m_in_blanking = false;
155 		const uint64_t blank_duration = cycles - m_blanking_start;
156 		if (blank_duration < 80) // Approximate length of hblank
157 		{
158 			m_sample_y++;
159 			m_sample_x = 0;
160 		}
161 		else
162 		{
163 			m_sample_y = 0;
164 			m_sample_x = 0;
165 			m_frame_start_cycle = machine().time().as_ticks(MASTER_CLOCK);
166 		}
167 	}
168 
169 	if (m_last_cycles < cycles && !m_in_blanking)
170 	{
171 		const uint8_t shift = (m_ports[PORT_B] & 4);
172 		uint32_t sample_pix = m_sample_y * 1135 + m_sample_x;
173 		for (uint64_t idx = m_last_cycles; idx < cycles && sample_pix < SAMPLES_PER_FRAME; idx++)
174 		{
175 			m_samples[sample_pix++] = (m_latched_sample >> shift) & 0x0f;
176 			m_sample_x++;
177 		}
178 	}
179 
180 	m_last_cycles = cycles;
181 }
182 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)183 uint32_t lft_phasor_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
184 {
185 	const pen_t *pens = m_palette->pens();
186 	for(int y = 0; y < 313; y++)
187 	{
188 		uint32_t *dst = &bitmap.pix(y);
189 		uint8_t *src = &m_samples[y * 1135];
190 		for(int x = 0; x < 1135; x++)
191 		{
192 			*dst++ = pens[*src++];
193 		}
194 	}
195 	return 0;
196 }
197 
198 //**************************************************************************
199 //  MACHINE
200 //**************************************************************************
201 
INPUT_PORTS_START(empty_input)202 static INPUT_PORTS_START( empty_input )
203 INPUT_PORTS_END
204 
205 void lft_phasor_state::machine_start()
206 {
207 	m_samples = std::make_unique<uint8_t[]>(SAMPLES_PER_FRAME);
208 
209 	save_item(NAME(m_ports));
210 	save_item(NAME(m_last_cycles));
211 	save_item(NAME(m_frame_start_cycle));
212 
213 	save_item(NAME(m_latched_sample));
214 	save_item(NAME(m_in_blanking));
215 	save_item(NAME(m_blanking_start));
216 	save_item(NAME(m_sample_x));
217 	save_item(NAME(m_sample_y));
218 	save_pointer(NAME(m_samples), SAMPLES_PER_FRAME);
219 }
220 
machine_reset()221 void lft_phasor_state::machine_reset()
222 {
223 	memset(m_ports, 0, PORT_COUNT);
224 
225 	m_frame_start_cycle = 0;
226 	m_last_cycles = 0;
227 
228 	m_latched_sample = 0;
229 	m_in_blanking = true;
230 	m_blanking_start = 0;
231 	m_sample_x = 0;
232 	m_sample_y = 0;
233 	memset(&m_samples[0], 0, SAMPLES_PER_FRAME);
234 }
235 
phasor(machine_config & config)236 void lft_phasor_state::phasor(machine_config &config)
237 {
238 	ATMEGA88(config, m_maincpu, MASTER_CLOCK);
239 	m_maincpu->set_addrmap(AS_PROGRAM, &lft_phasor_state::prg_map);
240 	m_maincpu->set_addrmap(AS_DATA, &lft_phasor_state::data_map);
241 	m_maincpu->set_addrmap(AS_IO, &lft_phasor_state::io_map);
242 	m_maincpu->set_eeprom_tag("eeprom");
243 
244 	PALETTE(config, m_palette, FUNC(lft_phasor_state::init_palette), 0x10);
245 	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
246 	m_screen->set_raw(MASTER_CLOCK, 1135, 0, 1064, 313, 6, 310);
247 	m_screen->set_screen_update(FUNC(lft_phasor_state::screen_update));
248 
249 	SPEAKER(config, "avr8").front_center();
250 
251 	DAC_6BIT_R2R(config, m_dac, 0).add_route(0, "avr8", 0.5);
252 }
253 
254 ROM_START( phasor )
255 	ROM_REGION( 0x2000, "maincpu", 0 )
256 	ROM_LOAD( "phasor.bin", 0x0000, 0x2000, CRC(300ef49b) SHA1(36b26137f5e8359dc9c2b746621a98bdd6634d2f) )
257 	ROM_REGION( 0x200, "eeprom", 0 )
258 	ROM_LOAD( "eeprom.raw", 0x0000, 0x0200, CRC(49036547) SHA1(d98c4d02771e80499c56dd71ad3d07597102f9b7) )
259 ROM_END
260 
261 /*   YEAR  NAME      PARENT  COMPAT  MACHINE     INPUT        CLASS             INIT        COMPANY            FULLNAME */
262 CONS(2010, phasor,   0,      0,      phasor,     empty_input, lft_phasor_state, empty_init, u8"Linus Åkesson", "Phasor", MACHINE_IMPERFECT_GRAPHICS)
263