summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/distate.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/distate.c')
-rw-r--r--trunk/src/emu/distate.c608
1 files changed, 608 insertions, 0 deletions
diff --git a/trunk/src/emu/distate.c b/trunk/src/emu/distate.c
new file mode 100644
index 00000000000..6a0621d3eac
--- /dev/null
+++ b/trunk/src/emu/distate.c
@@ -0,0 +1,608 @@
+/***************************************************************************
+
+ distate.c
+
+ Device state interfaces.
+
+****************************************************************************
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#include "emu.h"
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+const UINT64 device_state_entry::k_decimal_divisor[] =
+{
+ 1,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ 10000000,
+ 100000000,
+ 1000000000,
+ U64(10000000000),
+ U64(100000000000),
+ U64(1000000000000),
+ U64(10000000000000),
+ U64(100000000000000),
+ U64(1000000000000000),
+ U64(10000000000000000),
+ U64(100000000000000000),
+ U64(1000000000000000000),
+ U64(10000000000000000000)
+};
+
+
+
+//**************************************************************************
+// DEVICE STATE ENTRY
+//**************************************************************************
+
+//-------------------------------------------------
+// device_state_entry - constructor
+//-------------------------------------------------
+
+device_state_entry::device_state_entry(int index, const char *symbol, void *dataptr, UINT8 size)
+ : m_next(NULL),
+ m_index(index),
+ m_datamask(0),
+ m_datasize(size),
+ m_flags(0),
+ m_symbol(symbol),
+ m_default_format(true),
+ m_sizemask(0)
+{
+ // set the data pointer
+ m_dataptr.v = dataptr;
+
+ // convert the size to a mask
+ assert(size == 1 || size == 2 || size == 4 || size == 8);
+ if (size == 1)
+ m_sizemask = 0xff;
+ else if (size == 2)
+ m_sizemask = 0xffff;
+ else if (size == 4)
+ m_sizemask = 0xffffffff;
+ else
+ m_sizemask = ~U64(0);
+
+ // default the data mask to the same
+ m_datamask = m_sizemask;
+ format_from_mask();
+
+ // override well-known symbols
+ if (index == STATE_GENPC)
+ m_symbol.cpy("CURPC");
+ else if (index == STATE_GENPCBASE)
+ m_symbol.cpy("CURPCBASE");
+ else if (index == STATE_GENSP)
+ m_symbol.cpy("CURSP");
+ else if (index == STATE_GENFLAGS)
+ m_symbol.cpy("CURFLAGS");
+}
+
+
+//-------------------------------------------------
+// formatstr - specify a format string
+//-------------------------------------------------
+
+device_state_entry &device_state_entry::formatstr(const char *_format)
+{
+ m_format.cpy(_format);
+ m_default_format = false;
+
+ // set the DSF_CUSTOM_STRING flag by formatting with a NULL string
+ m_flags &= ~DSF_CUSTOM_STRING;
+ astring dummy;
+ format(dummy, NULL);
+
+ return *this;
+}
+
+
+//-------------------------------------------------
+// format_from_mask - make a format based on
+// the data mask
+//-------------------------------------------------
+
+void device_state_entry::format_from_mask()
+{
+ // skip if we have a user-provided format
+ if (!m_default_format)
+ return;
+
+ // make up a format based on the mask
+ int width = 0;
+ for (UINT64 tempmask = m_datamask; tempmask != 0; tempmask >>= 4)
+ width++;
+ m_format.printf("%%0%dX", width);
+}
+
+
+//-------------------------------------------------
+// value - return the current value as a UINT64
+//-------------------------------------------------
+
+UINT64 device_state_entry::value() const
+{
+ // pick up the value
+ UINT64 result = ~(UINT64)0;
+ switch (m_datasize)
+ {
+ default:
+ case 1: result = *m_dataptr.u8; break;
+ case 2: result = *m_dataptr.u16; break;
+ case 4: result = *m_dataptr.u32; break;
+ case 8: result = *m_dataptr.u64; break;
+ }
+ return result & m_datamask;
+}
+
+
+//-------------------------------------------------
+// format - return the value of the given
+// pieces of indexed state as a string
+//-------------------------------------------------
+
+astring &device_state_entry::format(astring &dest, const char *string, bool maxout) const
+{
+ UINT64 result = value();
+
+ // parse the format
+ bool leadzero = false;
+ bool percent = false;
+ bool explicitsign = false;
+ bool hitnonzero = false;
+ bool reset = true;
+ int width = 0;
+ for (const char *fptr = m_format; *fptr != 0; fptr++)
+ {
+ // reset any accumulated state
+ if (reset)
+ {
+ leadzero = maxout;
+ percent = explicitsign = reset = false;
+ width = 0;
+ }
+
+ // if we're not within a format, then anything other than a % outputs directly
+ if (!percent && *fptr != '%')
+ {
+ dest.cat(fptr, 1);
+ continue;
+ }
+
+ // handle each character in turn
+ switch (*fptr)
+ {
+ // % starts a format; %% outputs a single %
+ case '%':
+ if (!percent)
+ percent = true;
+ else
+ {
+ dest.cat(fptr, 1);
+ percent = false;
+ }
+ break;
+
+ // 0 means insert leading 0s, unless it follows another width digit
+ case '0':
+ if (width == 0)
+ leadzero = true;
+ else
+ width *= 10;
+ break;
+
+ // 1-9 accumulate into the width
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ width = width * 10 + (*fptr - '0');
+ break;
+
+ // + means explicit sign
+ case '+':
+ explicitsign = true;
+ break;
+
+ // X outputs as hexadecimal
+ case 'X':
+ if (width == 0)
+ throw emu_fatalerror("Width required for %%X formats\n");
+ hitnonzero = false;
+ while (leadzero && width > 16)
+ {
+ dest.cat(" ");
+ width--;
+ }
+ for (int digitnum = 15; digitnum >= 0; digitnum--)
+ {
+ int digit = (result >> (4 * digitnum)) & 0x0f;
+ if (digit != 0)
+ {
+ static const char hexchars[] = "0123456789ABCDEF";
+ dest.cat(&hexchars[digit], 1);
+ hitnonzero = true;
+ }
+ else if (hitnonzero || (leadzero && digitnum < width) || digitnum == 0)
+ dest.cat("0");
+ }
+ reset = true;
+ break;
+
+ // d outputs as signed decimal
+ case 'd':
+ if (width == 0)
+ throw emu_fatalerror("Width required for %%d formats\n");
+ if ((result & m_datamask) > (m_datamask >> 1))
+ {
+ result = -result & m_datamask;
+ dest.cat("-");
+ width--;
+ }
+ else if (explicitsign)
+ {
+ dest.cat("+");
+ width--;
+ }
+ // fall through to unsigned case
+
+ // u outputs as unsigned decimal
+ case 'u':
+ if (width == 0)
+ throw emu_fatalerror("Width required for %%u formats\n");
+ hitnonzero = false;
+ while (leadzero && width > ARRAY_LENGTH(k_decimal_divisor))
+ {
+ dest.cat(" ");
+ width--;
+ }
+ for (int digitnum = ARRAY_LENGTH(k_decimal_divisor) - 1; digitnum >= 0; digitnum--)
+ {
+ int digit = (result >= k_decimal_divisor[digitnum]) ? (result / k_decimal_divisor[digitnum]) % 10 : 0;
+ if (digit != 0)
+ {
+ static const char decchars[] = "0123456789";
+ dest.cat(&decchars[digit], 1);
+ hitnonzero = true;
+ }
+ else if (hitnonzero || (leadzero && digitnum < width) || digitnum == 0)
+ dest.cat("0");
+ }
+ reset = true;
+ break;
+
+ // s outputs a custom string
+ case 's':
+ if (width == 0)
+ throw emu_fatalerror("Width required for %%s formats\n");
+ if (string == NULL)
+ {
+ const_cast<device_state_entry *>(this)->m_flags |= DSF_CUSTOM_STRING;
+ return dest;
+ }
+ if (strlen(string) <= width)
+ {
+ dest.cat(string);
+ width -= strlen(string);
+ while (width-- != 0)
+ dest.cat(" ");
+ }
+ else
+ dest.cat(string, width);
+ reset = true;
+ break;
+
+ // other formats unknown
+ default:
+ throw emu_fatalerror("Unknown format character '%c'\n", *fptr);
+ break;
+ }
+ }
+ return dest;
+}
+
+
+//-------------------------------------------------
+// set_value - set the value from a UINT64
+//-------------------------------------------------
+
+void device_state_entry::set_value(UINT64 value) const
+{
+ // apply the mask
+ value &= m_datamask;
+
+ // sign-extend if necessary
+ if ((m_flags & DSF_IMPORT_SEXT) != 0 && value > (m_datamask >> 1))
+ value |= ~m_datamask;
+
+ // store the value
+ switch (m_datasize)
+ {
+ default:
+ case 1: *m_dataptr.u8 = value; break;
+ case 2: *m_dataptr.u16 = value; break;
+ case 4: *m_dataptr.u32 = value; break;
+ case 8: *m_dataptr.u64 = value; break;
+ }
+}
+
+
+//-------------------------------------------------
+// set_value - set the value from a string
+//-------------------------------------------------
+
+void device_state_entry::set_value(const char *string) const
+{
+ // not implemented
+}
+
+
+
+//**************************************************************************
+// DEVICE STATE INTERFACE
+//**************************************************************************
+
+//-------------------------------------------------
+// device_state_interface - constructor
+//-------------------------------------------------
+
+device_state_interface::device_state_interface(const machine_config &mconfig, device_t &device)
+ : device_interface(device)
+{
+ memset(m_fast_state, 0, sizeof(m_fast_state));
+
+ // configure the fast accessor
+ device.m_state = this;
+}
+
+
+//-------------------------------------------------
+// ~device_state_interface - destructor
+//-------------------------------------------------
+
+device_state_interface::~device_state_interface()
+{
+}
+
+
+//-------------------------------------------------
+// state - return the value of the given piece
+// of indexed state as a UINT64
+//-------------------------------------------------
+
+UINT64 device_state_interface::state(int index)
+{
+ // NULL or out-of-range entry returns 0
+ const device_state_entry *entry = state_find_entry(index);
+ if (entry == NULL)
+ return 0;
+
+ // call the exporter before we do anything
+ if (entry->needs_export())
+ state_export(*entry);
+
+ // pick up the value
+ return entry->value();
+}
+
+
+//-------------------------------------------------
+// state_string - return the value of the given
+// pieces of indexed state as a string
+//-------------------------------------------------
+
+astring &device_state_interface::state_string(int index, astring &dest)
+{
+ // NULL or out-of-range entry returns bogus string
+ const device_state_entry *entry = state_find_entry(index);
+ if (entry == NULL)
+ return dest.cpy("???");
+
+ // get the custom string if needed
+ astring custom;
+ if (entry->needs_custom_string())
+ state_string_export(*entry, custom);
+
+ // ask the entry to format itself
+ return entry->format(dest, custom);
+}
+
+
+//-------------------------------------------------
+// state_string_max_length - return the maximum
+// length of the given state string
+//-------------------------------------------------
+
+int device_state_interface::state_string_max_length(int index)
+{
+ // NULL or out-of-range entry returns bogus string
+ const device_state_entry *entry = state_find_entry(index);
+ if (entry == NULL)
+ return 3;
+
+ // ask the entry to format itself maximally
+ astring tempstring;
+ return entry->format(tempstring, "", true).len();
+}
+
+
+//-------------------------------------------------
+// set_state - set the value of the given piece
+// of indexed state from a UINT64
+//-------------------------------------------------
+
+void device_state_interface::set_state(int index, UINT64 value)
+{
+ // NULL or out-of-range entry is a no-op
+ const device_state_entry *entry = state_find_entry(index);
+ if (entry == NULL)
+ return;
+
+ // set the value
+ entry->set_value(value);
+
+ // call the importer to finish up
+ if (entry->needs_import())
+ state_import(*entry);
+}
+
+
+//-------------------------------------------------
+// set_state - set the value of the given piece
+// of indexed state from a string
+//-------------------------------------------------
+
+void device_state_interface::set_state_string(int index, const char *string)
+{
+ // NULL or out-of-range entry is a no-op
+ const device_state_entry *entry = state_find_entry(index);
+ if (entry == NULL)
+ return;
+
+ // set the value
+ entry->set_value(string);
+
+ // call the importer to finish up
+ if (entry->needs_import())
+ state_import(*entry);
+}
+
+
+//-------------------------------------------------
+// state_add - return the value of the given
+// pieces of indexed state as a UINT64
+//-------------------------------------------------
+
+device_state_entry &device_state_interface::state_add(int index, const char *symbol, void *data, UINT8 size)
+{
+ // assert validity of incoming parameters
+ assert(size == 1 || size == 2 || size == 4 || size == 8);
+ assert(symbol != NULL);
+
+ // allocate new entry
+ device_state_entry *entry = global_alloc(device_state_entry(index, symbol, data, size));
+
+ // append to the end of the list
+ m_state_list.append(*entry);
+
+ // set the fast entry if applicable
+ if (index >= k_fast_state_min && index <= k_fast_state_max)
+ m_fast_state[index - k_fast_state_min] = entry;
+
+ return *entry;
+}
+
+
+//-------------------------------------------------
+// state_import - called after new state is
+// written to perform any post-processing
+//-------------------------------------------------
+
+void device_state_interface::state_import(const device_state_entry &entry)
+{
+ // do nothing by default
+}
+
+
+//-------------------------------------------------
+// state_export - called prior to new state
+// reading the state
+//-------------------------------------------------
+
+void device_state_interface::state_export(const device_state_entry &entry)
+{
+ // do nothing by default
+}
+
+
+//-------------------------------------------------
+// state_string_import - called after new state is
+// written to perform any post-processing
+//-------------------------------------------------
+
+void device_state_interface::state_string_import(const device_state_entry &entry, astring &string)
+{
+ // do nothing by default
+}
+
+
+//-------------------------------------------------
+// state_string_export - called after new state is
+// written to perform any post-processing
+//-------------------------------------------------
+
+void device_state_interface::state_string_export(const device_state_entry &entry, astring &string)
+{
+ // do nothing by default
+}
+
+
+//-------------------------------------------------
+// interface_post_start - verify that state was
+// properly set up
+//-------------------------------------------------
+
+void device_state_interface::interface_post_start()
+{
+ // make sure we got something during startup
+ if (m_state_list.count() == 0)
+ throw emu_fatalerror("No state registered for device '%s' that supports it!", m_device.tag());
+}
+
+
+//-------------------------------------------------
+// state_find_entry - return a pointer to the
+// state entry for the given index
+//-------------------------------------------------
+
+const device_state_entry *device_state_interface::state_find_entry(int index)
+{
+ // use fast lookup if possible
+ if (index >= k_fast_state_min && index <= k_fast_state_max)
+ return m_fast_state[index - k_fast_state_min];
+
+ // otherwise, scan the first
+ for (const device_state_entry *entry = m_state_list.first(); entry != NULL; entry = entry->m_next)
+ if (entry->m_index == index)
+ return entry;
+
+ // handle failure by returning NULL
+ return NULL;
+}