1 // license:BSD-3-Clause
2 // copyright-holders:Curt Coder
3 /**********************************************************************
4 
5     RCA CDP1861 Video Display Controller emulation
6 
7 Timing: The 1861 interrupts the CPU to signal that DMA will be starting
8 in exactly 29 cycles. The CPU must set R0 as the pointer to the first of
9 8 sequential bytes to be transferred. After the DMA, exactly 6 cycles will
10 elapse before the next DMA. This process continues until the 1861 is within
11 4 lines of the end of the visible area, when it will assert EFx. When the
12 CPU sees this, it can finish up the interrupt routine. The 1861 will clear
13 EFx at the last visible line. The original IRQ request is cleared 28 cycles
14 after it was asserted. EFx is also asserted 4 lines before the first visible
15 scanline and ends 4 lines later, but this is usually ignored.
16 
17 Timing as it applies to the Studio II:
18 - The usage of EFx before the visible area is not used.
19 - R1 is preset with the value 001C, the interrupt vector.
20 - When the interrupt from the 1861 occurs, R1 becomes the P register, so
21   causing a jump to 001C.
22 - This is followed by 13 2-cycle instructions and one 3-cycle instruction,
23   giving us the required 29 cycles.
24 - The first DMA therefore will occur just after the PLO at 002D.
25 - This is followed by 3 2-cycle instructions, giving the required 6 cycles.
26 - The 1861 will draw 128 scanlines, but due to memory constraints, the
27   Studio II can only do 32 lines, and so each group of 4 scanlines is
28   DMA'd from the same part of memory.
29 - Each DMA will automatically add 8 to R0, and so this needs to be reset
30   for each of the 4 lines. After this, the new R0 value can be used for
31   the next group of 4 scanlines.
32 - After the 4 scanlines are done, EF1 is checked to see if the bottom of
33   the display is being reached. If not, more lines can be processed.
34 - At the end, the random number seed (not part of video drawing) gets
35   updated and the interrupt routine ends.
36 
37 **********************************************************************/
38 
39 #include "emu.h"
40 #include "cdp1861.h"
41 
42 #include "screen.h"
43 
44 
45 
46 //**************************************************************************
47 //  MACROS / CONSTANTS
48 //**************************************************************************
49 
50 #define CDP1861_CYCLES_DMA_START    (2*8)
51 #define CDP1861_CYCLES_DMA_ACTIVE   (8*8)
52 #define CDP1861_CYCLES_DMA_WAIT     (6*8)
53 
54 
55 
56 //**************************************************************************
57 //  DEVICE DEFINITIONS
58 //**************************************************************************
59 
60 // device type definition
61 DEFINE_DEVICE_TYPE(CDP1861, cdp1861_device, "cdp1861", "RCA CDP1861")
62 
63 
64 
65 //**************************************************************************
66 //  LIVE DEVICE
67 //**************************************************************************
68 
69 //-------------------------------------------------
70 //  cdp1861_device - constructor
71 //-------------------------------------------------
72 
cdp1861_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)73 cdp1861_device::cdp1861_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
74 	: device_t(mconfig, CDP1861, tag, owner, clock)
75 	, device_video_interface(mconfig, *this)
76 	, m_write_int(*this)
77 	, m_write_dma_out(*this)
78 	, m_write_efx(*this)
79 	, m_disp(0)
80 	, m_dispon(0), m_dispoff(0)
81 	, m_dmaout(CLEAR_LINE)
82 	, m_int_timer(nullptr), m_efx_timer(nullptr), m_dma_timer(nullptr)
83 {
84 }
85 
86 
87 //-------------------------------------------------
88 //  device_config_complete - perform any
89 //  operations now that the configuration is
90 //  complete
91 //-------------------------------------------------
92 
device_config_complete()93 void cdp1861_device::device_config_complete()
94 {
95 	if (!has_screen())
96 		return;
97 
98 	if (!screen().refresh_attoseconds())
99 		screen().set_raw(clock(), SCREEN_WIDTH, HBLANK_END, HBLANK_START, TOTAL_SCANLINES, SCANLINE_VBLANK_END, SCANLINE_VBLANK_START);
100 
101 	if (!screen().has_screen_update())
102 		screen().set_screen_update(*this, FUNC(cdp1861_device::screen_update));
103 }
104 
105 
106 //-------------------------------------------------
107 //  device_start - device-specific startup
108 //-------------------------------------------------
109 
device_start()110 void cdp1861_device::device_start()
111 {
112 	// resolve callbacks
113 	m_write_int.resolve_safe();
114 	m_write_dma_out.resolve_safe();
115 	m_write_efx.resolve_safe();
116 
117 	// allocate timers
118 	m_int_timer = timer_alloc(TIMER_INT);
119 	m_efx_timer = timer_alloc(TIMER_EFX);
120 	m_dma_timer = timer_alloc(TIMER_DMA);
121 
122 	// find devices
123 	screen().register_screen_bitmap(m_bitmap);
124 
125 	// register for state saving
126 	save_item(NAME(m_disp));
127 	save_item(NAME(m_dispon));
128 	save_item(NAME(m_dispoff));
129 	save_item(NAME(m_dmaout));
130 }
131 
132 
133 //-------------------------------------------------
134 //  device_reset - device-specific reset
135 //-------------------------------------------------
136 
device_reset()137 void cdp1861_device::device_reset()
138 {
139 	m_int_timer->adjust(screen().time_until_pos(SCANLINE_INT_START, 0));
140 	m_efx_timer->adjust(screen().time_until_pos(SCANLINE_EFX_TOP_START, 0));
141 	m_dma_timer->adjust(clocks_to_attotime(CDP1861_CYCLES_DMA_START));
142 
143 	m_disp = 0;
144 	m_dmaout = 0;
145 	m_dispon = 0;
146 
147 	m_write_int(CLEAR_LINE);
148 	m_write_dma_out(CLEAR_LINE);
149 	m_write_efx(CLEAR_LINE);
150 }
151 
152 
153 //-------------------------------------------------
154 //  device_timer - handle timer events
155 //-------------------------------------------------
156 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)157 void cdp1861_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
158 {
159 	int scanline = screen().vpos();
160 
161 	switch (id)
162 	{
163 	case TIMER_INT:
164 		if (scanline == SCANLINE_INT_START)
165 		{
166 			if (m_disp)
167 			{
168 				m_write_int(ASSERT_LINE);
169 			}
170 
171 			m_int_timer->adjust(screen().time_until_pos( SCANLINE_INT_END, 0));
172 		}
173 		else
174 		{
175 			if (m_disp)
176 			{
177 				m_write_int(CLEAR_LINE);
178 			}
179 
180 			m_int_timer->adjust(screen().time_until_pos(SCANLINE_INT_START, 0));
181 		}
182 		break;
183 
184 	case TIMER_EFX:
185 		switch (scanline)
186 		{
187 		case SCANLINE_EFX_TOP_START:
188 			m_write_efx(ASSERT_LINE);
189 			m_efx_timer->adjust(screen().time_until_pos(SCANLINE_EFX_TOP_END, 0));
190 			break;
191 
192 		case SCANLINE_EFX_TOP_END:
193 			m_write_efx(CLEAR_LINE);
194 			m_efx_timer->adjust(screen().time_until_pos(SCANLINE_EFX_BOTTOM_START, 0));
195 			break;
196 
197 		case SCANLINE_EFX_BOTTOM_START:
198 			m_write_efx(ASSERT_LINE);
199 			m_efx_timer->adjust(screen().time_until_pos(SCANLINE_EFX_BOTTOM_END, 0));
200 			break;
201 
202 		case SCANLINE_EFX_BOTTOM_END:
203 			m_write_efx(CLEAR_LINE);
204 			m_efx_timer->adjust(screen().time_until_pos(SCANLINE_EFX_TOP_START, 0));
205 			break;
206 		}
207 		break;
208 
209 	case TIMER_DMA:
210 		if (m_dmaout)
211 		{
212 			if (m_disp)
213 			{
214 				if (scanline >= SCANLINE_DISPLAY_START && scanline < SCANLINE_DISPLAY_END)
215 				{
216 					m_write_dma_out(CLEAR_LINE);
217 				}
218 			}
219 
220 			m_dma_timer->adjust(clocks_to_attotime(CDP1861_CYCLES_DMA_WAIT));
221 
222 			m_dmaout = CLEAR_LINE;
223 		}
224 		else
225 		{
226 			if (m_disp)
227 			{
228 				if (scanline >= SCANLINE_DISPLAY_START && scanline < SCANLINE_DISPLAY_END)
229 				{
230 					m_write_dma_out(ASSERT_LINE);
231 				}
232 			}
233 
234 			m_dma_timer->adjust(clocks_to_attotime(CDP1861_CYCLES_DMA_ACTIVE));
235 
236 			m_dmaout = ASSERT_LINE;
237 		}
238 		break;
239 	}
240 }
241 
242 
243 //-------------------------------------------------
244 //  dma_w -
245 //-------------------------------------------------
246 
dma_w(uint8_t data)247 void cdp1861_device::dma_w(uint8_t data)
248 {
249 	int sx = screen().hpos() + 4;
250 	int y = screen().vpos();
251 
252 	for (int x = 0; x < 8; x++)
253 	{
254 		pen_t color = BIT(data, 7) ? rgb_t::white() : rgb_t::black();
255 		m_bitmap.pix(y, sx + x) = color;
256 		data <<= 1;
257 	}
258 }
259 
260 
261 //-------------------------------------------------
262 //  disp_on_w -
263 //-------------------------------------------------
264 
disp_on_w(int state)265 void cdp1861_device::disp_on_w(int state)
266 {
267 	if (!m_dispon && state) m_disp = 1;
268 
269 	m_dispon = state;
270 }
271 
272 
273 //-------------------------------------------------
274 //  disp_off_w -
275 //-------------------------------------------------
276 
disp_off_w(int state)277 void cdp1861_device::disp_off_w(int state)
278 {
279 	if (!m_dispon && !m_dispoff && state) m_disp = 0;
280 
281 	m_dispoff = state;
282 
283 	m_write_int(CLEAR_LINE);
284 	m_write_dma_out(CLEAR_LINE);
285 }
286 
287 
288 //-------------------------------------------------
289 //  screen_update -
290 //-------------------------------------------------
291 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)292 uint32_t cdp1861_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
293 {
294 	if (m_disp)
295 	{
296 		copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
297 	}
298 	else
299 	{
300 		bitmap.fill(rgb_t::black(), cliprect);
301 	}
302 	return 0;
303 }
304