1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /*********************************************************************
4 
5     dvmemory.cpp
6 
7     Memory debugger view.
8 
9 ***************************************************************************/
10 
11 #include "emu.h"
12 #include "dvmemory.h"
13 
14 #include "debugcpu.h"
15 #include "debugger.h"
16 
17 #include <algorithm>
18 #include <cctype>
19 #include <tuple>
20 
21 
22 //**************************************************************************
23 //  GLOBAL VARIABLES
24 //**************************************************************************
25 
26 const debug_view_memory::memory_view_pos debug_view_memory::s_memory_pos_table[12] =
27 {
28 	/* 0 bytes per chunk:                         */ {  0, { 0 } },
29 	/* 1 byte  per chunk: 00 11 22 33 44 55 66 77 */ {  3, { 0x04, 0x00, 0x80 } },
30 	/* 2 bytes per chunk:  0011  2233  4455  6677 */ {  6, { 0x8c, 0x0c, 0x08, 0x04, 0x00, 0x80 } },
31 	/* 3 bytes per chunk:                         */ {  0, { 0 } },
32 	/* 4 bytes per chunk:   00112233    44556677  */ { 12, { 0x9c, 0x9c, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80 } },
33 	/* 5 bytes per chunk:                         */ {  0, { 0 } },
34 	/* 6 bytes per chunk:                         */ {  0, { 0 } },
35 	/* 7 bytes per chunk:                         */ {  0, { 0 } },
36 	/* 8 bytes per chunk:     0011223344556677    */ { 24, { 0xbc, 0xbc, 0xbc, 0xbc, 0x3c, 0x38, 0x34, 0x30, 0x2c, 0x28, 0x24, 0x20, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80, 0x80, 0x80 } },
37 	/* 32 bit floating point:                     */ { 16, { 0 } },
38 	/* 64 bit floating point:                     */ { 32, { 0 } },
39 	/* 80 bit floating point:                     */ { 32, { 0 } },
40 };
41 
42 
43 
44 //**************************************************************************
45 //  DEBUG VIEW MEMORY SOURCE
46 //**************************************************************************
47 
48 //-------------------------------------------------
49 //  debug_view_memory_source - constructors
50 //-------------------------------------------------
51 
debug_view_memory_source(std::string && name,address_space & space)52 debug_view_memory_source::debug_view_memory_source(std::string &&name, address_space &space)
53 	: debug_view_source(std::move(name), &space.device())
54 	, m_space(&space)
55 	, m_memintf(dynamic_cast<device_memory_interface *>(&space.device()))
56 	, m_base(nullptr)
57 	, m_blocklength(0)
58 	, m_numblocks(0)
59 	, m_blockstride(0)
60 	, m_offsetxor(0)
61 	, m_endianness(space.endianness())
62 	, m_prefsize(space.data_width() / 8)
63 {
64 }
65 
debug_view_memory_source(std::string && name,memory_region & region)66 debug_view_memory_source::debug_view_memory_source(std::string &&name, memory_region &region)
67 	: debug_view_source(std::move(name))
68 	, m_space(nullptr)
69 	, m_memintf(nullptr)
70 	, m_base(region.base())
71 	, m_blocklength(region.bytes())
72 	, m_numblocks(1)
73 	, m_blockstride(0)
74 	, m_offsetxor(ENDIAN_VALUE_NE_NNE(region.endianness(), 0, region.bytewidth() - 1))
75 	, m_endianness(region.endianness())
76 	, m_prefsize(std::min<u8>(region.bytewidth(), 8))
77 {
78 }
79 
debug_view_memory_source(std::string && name,void * base,int element_size,int num_elements,int num_blocks,int block_stride)80 debug_view_memory_source::debug_view_memory_source(std::string &&name, void *base, int element_size, int num_elements, int num_blocks, int block_stride)
81 	: debug_view_source(std::move(name))
82 	, m_space(nullptr)
83 	, m_memintf(nullptr)
84 	, m_base(base)
85 	, m_blocklength(element_size * num_elements)
86 	, m_numblocks(num_blocks)
87 	, m_blockstride(block_stride)
88 	, m_offsetxor(0)
89 	, m_endianness(ENDIANNESS_NATIVE)
90 	, m_prefsize(std::min(element_size, 8))
91 {
92 }
93 
94 
95 
96 //**************************************************************************
97 //  DEBUG VIEW MEMORY
98 //**************************************************************************
99 
100 //-------------------------------------------------
101 //  debug_view_memory - constructor
102 //-------------------------------------------------
103 
debug_view_memory(running_machine & machine,debug_view_osd_update_func osdupdate,void * osdprivate)104 debug_view_memory::debug_view_memory(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate)
105 	: debug_view(machine, DVT_MEMORY, osdupdate, osdprivate),
106 		m_expression(machine),
107 		m_chunks_per_row(16),
108 		m_bytes_per_chunk(1),
109 		m_steps_per_chunk(1),
110 		m_data_format(1),
111 		m_reverse_view(false),
112 		m_ascii_view(true),
113 		m_no_translation(false),
114 		m_edit_enabled(true),
115 		m_maxaddr(0),
116 		m_bytes_per_row(16),
117 		m_byte_offset(0)
118 {
119 	// hack: define some sane init values
120 	// that don't hurt the initial computation of top_left
121 	// in set_cursor_pos()
122 	m_section[0].m_pos = 0;
123 	m_section[0].m_width = 1 + 8 + 1;
124 	m_section[1].m_pos = m_section[0].m_pos + m_section[0].m_width;
125 
126 	// fail if no available sources
127 	enumerate_sources();
128 	if (m_source_list.empty())
129 		throw std::bad_alloc();
130 
131 	// configure the view
132 	m_supports_cursor = true;
133 }
134 
135 
136 //-------------------------------------------------
137 //  enumerate_sources - enumerate all possible
138 //  sources for a memory view
139 //-------------------------------------------------
140 
enumerate_sources()141 void debug_view_memory::enumerate_sources()
142 {
143 	// start with an empty list
144 	m_source_list.clear();
145 	m_source_list.reserve(machine().save().registration_count());
146 
147 	// first add all the devices' address spaces
148 	for (device_memory_interface &memintf : memory_interface_iterator(machine().root_device()))
149 	{
150 		for (int spacenum = 0; spacenum < memintf.max_space_count(); ++spacenum)
151 		{
152 			if (memintf.has_space(spacenum))
153 			{
154 				address_space &space(memintf.space(spacenum));
155 				m_source_list.emplace_back(
156 						std::make_unique<debug_view_memory_source>(
157 							util::string_format("%s '%s' %s space memory", memintf.device().name(), memintf.device().tag(), space.name()),
158 							space));
159 			}
160 		}
161 	}
162 
163 	// then add all the memory regions
164 	for (auto &region : machine().memory().regions())
165 	{
166 		m_source_list.emplace_back(
167 				std::make_unique<debug_view_memory_source>(
168 					util::string_format("Region '%s'", region.second->name()),
169 					*region.second.get()));
170 	}
171 
172 	// finally add all global array symbols in ASCII order
173 	std::string name;
174 	std::size_t const firstsave = m_source_list.size();
175 	for (int itemnum = 0; itemnum < machine().save().registration_count(); itemnum++)
176 	{
177 		u32 valsize, valcount, blockcount, stride;
178 		void *base;
179 		name = machine().save().indexed_item(itemnum, base, valsize, valcount, blockcount, stride);
180 
181 		// add pretty much anything that's not a timer (we may wish to cull other items later)
182 		// also, don't trim the front of the name, it's important to know which VIA6522 we're looking at, e.g.
183 		if (strncmp(name.c_str(), "timer/", 6))
184 			m_source_list.emplace_back(std::make_unique<debug_view_memory_source>(std::move(name), base, valsize, valcount, blockcount, stride));
185 	}
186 	std::sort(
187 			std::next(m_source_list.begin(), firstsave),
188 			m_source_list.end(),
189 			[] (auto const &x, auto const &y) { return 0 > std::strcmp(x->name(), y->name()); });
190 
191 	// reset the source to a known good entry
192 	if (!m_source_list.empty())
193 		set_source(*m_source_list[0]);
194 }
195 
196 
197 //-------------------------------------------------
198 //  view_notify - handle notification of updates
199 //  to cursor changes
200 //-------------------------------------------------
201 
view_notify(debug_view_notification type)202 void debug_view_memory::view_notify(debug_view_notification type)
203 {
204 	if (type == VIEW_NOTIFY_CURSOR_CHANGED)
205 	{
206 		// normalize the cursor
207 		set_cursor_pos(get_cursor_pos(m_cursor));
208 	}
209 	else if (type == VIEW_NOTIFY_SOURCE_CHANGED)
210 	{
211 		// update for the new source
212 		const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
213 		m_chunks_per_row = m_bytes_per_chunk * m_chunks_per_row / source.m_prefsize;
214 		m_bytes_per_chunk = source.m_prefsize;
215 		if (m_bytes_per_chunk > 8)
216 			m_bytes_per_chunk = 8;
217 		m_data_format = m_bytes_per_chunk;
218 		m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk;
219 		if (source.m_space != nullptr)
220 			m_expression.set_context(&source.m_space->device().debug()->symtable());
221 		else
222 			m_expression.set_context(nullptr);
223 	}
224 }
225 
226 
227 //-------------------------------------------------
228 //  u32_to_float - return a floating point number
229 //  whose 32 bit representation is value
230 //-------------------------------------------------
231 
u32_to_float(u32 value)232 static inline float u32_to_float(u32 value)
233 {
234 	union {
235 		float f;
236 		u32 i;
237 	} v;
238 
239 	v.i = value;
240 	return v.f;
241 }
242 
243 //-------------------------------------------------
244 //  u64_to_double - return a floating point number
245 //  whose 64 bit representation is value
246 //-------------------------------------------------
247 
u64_to_double(u64 value)248 static inline float u64_to_double(u64 value)
249 {
250 	union {
251 		double f;
252 		u64 i;
253 	} v;
254 
255 	v.i = value;
256 	return v.f;
257 }
258 
259 //-------------------------------------------------
260 //  generate_row - read one row of data and make a
261 //  text representation of the chunks
262 //-------------------------------------------------
263 
generate_row(debug_view_char * destmin,debug_view_char * destmax,debug_view_char * destrow,offs_t address)264 void debug_view_memory::generate_row(debug_view_char *destmin, debug_view_char *destmax, debug_view_char *destrow, offs_t address)
265 {
266 	// get positional data
267 	const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
268 	int spacing = posdata.m_spacing;
269 
270 	// generate the address
271 	char addrtext[20];
272 	sprintf(addrtext, m_addrformat.c_str(), address);
273 	debug_view_char *dest = destrow + m_section[0].m_pos + 1;
274 	for (int ch = 0; addrtext[ch] != 0 && ch < m_section[0].m_width - 1; ch++, dest++)
275 		if (dest >= destmin && dest < destmax)
276 			dest->byte = addrtext[ch];
277 
278 	// generate the data and the ascii string
279 	std::string chunkascii;
280 	if (m_data_format <= 8)
281 	{
282 		for (int chunknum = 0; chunknum < m_chunks_per_row; chunknum++)
283 		{
284 			u64 chunkdata;
285 			bool ismapped = read_chunk(address, chunknum, chunkdata);
286 
287 			int chunkindex = m_reverse_view ? (m_chunks_per_row - 1 - chunknum) : chunknum;
288 			dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing;
289 			for (int ch = 0; ch < spacing; ch++, dest++)
290 				if (dest >= destmin && dest < destmax)
291 				{
292 					u8 shift = posdata.m_shift[ch];
293 					if (shift < 64)
294 						dest->byte = ismapped ? "0123456789ABCDEF"[(chunkdata >> shift) & 0x0f] : '*';
295 				}
296 
297 			for (int i = 0; i < m_bytes_per_chunk; i++)
298 			{
299 				u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1));
300 				chunkascii += char((ismapped && isprint(chval)) ? chval : '.');
301 			}
302 		}
303 	}
304 	else
305 	{
306 		for (int chunknum = 0; chunknum < m_chunks_per_row; chunknum++)
307 		{
308 			char valuetext[64];
309 			u64 chunkdata = 0;
310 			extFloat80_t chunkdata80 = { 0, 0 };
311 			bool ismapped;
312 
313 			if (m_data_format != 11)
314 				ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata);
315 			else
316 				ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata80);
317 
318 			if (ismapped)
319 				switch (m_data_format)
320 				{
321 				case 9:
322 					sprintf(valuetext, "%.8g", u32_to_float(u32(chunkdata)));
323 					break;
324 				case 10:
325 					sprintf(valuetext, "%.24g", u64_to_double(chunkdata));
326 					break;
327 				case 11:
328 					float64_t f64 = extF80M_to_f64(&chunkdata80);
329 					sprintf(valuetext, "%.24g", u64_to_double(f64.v));
330 					break;
331 				}
332 			else
333 			{
334 				valuetext[0] = '*';
335 				valuetext[1] = 0;
336 			}
337 
338 			int ch;
339 			int chunkindex = m_reverse_view ? (m_chunks_per_row - 1 - chunknum) : chunknum;
340 			dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing;
341 			// first copy the text
342 			for (ch = 0; (ch < spacing) && (valuetext[ch] != 0); ch++, dest++)
343 				if (dest >= destmin && dest < destmax)
344 					dest->byte = valuetext[ch];
345 			// then fill with spaces
346 			for (; ch < spacing; ch++, dest++)
347 				if (dest >= destmin && dest < destmax)
348 					dest->byte = ' ';
349 
350 			for (int i = 0; i < m_bytes_per_chunk; i++)
351 			{
352 				u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1));
353 				chunkascii += char((ismapped && isprint(chval)) ? chval : '.');
354 			}
355 		}
356 	}
357 
358 	// generate the ASCII data, but follow the chunks
359 	if (m_section[2].m_width > 0)
360 	{
361 		dest = destrow + m_section[2].m_pos + 1;
362 		for (size_t i = 0; i != chunkascii.size(); i++)
363 		{
364 			if (dest >= destmin && dest < destmax)
365 				dest->byte = chunkascii[i];
366 			dest++;
367 		}
368 	}
369 }
370 
371 //-------------------------------------------------
372 //  view_update - update the contents of the
373 //  memory view
374 //-------------------------------------------------
375 
view_update()376 void debug_view_memory::view_update()
377 {
378 	const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
379 
380 	// if we need to recompute, do it now
381 	if (needs_recompute())
382 		recompute();
383 
384 	// loop over visible rows
385 	for (u32 row = 0; row < m_visible.y; row++)
386 	{
387 		debug_view_char *destmin = &m_viewdata[row * m_visible.x];
388 		debug_view_char *destmax = destmin + m_visible.x;
389 		debug_view_char *destrow = destmin - m_topleft.x;
390 		u32 effrow = m_topleft.y + row;
391 
392 		// reset the line of data; section 1 is normal, others are ancillary, cursor is selected
393 		u32 effcol = m_topleft.x;
394 		for (debug_view_char *dest = destmin; dest != destmax; dest++, effcol++)
395 		{
396 			dest->byte = ' ';
397 			dest->attrib = DCA_ANCILLARY;
398 			if (m_section[1].contains(effcol))
399 			{
400 				dest->attrib = DCA_NORMAL;
401 				if (m_cursor_visible && effrow == m_cursor.y && effcol == m_cursor.x)
402 					dest->attrib |= DCA_SELECTED;
403 			}
404 		}
405 
406 		// if this visible row is valid, add it to the buffer
407 		if (effrow < m_total.y)
408 		{
409 			offs_t addrbyte = m_byte_offset + effrow * m_bytes_per_row;
410 			offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(addrbyte) : addrbyte;
411 			generate_row(destmin, destmax, destrow, address);
412 		}
413 	}
414 }
415 
416 
417 //-------------------------------------------------
418 //  view_char - handle a character typed within
419 //  the current view
420 //-------------------------------------------------
421 
view_char(int chval)422 void debug_view_memory::view_char(int chval)
423 {
424 	// get the position
425 	cursor_pos pos = get_cursor_pos(m_cursor);
426 
427 	// editing is not supported when showing floating point values
428 	if (m_edit_enabled == false)
429 		return;
430 
431 	// handle the incoming key
432 	switch (chval)
433 	{
434 		case DCH_UP:
435 			if (pos.m_address >= m_byte_offset + m_bytes_per_row)
436 				pos.m_address -= m_bytes_per_row;
437 			break;
438 
439 		case DCH_DOWN:
440 			if (pos.m_address <= m_maxaddr - m_bytes_per_row)
441 				pos.m_address += m_bytes_per_row;
442 			break;
443 
444 		case DCH_PUP:
445 			for (u32 delta = (m_visible.y - 2) * m_bytes_per_row; delta > 0; delta -= m_bytes_per_row)
446 				if (pos.m_address >= m_byte_offset + delta)
447 				{
448 					pos.m_address -= delta;
449 					break;
450 				}
451 			break;
452 
453 		case DCH_PDOWN:
454 			for (u32 delta = (m_visible.y - 2) * m_bytes_per_row; delta > 0; delta -= m_bytes_per_row)
455 				if (pos.m_address <= m_maxaddr - delta)
456 				{
457 					pos.m_address += delta;
458 					break;
459 				}
460 			break;
461 
462 		case DCH_HOME:
463 			pos.m_address -= pos.m_address % m_bytes_per_row;
464 			pos.m_shift = (m_bytes_per_chunk * 8) - 4;
465 			break;
466 
467 		case DCH_CTRLHOME:
468 			pos.m_address = m_byte_offset;
469 			pos.m_shift = (m_bytes_per_chunk * 8) - 4;
470 			break;
471 
472 		case DCH_END:
473 			pos.m_address += (m_bytes_per_row - (pos.m_address % m_bytes_per_row) - 1);
474 			pos.m_shift = 0;
475 			break;
476 
477 		case DCH_CTRLEND:
478 			pos.m_address = m_maxaddr;
479 			pos.m_shift = 0;
480 			break;
481 
482 		case DCH_CTRLLEFT:
483 			if (pos.m_address >= m_byte_offset + m_bytes_per_chunk)
484 				pos.m_address -= m_bytes_per_chunk;
485 			break;
486 
487 		case DCH_CTRLRIGHT:
488 			if (pos.m_address <= m_maxaddr - m_bytes_per_chunk)
489 				pos.m_address += m_bytes_per_chunk;
490 			break;
491 
492 		default:
493 		{
494 			static const char hexvals[] = "0123456789abcdef";
495 			char *hexchar = (char *)strchr(hexvals, tolower(chval));
496 			if (hexchar == nullptr)
497 				break;
498 
499 			const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
500 			offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(pos.m_address) : pos.m_address;
501 			u64 data;
502 			bool ismapped = read(m_bytes_per_chunk, address, data);
503 			if (!ismapped)
504 				break;
505 
506 			data &= ~(u64(0x0f) << pos.m_shift);
507 			data |= u64(hexchar - hexvals) << pos.m_shift;
508 			write(m_bytes_per_chunk, address, data);
509 			// fall through to the right-arrow press
510 		}
511 
512 		case DCH_RIGHT:
513 			if (pos.m_shift == 0 && pos.m_address != m_maxaddr)
514 			{
515 				pos.m_shift = m_bytes_per_chunk * 8 - 4;
516 				pos.m_address += m_bytes_per_chunk;
517 			}
518 			else
519 				pos.m_shift -= 4;
520 			break;
521 
522 		case DCH_LEFT:
523 			if (pos.m_shift == m_bytes_per_chunk * 8 - 4 && pos.m_address != m_byte_offset)
524 			{
525 				pos.m_shift = 0;
526 				pos.m_address -= m_bytes_per_chunk;
527 			}
528 			else
529 				pos.m_shift += 4;
530 			break;
531 	}
532 
533 	// set a new position
534 	begin_update();
535 	set_cursor_pos(pos);
536 	m_update_pending = true;
537 	end_update();
538 }
539 
540 
541 //-------------------------------------------------
542 //  view_click - handle a mouse click within the
543 //  current view
544 //-------------------------------------------------
545 
view_click(const int button,const debug_view_xy & pos)546 void debug_view_memory::view_click(const int button, const debug_view_xy& pos)
547 {
548 	const debug_view_xy origcursor = m_cursor;
549 	m_cursor = pos;
550 
551 	/* cursor popup|toggle */
552 	bool cursorVisible = true;
553 	if (m_cursor.y == origcursor.y && m_cursor.x == origcursor.x)
554 	{
555 		cursorVisible = !m_cursor_visible;
556 	}
557 
558 	/* send a cursor changed notification */
559 	begin_update();
560 	m_cursor_visible = cursorVisible;
561 	view_notify(VIEW_NOTIFY_CURSOR_CHANGED);
562 	m_update_pending = true;
563 	end_update();
564 }
565 
566 
567 //-------------------------------------------------
568 //  recompute - recompute the internal data and
569 //  structure of the memory view
570 //-------------------------------------------------
571 
recompute()572 void debug_view_memory::recompute()
573 {
574 	const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
575 
576 	// get the current cursor position
577 	cursor_pos pos = get_cursor_pos(m_cursor);
578 
579 	// determine the maximum address and address format string from the raw information
580 	int addrchars;
581 	u64 maxbyte;
582 	if (source.m_space != nullptr)
583 	{
584 		m_maxaddr = m_no_translation ? source.m_space->addrmask() : source.m_space->logaddrmask();
585 		maxbyte = source.m_space->address_to_byte_end(m_maxaddr);
586 		addrchars = m_no_translation ? source.m_space->addrchars() : source.m_space->logaddrchars();
587 	}
588 	else
589 	{
590 		maxbyte = m_maxaddr = (source.m_blocklength * source.m_numblocks) - 1;
591 		addrchars = string_format("%X", m_maxaddr).size();
592 	}
593 
594 	// generate an 8-byte aligned format for the address
595 	if (!m_reverse_view)
596 		m_addrformat = string_format("%*s%%0%dX", 8 - addrchars, "", addrchars);
597 	else
598 		m_addrformat = string_format("%%0%dX%*s", addrchars, 8 - addrchars, "");
599 
600 	// if we are viewing a space with a minimum chunk size, clamp the bytes per chunk
601 	// BAD
602 #if 0
603 	if (source.m_space != nullptr && source.m_space->byte_to_address(1) > 1)
604 	{
605 		u32 min_bytes_per_chunk = source.m_space->byte_to_address(1);
606 		while (m_bytes_per_chunk < min_bytes_per_chunk)
607 		{
608 			m_bytes_per_chunk *= 2;
609 			m_chunks_per_row /= 2;
610 		}
611 		m_chunks_per_row = std::max(1U, m_chunks_per_row);
612 	}
613 #endif
614 
615 	// recompute the byte offset based on the most recent expression result
616 	m_bytes_per_row = m_bytes_per_chunk * m_chunks_per_row;
617 	offs_t val = m_expression.value();
618 	if (source.m_space)
619 		val = source.m_space->address_to_byte(val);
620 	m_byte_offset = val % m_bytes_per_row;
621 
622 	// compute the section widths
623 	m_section[0].m_width = 1 + 8 + 1;
624 	if (m_data_format <= 8)
625 		m_section[1].m_width = 1 + 3 * m_bytes_per_row + 1;
626 	else {
627 		const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
628 
629 		m_section[1].m_width = 1 + posdata.m_spacing * m_chunks_per_row + 1;
630 	}
631 	m_section[2].m_width = m_ascii_view ? (1 + m_bytes_per_row + 1) : 0;
632 
633 	// compute the section positions
634 	if (!m_reverse_view)
635 	{
636 		m_section[0].m_pos = 0;
637 		m_section[1].m_pos = m_section[0].m_pos + m_section[0].m_width;
638 		m_section[2].m_pos = m_section[1].m_pos + m_section[1].m_width;
639 		m_total.x = m_section[2].m_pos + m_section[2].m_width;
640 	}
641 	else
642 	{
643 		m_section[2].m_pos = 0;
644 		m_section[1].m_pos = m_section[2].m_pos + m_section[2].m_width;
645 		m_section[0].m_pos = m_section[1].m_pos + m_section[1].m_width;
646 		m_total.x = m_section[0].m_pos + m_section[0].m_width;
647 	}
648 
649 	// derive total sizes from that
650 	m_total.y = (maxbyte - u64(m_byte_offset) + u64(m_bytes_per_row) /*- 1*/) / m_bytes_per_row;
651 
652 	// reset the current cursor position
653 	set_cursor_pos(pos);
654 }
655 
656 
657 //-------------------------------------------------
658 //  needs_recompute - determine if anything has
659 //  changed that requires a recomputation
660 //-------------------------------------------------
661 
needs_recompute()662 bool debug_view_memory::needs_recompute()
663 {
664 	bool recompute = m_recompute;
665 
666 	// handle expression changes
667 	if (m_expression.dirty())
668 	{
669 		const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
670 		offs_t val = m_expression.value();
671 		if (source.m_space)
672 			val = source.m_space->address_to_byte(val & (m_no_translation ? source.m_space->addrmask() : source.m_space->logaddrmask()));
673 		recompute = true;
674 
675 		m_byte_offset = val % m_bytes_per_row;
676 		m_topleft.y = std::min(s32(val / m_bytes_per_row), m_total.y - 1);
677 
678 		set_cursor_pos(cursor_pos(val, m_bytes_per_chunk * 8 - 4));
679 	}
680 
681 	// expression is clean at this point, and future recomputation is not necessary
682 	m_recompute = false;
683 	return recompute;
684 }
685 
686 
687 //-------------------------------------------------
688 //  get_cursor_pos - return the cursor position as
689 //  an address and a shift value
690 //-------------------------------------------------
691 
get_cursor_pos(const debug_view_xy & cursor)692 debug_view_memory::cursor_pos debug_view_memory::get_cursor_pos(const debug_view_xy& cursor)
693 {
694 	// start with the base address for this row
695 	cursor_pos pos;
696 	const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
697 	pos.m_address = m_byte_offset + cursor.y * m_bytes_per_chunk * m_chunks_per_row;
698 
699 	// determine the X position within the middle section, clamping as necessary
700 	if (m_data_format <= 8) {
701 		int xposition = cursor.x - m_section[1].m_pos - 1;
702 		if (xposition < 0)
703 			xposition = 0;
704 		else if (xposition >= posdata.m_spacing * m_chunks_per_row)
705 			xposition = posdata.m_spacing * m_chunks_per_row - 1;
706 
707 		// compute chunk number and offset within that chunk
708 		int chunknum = xposition / posdata.m_spacing;
709 		int chunkoffs = xposition % posdata.m_spacing;
710 
711 		// reverse the chunknum if we're reversed
712 		if (m_reverse_view)
713 			chunknum = m_chunks_per_row - 1 - chunknum;
714 
715 		// compute the address and shift
716 		pos.m_address += chunknum * m_bytes_per_chunk;
717 		pos.m_shift = posdata.m_shift[chunkoffs] & 0x7f;
718 	}
719 	else {
720 		int xposition = cursor.x - m_section[1].m_pos - 1;
721 		// check for lower limit
722 		if (xposition < 0)
723 			xposition = 0;
724 		int chunknum = xposition / posdata.m_spacing;
725 		// check for upper limit
726 		if (chunknum >= m_chunks_per_row)
727 			chunknum = m_chunks_per_row - 1;
728 		// reverse the chunknum if we're reversed
729 		if (m_reverse_view)
730 			chunknum = m_chunks_per_row - 1 - chunknum;
731 		// compute the address
732 		pos.m_address += chunknum * m_bytes_per_chunk;
733 		pos.m_shift = 0;
734 	}
735 
736 	return pos;
737 }
738 
739 
740 //-------------------------------------------------
741 //  set_cursor_pos - set the cursor position as a
742 //  function of an address and a shift value
743 //-------------------------------------------------
744 
set_cursor_pos(cursor_pos pos)745 void debug_view_memory::set_cursor_pos(cursor_pos pos)
746 {
747 	const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
748 
749 	// offset the address by the byte offset
750 	if (pos.m_address < m_byte_offset)
751 		pos.m_address = m_byte_offset;
752 	pos.m_address -= m_byte_offset;
753 
754 	// compute the Y coordinate and chunk index
755 	m_cursor.y = pos.m_address / m_bytes_per_row;
756 	int chunknum = (pos.m_address % m_bytes_per_row) / m_bytes_per_chunk;
757 
758 	// reverse the chunknum if we're reversed
759 	if (m_reverse_view)
760 		chunknum = m_chunks_per_row - 1 - chunknum;
761 
762 	if (m_data_format <= 8) {
763 		// scan within the chunk to find the shift
764 		for (m_cursor.x = 0; m_cursor.x < posdata.m_spacing; m_cursor.x++)
765 			if (posdata.m_shift[m_cursor.x] == pos.m_shift)
766 				break;
767 
768 		// add in the chunk offset and shift to the right of divider1
769 		m_cursor.x += m_section[1].m_pos + 1 + posdata.m_spacing * chunknum;
770 	}
771 	else {
772 		m_cursor.x = m_section[1].m_pos + 1 + posdata.m_spacing * chunknum;
773 	}
774 
775 	// clamp to the window bounds
776 	m_cursor.x = std::min(m_cursor.x, m_total.x);
777 	m_cursor.y = std::min(m_cursor.y, m_total.y);
778 
779 	// scroll if out of range
780 	adjust_visible_x_for_cursor();
781 	adjust_visible_y_for_cursor();
782 }
783 
784 
785 //-------------------------------------------------
786 //  read - generic memory view data reader
787 //-------------------------------------------------
788 
read(u8 size,offs_t offs,u64 & data)789 bool debug_view_memory::read(u8 size, offs_t offs, u64 &data)
790 {
791 	const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
792 
793 	// if no raw data, just use the standard debug routines
794 	if (source.m_space)
795 	{
796 		auto dis = machine().disable_side_effects();
797 
798 		bool ismapped = offs <= m_maxaddr;
799 		if (ismapped && !m_no_translation)
800 		{
801 			offs_t dummyaddr = offs;
802 			ismapped = source.m_memintf->translate(source.m_space->spacenum(), TRANSLATE_READ_DEBUG, dummyaddr);
803 		}
804 		data = ~u64(0);
805 		if (ismapped)
806 			data = m_expression.context().read_memory(*source.m_space, offs, size, !m_no_translation);
807 		return ismapped;
808 	}
809 
810 	// if larger than a byte, reduce by half and recurse
811 	if (size > 1)
812 	{
813 		size /= 2;
814 
815 		u64 data0, data1;
816 		bool ismapped = read(size, offs + 0 * size, data0);
817 		ismapped |= read(size, offs + 1 * size, data1);
818 
819 		if (source.m_endianness == ENDIANNESS_LITTLE)
820 			data = data0 | (data1 << (size * 8));
821 		else
822 			data = data1 | (data0 << (size * 8));
823 		return ismapped;
824 	}
825 
826 	// all 0xff if out of bounds
827 	offs ^= source.m_offsetxor;
828 	if (offs >= (source.m_blocklength * source.m_numblocks))
829 		return false;
830 	data = *(reinterpret_cast<const u8 *>(source.m_base) + (offs / source.m_blocklength * source.m_blockstride) + (offs % source.m_blocklength));
831 	return true;
832 }
833 
834 
835 //-------------------------------------------------
836 //  read - read a 80 bit value
837 //-------------------------------------------------
838 
read(u8 size,offs_t offs,extFloat80_t & data)839 bool debug_view_memory::read(u8 size, offs_t offs, extFloat80_t &data)
840 {
841 	u64 t;
842 	bool mappedhi, mappedlo;
843 	const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
844 
845 	if (source.m_endianness == ENDIANNESS_LITTLE) {
846 		mappedlo = read(8, offs, data.signif);
847 		mappedhi = read(2, offs+8, t);
848 		data.signExp = u16(t);
849 	}
850 	else {
851 		mappedhi = read(2, offs, t);
852 		data.signExp = u16(t);
853 		mappedlo = read(8, offs + 2, data.signif);
854 	}
855 
856 	return mappedhi && mappedlo;
857 }
858 
859 
860 //-------------------------------------------------
861 //  read_chunk - memory view data reader helper
862 //-------------------------------------------------
863 
read_chunk(offs_t address,int chunknum,u64 & chunkdata)864 bool debug_view_memory::read_chunk(offs_t address, int chunknum, u64 &chunkdata)
865 {
866 	const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
867 	if (source.m_space) {
868 		address += source.m_space->byte_to_address(chunknum * m_bytes_per_chunk);
869 		if (!source.m_space->byte_to_address(m_bytes_per_chunk)) {
870 			// if chunks are too small to be addressable, read a minimal chunk and split it up
871 			u8 minbytes = 1 << -source.m_space->addr_shift();
872 			bool ismapped = read(minbytes, address, chunkdata);
873 			u8 suboffset = (chunknum * m_bytes_per_chunk) & (minbytes - 1);
874 			chunkdata >>= 8 * (source.m_space->endianness() == ENDIANNESS_LITTLE ? suboffset : minbytes - m_bytes_per_chunk - suboffset);
875 			chunkdata &= ~u64(0) >> (64 - 8 * m_bytes_per_chunk);
876 			return ismapped;
877 		}
878 	}
879 	else
880 		address += chunknum * m_bytes_per_chunk;
881 	return read(m_bytes_per_chunk, address, chunkdata);
882 }
883 
884 
885 //-------------------------------------------------
886 //  write - generic memory view data writer
887 //-------------------------------------------------
888 
write(u8 size,offs_t offs,u64 data)889 void debug_view_memory::write(u8 size, offs_t offs, u64 data)
890 {
891 	const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
892 
893 	// if no raw data, just use the standard debug routines
894 	if (source.m_space)
895 	{
896 		auto dis = machine().disable_side_effects();
897 		m_expression.context().write_memory(*source.m_space, offs, data, size, !m_no_translation);
898 		return;
899 	}
900 
901 	// if larger than a byte, reduce by half and recurse
902 	if (size > 1)
903 	{
904 		size /= 2;
905 		if (source.m_endianness == ENDIANNESS_LITTLE)
906 		{
907 			write(size, offs + 0 * size, data);
908 			write(size, offs + 1 * size, data >> (8 * size));
909 		}
910 		else
911 		{
912 			write(size, offs + 1 * size, data);
913 			write(size, offs + 0 * size, data >> (8 * size));
914 		}
915 		return;
916 	}
917 
918 	// ignore if out of bounds
919 	offs ^= source.m_offsetxor;
920 	if (offs >= (source.m_blocklength * source.m_numblocks))
921 		return;
922 	*(reinterpret_cast<u8 *>(source.m_base) + (offs / source.m_blocklength * source.m_blockstride) + (offs % source.m_blocklength)) = data;
923 
924 // hack for FD1094 editing
925 #ifdef FD1094_HACK
926 	if (source.m_base == machine().root_device().memregion("user2"))
927 	{
928 		extern void fd1094_regenerate_key(running_machine &machine);
929 		fd1094_regenerate_key(machine());
930 	}
931 #endif
932 }
933 
934 
935 //-------------------------------------------------
936 //  set_expression - set the expression string
937 //  describing the home address
938 //-------------------------------------------------
939 
set_expression(const std::string & expression)940 void debug_view_memory::set_expression(const std::string &expression)
941 {
942 	begin_update();
943 	m_expression.set_string(expression);
944 	m_recompute = m_update_pending = true;
945 	end_update();
946 }
947 
948 
949 //-------------------------------------------------
950 //  set_chunks_per_row - specify the number of
951 //  chunks displayed across a row
952 //-------------------------------------------------
953 
set_chunks_per_row(u32 rowchunks)954 void debug_view_memory::set_chunks_per_row(u32 rowchunks)
955 {
956 	if (rowchunks < 1)
957 		return;
958 
959 	cursor_pos pos = begin_update_and_get_cursor_pos();
960 	m_chunks_per_row = rowchunks;
961 	m_recompute = m_update_pending = true;
962 	end_update_and_set_cursor_pos(pos);
963 }
964 
965 
966 //-------------------------------------------------
967 //  set_data_format - specify what kind of values
968 //  are shown, 1-8 8-64 bits, 9 32bit floating point
969 //-------------------------------------------------
970 
set_data_format(int format)971 void debug_view_memory::set_data_format(int format)
972 {
973 	cursor_pos pos;
974 
975 	// should never be
976 	if ((format <= 0) || (format > 11))
977 		return;
978 	// no need to change
979 	if (format == m_data_format)
980 		return;
981 
982 	pos = begin_update_and_get_cursor_pos();
983 	const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
984 	if ((format <= 8) && (m_data_format <= 8)) {
985 
986 		pos.m_address += (pos.m_shift / 8) ^ ((source.m_endianness == ENDIANNESS_LITTLE) ? 0 : (m_bytes_per_chunk - 1));
987 		pos.m_shift %= 8;
988 
989 		m_bytes_per_chunk = format;
990 		m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk;
991 		m_chunks_per_row = m_bytes_per_row / format;
992 		if (m_chunks_per_row < 1)
993 			m_chunks_per_row = 1;
994 
995 		pos.m_shift += 8 * ((pos.m_address % m_bytes_per_chunk) ^ ((source.m_endianness == ENDIANNESS_LITTLE) ? 0 : (m_bytes_per_chunk - 1)));
996 		pos.m_address -= pos.m_address % m_bytes_per_chunk;
997 	} else {
998 		if (format <= 8) {
999 			m_supports_cursor = true;
1000 			m_edit_enabled = true;
1001 
1002 			m_bytes_per_chunk = format;
1003 		}
1004 		else {
1005 			m_supports_cursor = false;
1006 			m_edit_enabled = false;
1007 			m_cursor_visible = false;
1008 
1009 			switch (format)
1010 			{
1011 			case 9:
1012 				m_bytes_per_chunk = 4;
1013 				break;
1014 			case 10:
1015 				m_bytes_per_chunk = 8;
1016 				break;
1017 			case 11:
1018 				m_bytes_per_chunk = 10;
1019 				break;
1020 			}
1021 		}
1022 		m_chunks_per_row = m_bytes_per_row / m_bytes_per_chunk;
1023 		m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk;
1024 		pos.m_shift = 0;
1025 		pos.m_address -= pos.m_address % m_bytes_per_chunk;
1026 	}
1027 	m_recompute = m_update_pending = true;
1028 	m_data_format = format;
1029 	end_update_and_set_cursor_pos(pos);
1030 }
1031 
1032 //-------------------------------------------------
1033 //  set_reverse - specify true if the memory view
1034 //  is displayed reverse
1035 //-------------------------------------------------
1036 
set_reverse(bool reverse)1037 void debug_view_memory::set_reverse(bool reverse)
1038 {
1039 	cursor_pos pos = begin_update_and_get_cursor_pos();
1040 	m_reverse_view = reverse;
1041 	m_recompute = m_update_pending = true;
1042 	end_update_and_set_cursor_pos(pos);
1043 }
1044 
1045 
1046 //-------------------------------------------------
1047 //  set_ascii - specify true if the memory view
1048 //  should display an ASCII representation
1049 //-------------------------------------------------
1050 
set_ascii(bool ascii)1051 void debug_view_memory::set_ascii(bool ascii)
1052 {
1053 	cursor_pos pos = begin_update_and_get_cursor_pos();
1054 	m_ascii_view = ascii;
1055 	m_recompute = m_update_pending = true;
1056 	end_update_and_set_cursor_pos(pos);
1057 }
1058 
1059 
1060 //-------------------------------------------------
1061 //  set_physical - specify true if the memory view
1062 //  should display physical addresses versus
1063 //  logical addresses
1064 //-------------------------------------------------
1065 
set_physical(bool physical)1066 void debug_view_memory::set_physical(bool physical)
1067 {
1068 	cursor_pos pos = begin_update_and_get_cursor_pos();
1069 	m_no_translation = physical;
1070 	m_recompute = m_update_pending = true;
1071 	end_update_and_set_cursor_pos(pos);
1072 }
1073