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 ®ion)
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 ®ion : 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