1 // license:BSD-3-Clause
2 // copyright-holders:Gordon Jefferyes, Nigel Barnes
3 /******************************************************************************
4     BBC Model B
5 
6     MESS Driver By:
7 
8     Gordon Jefferyes
9     mess_bbc@romvault.com
10 
11     This is the first go around at converting the BBC code over to using
12     mames built in mc6845, there are a number of features now incorrect
13     or missing in this build:
14 
15     BBC split modes no longer work (Like is used in Elite.)
16 
17 ******************************************************************************/
18 
19 #include "emu.h"
20 #include "includes/bbc.h"
21 #include "video/saa5050.h"
22 #include "video/mc6845.h"
23 #include "screen.h"
24 
25 /************************************************************************
26  * C0 and C1 along with MA12 output from the 6845 drive 4 NAND gates in ICs 27,36 and 40
27  * the outputs from these NAND gates (B1 to B4) along with MA8 to MA11 from the 6845 (A1 to B4) are added together
28  * in IC39 74LS283 4 bit adder to form (S1 to S4) the logic is used to loop the screen memory for hardware scrolling.
29  * when MA13 from the 6845 is low the latches IC8 and IC9 are enabled
30  * they control the memory addressing for the Hi-Res modes.
31  * when MA13 from the 6845 is high the latches IC10 and IC11 are enabled
32  * they control the memory addressing for the Teletext mode.
33  * IC 8 or IC10 drives the row select in the memory (the lower 7 bits in the memory address) and
34  * IC 9 or IC11 drives the column select in the memory (the next 7 bits in the memory address) this
35  * gives control of the bottom 14 bits of the memory, in a 32K model B 15 bits are needed to access
36  * all the RAM, so S4 for the adder drives the CAS0 and CAS1 to access the top bit, in a 16K model A
37  * the output of S4 is linked out to a 0v supply by link S25 to just access the 16K memory area.
38  ************************************************************************/
39 
calculate_video_address(uint16_t ma,uint8_t ra)40 uint16_t bbc_state::calculate_video_address(uint16_t ma, uint8_t ra)
41 {
42 	/* output from IC32 74LS259 bits 4 and 5 */
43 	int c0 = m_latch->q4_r();
44 	int c1 = m_latch->q5_r();
45 
46 	/* the 4 bit input port b on IC39 are produced by 4 NAND gates. These NAND gates take their inputs
47 	   from c0 and c1 (from IC32) and ma12 (from the 6845) */
48 
49 	/* get bit m12 from the 6845 */
50 	int ma12 = BIT(ma, 12);
51 
52 	/* 4 bit input B on IC39 74LS283 (4 bit adder) */
53 	/* 3 input NAND part of IC 36 */
54 	int b1 = (~(c1 & c0 & ma12)) & 1;
55 	/* 2 input NAND part of IC40 (b3 is calculated before b2 and b4 because b3 feed back into b2 and b4) */
56 	int b3 = (~(c0 & ma12)) & 1;
57 	/* 3 input NAND part of IC 36 */
58 	int b2 = (~(c1 & b3 & ma12)) & 1;
59 	/* 2 input NAND part of IC 27 */
60 	int b4 = (~(b3 & ma12)) & 1;
61 
62 	/* inputs port b to IC39 are taken from the NAND gates b1 to b4 */
63 	int b = (b1<<0) | (b2<<1) | (b3<<2) | (b4<<3);
64 
65 	/* inputs port a to IC39 are MA8 to MA11 from the 6845 */
66 	int a = (ma>>8) & 0xf;
67 
68 	/* IC39 performs the 4 bit add with the carry input set high */
69 	int s = (a+b+1) & 0xf;
70 
71 	/* if MA13 (TTXVDU) is low then IC8 and IC9 are used to calculate
72 	   the memory location required for the hi res video.
73 	   if MA13 is high then IC10 and IC11 are used to calculate the memory location for the teletext chip */
74 	uint16_t m;
75 	if (BIT(ma, 13))
76 		m = ((ma & 0x3ff) | 0x3c00) | ((s & 0x8)<<11);
77 	else
78 		m = ((ma & 0xff)<<3) | (s<<11) | (ra & 0x7);
79 
80 	if (m_ram->size() == 16 * 1024) m &= 0x3fff;
81 
82 	return m;
83 }
84 
85 /***************************************************************************
86  * Palette
87  ***************************************************************************/
88 
update_palette(monitor_type monitor_type)89 void bbc_state::update_palette(monitor_type monitor_type)
90 {
91 	for (int i = 0; i < m_palette->entries(); i++)
92 	{
93 		rgb_t pen = rgb_t(pal1bit(i >> 0), pal1bit(i >> 1), pal1bit(i >> 2));
94 		float luma = float(pen.r()) * 0.299 + float(pen.g()) * 0.587 + float(pen.b()) * 0.114;
95 		switch (monitor_type)
96 		{
97 		case monitor_type::COLOUR:
98 			m_palette->set_pen_color(i, pen);
99 			break;
100 		case monitor_type::BLACKWHITE:
101 			m_palette->set_pen_color(i, rgb_t(luma, luma, luma));
102 			break;
103 		case monitor_type::GREEN:
104 			m_palette->set_pen_color(i, rgb_t(0.2 * luma, 0.9 * luma, 0.1 * luma));
105 			break;
106 		case monitor_type::AMBER:
107 			m_palette->set_pen_color(i, rgb_t(1.0 * luma, 0.8 * luma, 0.1 * luma));
108 			break;
109 		}
110 		m_vnula.flash[i & 7] = 1;
111 	}
112 }
113 
114 /************************************************************************
115  * VideoULA
116  ************************************************************************/
117 
118 static const int pixels_per_byte_set[8]={ 2,4,8,16,1,2,4,8 };
119 
120 static const int width_of_cursor_set[8]={ 0,0,1,2,1,0,2,4 };
121 
122 /* this is a quick lookup array that puts bits 0,2,4,6 into bits 0,1,2,3
123    this is used by the palette lookup in the video ULA */
set_pixel_lookup()124 void bbc_state::set_pixel_lookup()
125 {
126 	for (int i=0; i<256; i++)
127 	{
128 		m_pixel_bits[i] = (BIT(i,7)<<3) | (BIT(i,5)<<2) | (BIT(i,3)<<1) | (BIT(i,1)<<0);
129 	}
130 }
131 
132 
video_ula_w(offs_t offset,uint8_t data)133 void bbc_state::video_ula_w(offs_t offset, uint8_t data)
134 {
135 	int vpos = m_screen->vpos();
136 	if (vpos == 0)
137 		m_screen->update_partial(vpos);
138 	else
139 		m_screen->update_partial(vpos - 1);
140 
141 	uint8_t mask = m_vnula.disable ? 0x01 : 0x03;
142 	switch (offset & mask)
143 	{
144 	case 0:
145 		/* Video ULA control register
146 		     b7     Master cursor size
147 		     b6-b5  Width of cursor in bytes
148 		     b4     6845 clock rate select
149 		     b3-b2  Number of characters per line
150 		     b1     Teletext/Normal select
151 		     b0     Flash colour select
152 		*/
153 
154 		/* flash colour select has changed */
155 		if (BIT(m_vula_ctrl ^ data, 0))
156 		{
157 			for (int i = 0; i < 16; i++)
158 			{
159 				if (BIT(data, 0) && BIT(m_vula_palette[i], 3) && m_vnula.flash[m_vula_palette[i] & 7])
160 					m_vula_palette_lookup[i] = m_vula_palette[i];
161 				else
162 					m_vula_palette_lookup[i] = m_vula_palette[i] ^ 7;
163 			}
164 		}
165 
166 		m_vula_ctrl = data;
167 		m_cursor_size = width_of_cursor_set[(data >> 5) & 7];
168 
169 		/* number of BBC pixels held in each byte */
170 		if (BIT(data, 1))
171 			m_pixels_per_byte = 12;
172 		else
173 			m_pixels_per_byte = pixels_per_byte_set[(data >> 2) & 7];
174 
175 		m_hd6845->set_hpixels_per_column(m_pixels_per_byte);
176 		if (BIT(data, 4) || BIT(data, 1)) // FIXME: double clock for MODE7 until interlace is implemented
177 			m_hd6845->set_unscaled_clock(16_MHz_XTAL / 8);
178 		else
179 			m_hd6845->set_unscaled_clock(16_MHz_XTAL / 16);
180 		break;
181 
182 	case 1:
183 		/* Video ULA palette register */
184 		m_vula_palette[data >> 4] = data & 0x0f;
185 		if (BIT(data, 3) && BIT(m_vula_ctrl, 0) && m_vnula.flash[data & 7])
186 			m_vula_palette_lookup[data >> 4] = data & 0x0f;
187 		else
188 			m_vula_palette_lookup[data >> 4] = (data & 0x0f) ^ 7;
189 		break;
190 
191 	case 2:
192 		/* Video NuLA auxiliary control register */
193 		switch (data >> 4)
194 		{
195 		case 1:
196 			/* Set palette mode */
197 			m_vnula.palette_mode = data & 0x01;
198 			break;
199 		case 2:
200 			/* Set horizontal scroll offset */
201 			m_vnula.horiz_offset = data & 0x07;
202 			break;
203 		case 3:
204 			/* Set left-hand side blanking size */
205 			m_vnula.left_blank = data & 0x0f;
206 			break;
207 		case 4:
208 			/* Reset additional features */
209 			update_palette(monitor_type::COLOUR);
210 			for (int i = 0; i < 8; i++)
211 				m_vnula.flash[i] = 1;
212 			m_vnula.palette_mode = 0;
213 			m_vnula.horiz_offset = 0;
214 			m_vnula.left_blank = 0;
215 			m_vnula.attr_mode = 0;
216 			m_vnula.attr_text = 0;
217 			m_vnula.palette_write = 0;
218 			break;
219 		case 5:
220 			/* Disable A1 address line */
221 			m_vnula.disable = 1;
222 			break;
223 		case 6:
224 			/* Enable/disable attribute modes */
225 			m_vnula.attr_mode = data & 0x01;
226 			break;
227 		case 7:
228 			/* Enable/disable 8-colour attribute text modes */
229 			m_vnula.attr_text = data & 0x01;
230 			break;
231 		case 8:
232 			/* Set flashing flags for colours 8-11 */
233 			m_vnula.flash[0] = data & 0x08;
234 			m_vnula.flash[1] = data & 0x04;
235 			m_vnula.flash[2] = data & 0x02;
236 			m_vnula.flash[3] = data & 0x01;
237 			break;
238 		case 9:
239 			/* Set flashing flags for colours 12-15 */
240 			m_vnula.flash[4] = data & 0x08;
241 			m_vnula.flash[5] = data & 0x04;
242 			m_vnula.flash[6] = data & 0x02;
243 			m_vnula.flash[7] = data & 0x01;
244 			break;
245 		}
246 		break;
247 
248 	case 3:
249 		/* Video NuLA auxiliary palette register */
250 		if (m_vnula.palette_write)
251 		{
252 			uint8_t col = m_vnula.palette_byte >> 4;
253 			m_palette->set_pen_color(col, rgb_t(pal4bit(m_vnula.palette_byte & 0x0f), pal4bit(data >> 4), pal4bit(data & 0x0f)));
254 			/* redefined colour becomes solid */
255 			if (BIT(col, 3))
256 				m_vnula.flash[col & 7] = 0;
257 
258 			for (int i = 0; i < 16; i++)
259 			{
260 				if (BIT(m_vula_ctrl, 0) && BIT(m_vula_palette[i], 3) && m_vnula.flash[m_vula_palette[i] & 7])
261 					m_vula_palette_lookup[i] = m_vula_palette[i];
262 				else
263 					m_vula_palette_lookup[i] = m_vula_palette[i] ^ 7;
264 			}
265 		}
266 		else
267 		{
268 			m_vnula.palette_byte = data;
269 		}
270 		m_vnula.palette_write ^= 1;
271 		break;
272 	}
273 }
274 
275 /************************************************************************
276  * BBC circuits controlled by 6845 Outputs
277  ************************************************************************/
278 
MC6845_UPDATE_ROW(bbc_state::crtc_update_row)279 MC6845_UPDATE_ROW( bbc_state::crtc_update_row )
280 {
281 	const rgb_t *palette = m_palette->palette()->entry_list_raw();
282 
283 	if (BIT(m_vula_ctrl, 1))
284 	{
285 		m_trom->lose_w(1);
286 		m_trom->lose_w(0);
287 
288 		for (int x_pos = 0; x_pos < x_count; x_pos++)
289 		{
290 			/* Teletext Latch bits 0 to 5 go to bits 0 to 5 on the Teletext chip
291 			   Teletext Latch bit 6 is only passed onto bits 6 on the Teletext chip if DE is true
292 			   Teletext Latch bit 7 goes to LOSE on the Teletext chip */
293 
294 			if (BIT(ma, 13) == 0)
295 				m_teletext_latch = 0;
296 			else
297 				m_teletext_latch = (m_video_ram[calculate_video_address(ma + x_pos, ra)] & 0x7f) | (m_teletext_latch & 0x80);
298 
299 			m_trom->write((m_teletext_latch & 0x3f) | (m_teletext_latch & 0x40) | (de ? 0x00 : 0x40));
300 
301 			m_trom->f1_w(1);
302 			m_trom->f1_w(0);
303 
304 			for (int pixelno = 0; pixelno < 12; pixelno++)
305 			{
306 				m_trom->tr6_w(1);
307 				m_trom->tr6_w(0);
308 
309 				int col = m_trom->get_rgb() ^ ((x_pos == cursor_x) ? 7 : 0);
310 
311 				bitmap.pix(y, (x_pos*m_pixels_per_byte) + pixelno) = de ? palette[col] : rgb_t::black();
312 			}
313 		}
314 	}
315 	else
316 	{
317 		for (int x_pos = 0; x_pos<x_count; x_pos++)
318 		{
319 			uint8_t data = m_video_ram[calculate_video_address(ma + x_pos, ra)];
320 
321 			for (int pixelno = 0; pixelno < m_pixels_per_byte; pixelno++)
322 			{
323 				int col = !(ra & 0x08) ? m_vula_palette_lookup[m_pixel_bits[data]] : 0;
324 
325 				col ^= ((cursor_x != -1 && x_pos >= cursor_x && x_pos < (cursor_x + m_cursor_size)) ? 7 : 0);
326 
327 				bitmap.pix(y, (x_pos*m_pixels_per_byte) + pixelno) = de ? palette[col] : rgb_t::black();
328 				data = (data << 1) | 1;
329 			}
330 		}
331 	}
332 }
333 
WRITE_LINE_MEMBER(bbc_state::bbc_hsync_changed)334 WRITE_LINE_MEMBER(bbc_state::bbc_hsync_changed)
335 {
336 	m_hsync = state;
337 }
338 
WRITE_LINE_MEMBER(bbc_state::bbc_vsync_changed)339 WRITE_LINE_MEMBER(bbc_state::bbc_vsync_changed)
340 {
341 	m_vsync = state;
342 	m_via6522_0->write_ca1(state); // screen refresh interrupts
343 	m_trom->dew_w(state);
344 }
345 
WRITE_LINE_MEMBER(bbc_state::bbc_de_changed)346 WRITE_LINE_MEMBER(bbc_state::bbc_de_changed)
347 {
348 	if (!state)
349 		m_teletext_latch |= 0x80;
350 	else
351 		m_teletext_latch &= ~0x80;
352 }
353 
bus_video_data()354 uint8_t bbc_state::bus_video_data()
355 {
356 	int hpos = m_screen->hpos();
357 	int vpos = m_screen->vpos();
358 
359 	return m_video_ram[calculate_video_address(hpos, vpos)];
360 }
361 
362 /**** BBC B+/Master Shadow Ram change ****/
363 
setvideoshadow(int vdusel)364 void bbc_state::setvideoshadow(int vdusel)
365 {
366 	// LYNNE lives at 0xb000 in our map, but the offset we use here is 0x8000
367 	// as the video circuitry will already be looking at 0x3000 or so above
368 	// the offset.
369 	if (vdusel)
370 		m_video_ram = m_ram->pointer() + 0x8000;
371 	else
372 		m_video_ram = m_ram->pointer();
373 }
374 
375 /************************************************************************
376  * Initialize the BBC video emulation
377  ************************************************************************/
378 
video_start()379 void bbc_state::video_start()
380 {
381 	m_cursor_size = 1;
382 
383 	set_pixel_lookup();
384 
385 	m_video_ram = m_ram->pointer();
386 
387 	// register save states
388 	save_item(NAME(m_vula_ctrl));
389 	save_item(NAME(m_vula_palette));
390 	save_item(NAME(m_vula_palette_lookup));
391 	save_item(STRUCT_MEMBER(m_vnula, palette_mode));
392 	save_item(STRUCT_MEMBER(m_vnula, horiz_offset));
393 	save_item(STRUCT_MEMBER(m_vnula, left_blank));
394 	save_item(STRUCT_MEMBER(m_vnula, disable));
395 	save_item(STRUCT_MEMBER(m_vnula, flash));
396 	save_item(STRUCT_MEMBER(m_vnula, palette_byte));
397 	save_item(STRUCT_MEMBER(m_vnula, palette_write));
398 }
399 
video_reset()400 void bbc_state::video_reset()
401 {
402 	for (int i = 0; i < 8; i++)
403 		m_vnula.flash[i] = 1;
404 	m_vnula.palette_mode = 0;
405 	m_vnula.horiz_offset = 0;
406 	m_vnula.left_blank = 0;
407 	m_vnula.disable = !BIT(m_bbcconfig.read_safe(0), 3);
408 	m_vnula.palette_byte = 0;
409 	m_vnula.palette_write = 0;
410 }
411