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