1 // license:BSD-3-Clause
2 // copyright-holders:Nathan Woods
3 /***************************************************************************
4 
5     6883sam.cpp
6 
7     Motorola 6883 Synchronous Address Multiplexer
8 
9     The Motorola 6883 SAM has 16 bits worth of state, but the state is changed
10     by writing into a 32 byte address space; odd bytes set bits and even bytes
11     clear bits.  Here is the layout:
12 
13         31  Set     TY  Map Type            0: RAM/ROM  1: All RAM
14         30  Clear   TY  Map Type
15         29  Set     M1  Memory Size         00: 4K      10: 64K Dynamic
16         28  Clear   M1  Memory Size         01: 16K     11: 64K Static
17         27  Set     M0  Memory Size
18         26  Clear   M0  Memory Size
19         25  Set     R1  MPU Rate            00: Slow    10: Fast
20         24  Clear   R1  MPU Rate            01: Dual    11: Fast
21         23  Set     R0  MPU Rate
22         22  Clear   R0  MPU Rate
23         21  Set     P1  Page #1             0: Low      1: High
24         20  Clear   P1  Page #1
25         19  Set     F6  Display Offset
26         18  Clear   F6  Display Offset
27         17  Set     F5  Display Offset
28         16  Clear   F5  Display Offset
29         15  Set     F4  Display Offset
30         14  Clear   F4  Display Offset
31         13  Set     F3  Display Offset
32         12  Clear   F3  Display Offset
33         11  Set     F2  Display Offset
34         10  Clear   F2  Display Offset
35          9  Set     F1  Display Offset
36          8  Clear   F1  Display Offset
37          7  Set     F0  Display Offset
38          6  Clear   F0  Display Offset
39          5  Set     V2  VDG Mode
40          4  Clear   V2  VDG Mode
41          3  Set     V1  VDG Mode
42          2  Clear   V1  VDG Mode
43          1  Set     V0  VDG Mode
44          0  Clear   V0  VDG Mode
45 
46     All parts of the SAM are fully emulated except R1/R0 (the changes in the
47     MPU rate are approximated) and M1/M0
48 
49 ***************************************************************************/
50 
51 
52 #include "emu.h"
53 #include "machine/6883sam.h"
54 
55 
56 //**************************************************************************
57 //  CONSTANTS
58 //**************************************************************************
59 
60 #define LOG_SAM     0
61 
62 DEFINE_DEVICE_TYPE(SAM6883, sam6883_device, "sam6883", "MC6883 SAM")
63 
64 
65 
66 //**************************************************************************
67 //  DEVICE SETUP
68 //**************************************************************************
69 
70 //-------------------------------------------------
71 //  constructor
72 //-------------------------------------------------
73 
sam6883_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)74 sam6883_device::sam6883_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
75 	: device_t(mconfig, SAM6883, tag, owner, clock)
76 	, device_memory_interface(mconfig, *this)
77 	, sam6883_friend_device_interface(mconfig, *this, 4)
78 	, m_ram_config("RAM", ENDIANNESS_BIG, 8, 16, 0)
79 	, m_rom0_config("ROM0", ENDIANNESS_BIG, 8, 13, 0)
80 	, m_rom1_config("ROM1", ENDIANNESS_BIG, 8, 13, 0)
81 	, m_rom2_config("ROM2", ENDIANNESS_BIG, 8, 14, 0)
82 	, m_io0_config("I/O0", ENDIANNESS_BIG, 8, 5, 0)
83 	, m_io1_config("I/O1", ENDIANNESS_BIG, 8, 5, 0)
84 	, m_io2_config("I/O2", ENDIANNESS_BIG, 8, 5, 0)
85 	, m_boot_config("boot", ENDIANNESS_BIG, 8, 7, 0)
86 {
87 }
88 
sam6883_friend_device_interface(const machine_config & mconfig,device_t & device,int divider)89 sam6883_friend_device_interface::sam6883_friend_device_interface(const machine_config &mconfig, device_t &device, int divider)
90 	: device_interface(device, "sam6883")
91 	, m_cpu(device, finder_base::DUMMY_TAG)
92 	, m_sam_state(0x0000)
93 	, m_divider(divider)
94 {
95 }
96 
97 
98 //-------------------------------------------------
99 //  memory_space_config - return the configuration
100 //  for the address spaces
101 //-------------------------------------------------
102 
memory_space_config() const103 device_memory_interface::space_config_vector sam6883_device::memory_space_config() const
104 {
105 	return space_config_vector {
106 		std::make_pair(0, &m_ram_config),
107 		std::make_pair(1, &m_rom0_config),
108 		std::make_pair(2, &m_rom1_config),
109 		std::make_pair(3, &m_rom2_config),
110 		std::make_pair(4, &m_io0_config),
111 		std::make_pair(5, &m_io1_config),
112 		std::make_pair(6, &m_io2_config),
113 		std::make_pair(7, &m_boot_config)
114 	};
115 }
116 
117 
118 //-------------------------------------------------
119 //  device_start - device-specific startup
120 //-------------------------------------------------
121 
device_start()122 void sam6883_device::device_start()
123 {
124 	// get spaces
125 	space(0).cache(m_ram_space);
126 	for (int i = 0; i < 3; i++)
127 		space(i + 1).cache(m_rom_space[i]);
128 	for (int i = 0; i < 3; i++)
129 		space(i + 4).specific(m_io_space[i]);
130 	space(7).cache(m_boot_space);
131 
132 	// save state support
133 	save_item(NAME(m_sam_state));
134 	save_item(NAME(m_counter));
135 	save_item(NAME(m_counter_xdiv));
136 	save_item(NAME(m_counter_ydiv));
137 }
138 
139 
140 //-------------------------------------------------
141 //  read - read from one of the eight spaces
142 //-------------------------------------------------
143 
read(offs_t offset)144 uint8_t sam6883_device::read(offs_t offset)
145 {
146 	bool mode_64k = (m_sam_state & SAM_STATE_M1) == SAM_STATE_M1;
147 	if (offset < (mode_64k && (m_sam_state & SAM_STATE_TY) ? 0xff00 : 0x8000))
148 	{
149 		// RAM reads: 0000–7FFF or 0000–FEFF
150 		if (mode_64k && (m_sam_state & (SAM_STATE_TY|SAM_STATE_P1)) == SAM_STATE_P1)
151 			offset |= 0x8000;
152 		return m_ram_space.read_byte(offset);
153 	}
154 	else if (offset < 0xc000 || offset >= 0xffe0)
155 	{
156 		// ROM spaces: 8000–9FFF and A000–BFFF + FFE0–FFFF
157 		return m_rom_space[BIT(offset, 13)].read_byte(offset & 0x1fff);
158 	}
159 	else if (offset < 0xff00)
160 	{
161 		// ROM2 space: C000–FEFF
162 		return m_rom_space[2].read_byte(offset & 0x3fff);
163 	}
164 	else if (offset < 0xff60)
165 	{
166 		// I/O spaces: FF00–FF1F (slow), FF20–FF3F, FF40–FF5F
167 		return m_io_space[BIT(offset, 5, 2)].read_byte(offset & 0x1f);
168 	}
169 	else
170 	{
171 		// FF60–FFDF
172 		return m_boot_space.read_byte(offset - 0xff60);
173 	}
174 }
175 
176 
177 //-------------------------------------------------
178 //  write - write to RAM, I/O or internal register
179 //-------------------------------------------------
180 
write(offs_t offset,uint8_t data)181 void sam6883_device::write(offs_t offset, uint8_t data)
182 {
183 	bool mode_64k = (m_sam_state & SAM_STATE_M1) == SAM_STATE_M1;
184 	if (offset < 0x8000)
185 	{
186 		// RAM write space: 0000–7FFF (nominally space 7)
187 		if (mode_64k && (m_sam_state & (SAM_STATE_TY|SAM_STATE_P1)) == SAM_STATE_P1)
188 			offset |= 0x8000;
189 		m_ram_space.write_byte(offset, data);
190 	}
191 	else if (offset < 0xc000 || offset >= 0xffe0)
192 	{
193 		// ROM spaces: 8000–9FFF and A000–BFFF + FFE0–FFFF (may write through to RAM)
194 		if (offset < 0xc000 && mode_64k && (m_sam_state & SAM_STATE_TY))
195 			m_ram_space.write_byte(offset, data);
196 		m_rom_space[BIT(offset, 13)].write_byte(offset & 0x1fff, data);
197 	}
198 	else if (offset < 0xff00)
199 	{
200 		// ROM2 space: C000–FEFF (may write through to RAM)
201 		if (mode_64k && (m_sam_state & SAM_STATE_TY))
202 			m_ram_space.write_byte(offset, data);
203 		m_rom_space[2].write_byte(offset & 0x3fff, data);
204 	}
205 	else if (offset < 0xff60)
206 	{
207 		// I/O spaces: FF00–FF1F (slow), FF20–FF3F, FF40–FF5F
208 		m_io_space[BIT(offset, 5, 2)].write_byte(offset & 0x1f, data);
209 	}
210 	else
211 	{
212 		// FF60–FFDF
213 		m_boot_space.write_byte(offset - 0xff60, data);
214 		if (offset >= 0xffc0)
215 			internal_write(offset & 0x1f, data);
216 	}
217 }
218 
219 
220 //-------------------------------------------------
221 //  device_reset - device-specific reset
222 //-------------------------------------------------
223 
device_reset()224 void sam6883_device::device_reset()
225 {
226 	m_counter = 0;
227 	m_counter_xdiv = 0;
228 	m_counter_ydiv = 0;
229 	m_sam_state = 0x0000;
230 	update_state();
231 }
232 
233 
234 
235 //-------------------------------------------------
236 //  device_post_load - device-specific post load
237 //-------------------------------------------------
238 
device_post_load()239 void sam6883_device::device_post_load()
240 {
241 	device_t::device_post_load();
242 	update_state();
243 }
244 
245 
246 
247 //-------------------------------------------------
248 //  update_state
249 //-------------------------------------------------
250 
update_state()251 void sam6883_device::update_state()
252 {
253 	update_memory();
254 	update_cpu_clock();
255 }
256 
257 
258 
259 //-------------------------------------------------
260 //  update_memory
261 //-------------------------------------------------
262 
update_memory()263 void sam6883_device::update_memory()
264 {
265 	// Memory size - allowed restricting memory accesses to something less than
266 	// 32k
267 	//
268 	// This was a SAM switch that occupied 4 addresses:
269 	//
270 	//      $FFDD   (set)   R1
271 	//      $FFDC   (clear) R1
272 	//      $FFDB   (set)   R0
273 	//      $FFDA   (clear) R0
274 	//
275 	// R1:R0 formed the following states:
276 	//      00  - 4k
277 	//      01  - 16k
278 	//      10  - 64k
279 	//      11  - static RAM (??)
280 	//
281 	// If something less than 64k was set, the low RAM would be smaller and
282 	// mirror the other parts of the RAM
283 	//
284 	// TODO:  Find out what "static RAM" is
285 	// TODO:  This should affect _all_ memory accesses, not just video ram
286 	// TODO:  Verify that the CoCo 3 ignored this
287 
288 	// switch depending on the M1/M0 variables
289 	switch(m_sam_state & (SAM_STATE_M1|SAM_STATE_M0))
290 	{
291 		case 0:
292 			// 4K mode
293 			m_counter_mask = 0x0FFF;
294 			break;
295 
296 		case SAM_STATE_M0:
297 			// 16K mode
298 			m_counter_mask = 0x3FFF;
299 			break;
300 
301 		case SAM_STATE_M1:
302 			// 64k mode (dynamic)
303 		case SAM_STATE_M1|SAM_STATE_M0:
304 			// 64k mode (static)
305 			if (m_sam_state & SAM_STATE_TY)
306 			{
307 				// full 64k RAM
308 				m_counter_mask = 0xFFFF;
309 			}
310 			else
311 			{
312 				// ROM/RAM
313 				m_counter_mask = 0x7FFF;
314 			}
315 			break;
316 	}
317 }
318 
319 
320 
321 //-------------------------------------------------
322 //  update_cpu_clock - adjusts the speed of the CPU
323 //  clock
324 //-------------------------------------------------
325 
update_cpu_clock()326 void sam6883_friend_device_interface::update_cpu_clock()
327 {
328 	// The infamous speed up poke.
329 	//
330 	// This was a SAM switch that occupied 4 addresses:
331 	//
332 	//      $FFD9   (set)   R1
333 	//      $FFD8   (clear) R1
334 	//      $FFD7   (set)   R0
335 	//      $FFD6   (clear) R0
336 	//
337 	// R1:R0 formed the following states:
338 	//      00  - slow          0.89 MHz
339 	//      01  - dual speed    ???
340 	//      1x  - fast          1.78 MHz
341 	//
342 	// R1 controlled whether the video addressing was speeded up and R0
343 	// did the same for the CPU.  On pre-CoCo 3 machines, setting R1 caused
344 	// the screen to display garbage because the M6847 could not display
345 	// fast enough.
346 	//
347 	// TODO:  Make the overclock more accurate.  In dual speed, ROM was a fast
348 	// access but RAM was not.  I don't know how to simulate this.
349 
350 	int speed = (m_sam_state & (SAM_STATE_R1|SAM_STATE_R0)) / SAM_STATE_R0;
351 	m_cpu->owner()->set_unscaled_clock(device().clock() / (m_divider * (speed ? 2 : 4)));
352 }
353 
354 
355 
356 //-------------------------------------------------
357 //  internal_write
358 //-------------------------------------------------
359 
internal_write(offs_t offset,uint8_t data)360 void sam6883_device::internal_write(offs_t offset, uint8_t data)
361 {
362 	// data is ignored
363 	(void)data;
364 
365 	// alter the SAM state
366 	uint16_t xorval = alter_sam_state(offset);
367 
368 	// based on the mask, apply effects
369 	if (xorval & (SAM_STATE_TY|SAM_STATE_M1|SAM_STATE_M0|SAM_STATE_P1))
370 		update_memory();
371 	if (xorval & (SAM_STATE_R1|SAM_STATE_R0))
372 		update_cpu_clock();
373 }
374 
375 
376 
377 //-------------------------------------------------
378 //  horizontal_sync
379 //-------------------------------------------------
380 
horizontal_sync()381 void sam6883_device::horizontal_sync()
382 {
383 	bool carry;
384 
385 	// When horizontal sync occurs, bits B1-B3 or B1-B4 may be cleared (except in DMA mode).  The catch
386 	// is that the SAM's counter is a chain of flip-flops.  Clearing the counter can cause carries to
387 	// occur just as they can when the counter is bumped.
388 	//
389 	// This is critical in getting certain semigraphics modes to work correctly.  Guardian uses this
390 	// mode (see bug #1153).  Special thanks to Ciaran Anscomb and Phill Harvey-Smith for figuring this
391 	// out
392 	switch((m_sam_state & (SAM_STATE_V2|SAM_STATE_V1|SAM_STATE_V0)) / SAM_STATE_V0)
393 	{
394 		case 0x01:
395 		case 0x03:
396 		case 0x05:
397 			// these SAM modes clear bits B1-B3
398 			carry = (m_counter & 0x0008) ? true : false;
399 			m_counter &= ~0x000F;
400 			if (carry)
401 				counter_carry_bit3();
402 			break;
403 
404 		case 0x00:
405 		case 0x02:
406 		case 0x04:
407 		case 0x06:
408 			// clear bits B1-B4
409 			carry = (m_counter & 0x0010) ? true : false;
410 			m_counter &= ~0x001F;
411 			if (carry)
412 				counter_carry_bit4();
413 			break;
414 
415 		case 0x07:
416 			// DMA mode - do nothing
417 			break;
418 
419 		default:
420 			fatalerror("Should not get here\n");
421 	}
422 }
423 
424 
425 
426 //-------------------------------------------------
427 //  hs_w
428 //-------------------------------------------------
429 
WRITE_LINE_MEMBER(sam6883_device::hs_w)430 WRITE_LINE_MEMBER( sam6883_device::hs_w )
431 {
432 	if (state)
433 	{
434 		horizontal_sync();
435 	}
436 }
437