More user experience improvements:

frontend: Allow clicking the adjuster arrows on menu items.  This allows
things like video options and DIP switches to be configured using a
mouse only.  Also fixed a bug preventing paging menus with a mouse if
the first item scrolled off the bottom is not selectable.

debugger: Allow wplist and bplist to accept a CPU argument to list
breakpoints/watchpoints for a single CPU only.

debugger: Fixed some corner cases in address space syntax in memory
accesses, and allowed memory region accesses to use tags relative to the
visible CPU.

emu/softlist.cpp: Ignore notes elements when loading software lists.
It's effectively a comment that isn't a comment syntactically, it's
being used for things that are not useful to display in the internal UI,
and it slows down startup.

docs: Updated three more pages of debugger documentation.  Also updated
more of the built-in debugger help.

minimaws: Fixed up schema for software list notes, made sofware list
notes display initially collapsed.
This commit is contained in:
Vas Crabb 2021-10-14 08:00:04 +11:00
parent 75f9660fa2
commit 96ca1dbd96
24 changed files with 1275 additions and 922 deletions

View File

@ -3,126 +3,163 @@
Breakpoint Debugger Commands
============================
You can also type **help <command>** for further details on each command in the MAME Debugger interface.
| :ref:`debugger-command-bpset` -- sets breakpoint at <address>
| :ref:`debugger-command-bpclear` -- clears a given breakpoint or all if no <bpnum> specified
| :ref:`debugger-command-bpdisable` -- disables a given breakpoint or all if no <bpnum> specified
| :ref:`debugger-command-bpenable` -- enables a given breakpoint or all if no <bpnum> specified
| :ref:`debugger-command-bplist` -- lists all the breakpoints
:ref:`debugger-command-bpset`
sets a breakpoint at <address>
:ref:`debugger-command-bpclear`
clears a specific breakpoint or all breakpoints
:ref:`debugger-command-bpdisable`
disables a specific breakpoint or all breakpoints
:ref:`debugger-command-bpenable`
enables a specific breakpoint or all breakpoints
:ref:`debugger-command-bplist`
lists breakpoints
.. _debugger-command-bpset:
.. _debugger-command-bpset:
bpset
-----
| **bp[set] <address>[,<condition>[,<action>]]**
|
| Sets a new execution breakpoint at the specified <address>.
| The optional <condition> parameter lets you specify an expression that will be evaluated each time the breakpoint is hit. If the result of the expression is true (non-zero), the breakpoint will actually halt execution; otherwise, execution will continue with no notification.
| The optional <action> parameter provides a command that is executed whenever the breakpoint is hit and the <condition> is true. Note that you may need to embed the action within braces { } in order to prevent commas and semicolons from being interpreted as applying to the bpset command itself. Each breakpoint that is set is assigned an index which can be used in other breakpoint commands to reference this breakpoint.
|
| Examples:
|
| bp 1234
|
| Set a breakpoint that will halt execution whenever the PC is equal to 1234.
|
| bp 23456,a0 == 0 && a1 == 0
|
| Set a breakpoint that will halt execution whenever the PC is equal to 23456 AND the expression (a0 == 0 && a1 == 0) is true.
|
| bp 3456,1,{printf "A0=%08X\\n",a0; g}
|
| Set a breakpoint that will halt execution whenever the PC is equal to 3456. When this happens, print A0=<a0val> and continue executing.
|
| bp 45678,a0==100,{a0 = ff; g}
|
| Set a breakpoint that will halt execution whenever the PC is equal to 45678 AND the expression (a0 == 100) is true. When that happens, set a0 to ff and resume execution.
|
| temp0 = 0; bp 567890,++temp0 >= 10
|
| Set a breakpoint that will halt execution whenever the PC is equal to 567890 AND the expression (++temp0 >= 10) is true. This effectively breaks only after the breakpoint has been hit 16 times.
|
| Back to :ref:`debugger-breakpoint-list`
**bp[set] <address>[:<CPU>][,<condition>[,<action>]]**
Sets a new execution breakpoint at the specified **<address>**. The
**<address>** may optionally be followed by a colon and a tag or
debugger CPU number to set a breakpoint for a specific CPU. If no CPU
is specified, the breakpoint will be set for the CPU currently visible
in the debugger.
The optional **<condition>** parameter lets you specify an expression
that will be evaluated each time the breakpoint address is hit. If the
result of the expression is true (non-zero), the breakpoint will halt
execution; otherwise, execution will continue with no notification. The
optional **<action>** parameter provides a command to be executed
whenever the breakpoint is hit and the **<condition>** is true. Note
that you may need to surround the action with braces ``{ }`` to ensure
commas and semicolons within the command are not interpreted in the
context of the ``bpset`` command itself.
Each breakpoint that is set is assigned a numeric index which can be
used to refer to it in other breakpoint commands. Breakpoint indices
are unique throughout a session.
Examples:
``bp 1234``
Set a breakpoint for the visible CPU that will halt execution
whenever the PC is equal to 1234.
``bp 23456,a0 == 0 && a1 == 0``
Set a breakpoint for the visible CPU that will halt execution
whenever the PC is equal to 23456 *and* the expression
``a0 == 0 && a1 == 0`` is true.
``bp 3456:audiocpu,1,{ printf "A0=%08X\n",a0 ; g }``
Set a breakpoint for the CPU with the absolute tag path
``:audiocpu`` that will halt execution whenever the PC is equal to
3456. When this happens, print **A0=<a0val>** to the debugger
console and resume execution.
``bp 45678:2,a0==100,{ a0 = ff ; g }``
Set a breakpoint on the third CPU in the system (zero-based index)
that will halt execution whenever the PC is equal to 45678 and the
expression ``a0 == 100`` is true. When that happens, set **a0** to
ff and resume execution.
``temp0 = 0 ; bp 567890,++temp0 >= 10``
Set a breakpoint for the visible CPU that will halt execution
whenever the PC is equal to 567890 and the expression
``++temp0 >= 10`` is true. This effectively breaks only after the
breakpoint has been hit sixteen times.
Back to :ref:`debugger-breakpoint-list`
.. _debugger-command-bpclear:
.. _debugger-command-bpclear:
bpclear
-------
| **bpclear [<bpnum>]**
|
| The bpclear command clears a breakpoint. If <bpnum> is specified, only the requested breakpoint is cleared, otherwise all breakpoints are cleared.
|
| Examples:
|
| bpclear 3
|
| Clear breakpoint index 3.
|
| bpclear
|
| Clear all breakpoints.
|
| Back to :ref:`debugger-breakpoint-list`
**bpclear [<bpnum>]**
Clear breakpoints. If **<bpnum>** is specified, the breakpoint it
refers to will be cleared. If **<bpnum>** is not specified, all
breakpoints will be cleared.
Examples:
``bpclear 3``
Clear the breakpoint with index 3.
``bpclear``
Clear all breakpoints.
Back to :ref:`debugger-breakpoint-list`
.. _debugger-command-bpdisable:
.. _debugger-command-bpdisable:
bpdisable
---------
| **bpdisable [<bpnum>]**
|
| The bpdisable command disables a breakpoint. If <bpnum> is specified, only the requested breakpoint is disabled, otherwise all breakpoints are disabled. Note that disabling a breakpoint does not delete it, it just temporarily marks the breakpoint as inactive.
|
| Examples:
|
| bpdisable 3
|
| Disable breakpoint index 3.
|
| bpdisable
|
| Disable all breakpoints.
|
| Back to :ref:`debugger-breakpoint-list`
**bpdisable [<bpnum>]**
Disable breakpoints. If **<bpnum>** is specified, the breakpoint it
refers to will be disabled. If **<bpnum>** is not specified, all
breakpoints will be disabled.
Note that disabling a breakpoint does not delete it, it just temporarily
marks the breakpoint as inactive. Disabled breakpoints will not cause
execution to halt, their associated condition expressions will not be
evaluated, and their associated commands will not be executed.
Examples:
``bpdisable 3``
Disable the breakpoint with index 3.
``bpdisable``
Disable all breakpoints.
Back to :ref:`debugger-breakpoint-list`
.. _debugger-command-bpenable:
.. _debugger-command-bpenable:
bpenable
--------
| **bpenable [<bpnum>]**
|
| The bpenable command enables a breakpoint. If <bpnum> is specified, only the requested breakpoint is enabled, otherwise all breakpoints are enabled.
|
| Examples:
|
| bpenable 3
|
| Enable breakpoint index 3.
|
| bpenable
|
| Enable all breakpoints.
|
| Back to :ref:`debugger-breakpoint-list`
**bpenable [<bpnum>]**
Enable breakpoints. If **<bpnum>** is specified, the breakpoint it
refers to will be enabled. If **<bpnum>** is not specified, all
breakpoints will be enabled.
Examples:
``bpenable 3``
Enable the breakpoint with index 3.
``bpenable``
Enable all breakpoints.
Back to :ref:`debugger-breakpoint-list`
.. _debugger-command-bplist:
.. _debugger-command-bplist:
bplist
------
| **bplist**
|
| The bplist command lists all the current breakpoints, along with their index and any conditions or actions attached to them.
|
| Back to :ref:`debugger-breakpoint-list`
**bplist [<CPU>]**
List current breakpoints, along with their indices and any associated
conditions or actions. If no **<CPU>** is specified, breakpoints for
all CPUs in the system will be listed; if a **<CPU>** is specified, only
breakpoints for that CPU will be listed. The **<CPU>** can be specified
by tag or by debugger CPU number (see :ref:`debugger-devicespec` for
details).
Examples:
``bplist``
List all breakpoints.
``bplist .``
List all breakpoints for the visible CPU.
``bplist maincpu``
List all breakpoints for the CPU with the absolute tag path
``:maincpu``.
Back to :ref:`debugger-breakpoint-list`

View File

@ -3,358 +3,456 @@
Execution Debugger Commands
===========================
You can also type **help <command>** for further details on each command in the MAME Debugger interface.
| :ref:`debugger-command-step` -- single steps for <count> instructions (F11)
| :ref:`debugger-command-over` -- single steps over <count> instructions (F10)
| :ref:`debugger-command-out` -- single steps until the current subroutine/exception handler is exited (Shift-F11)
| :ref:`debugger-command-go` -- resumes execution, sets temp breakpoint at <address> (F5)
| :ref:`debugger-command-gint` -- resumes execution, setting temp breakpoint if <irqline> is taken (F7)
| :ref:`debugger-command-gtime` -- resumes execution until the given delay has elapsed
| :ref:`debugger-command-gvblank` -- resumes execution, setting temp breakpoint on the next VBLANK (F8)
| :ref:`debugger-command-next` -- executes until the next CPU switch (F6)
| :ref:`debugger-command-focus` -- focuses debugger only on <cpu>
| :ref:`debugger-command-ignore` -- stops debugging on <cpu>
| :ref:`debugger-command-observe` -- resumes debugging on <cpu>
| :ref:`debugger-command-trace` -- trace the given CPU to a file (defaults to active CPU)
| :ref:`debugger-command-traceover` -- trace the given CPU to a file, but skip subroutines (defaults to active CPU)
| :ref:`debugger-command-traceflush` -- flushes all open trace files.
:ref:`debugger-command-step`
single step for <count> instructions (F11)
:ref:`debugger-command-over`
single step over <count> instructions (F10)
:ref:`debugger-command-out`
single step until the current subroutine/exception handler returns
(Shift-F11)
:ref:`debugger-command-go`
resume execution (F5)
:ref:`debugger-command-gex`
resume execution until exception is raised
:ref:`debugger-command-gint`
resume execution until interrupt is taken (F7)
:ref:`debugger-command-gtime`
resume execution until the given delay has elapsed
:ref:`debugger-command-gvblank`
resume execution until next vertical blanking interval (F8)
:ref:`debugger-command-next`
resume execution until the next CPU switch (F6)
:ref:`debugger-command-focus`
focus debugger only on <CPU>
:ref:`debugger-command-ignore`
stop debugging on <CPU>
:ref:`debugger-command-observe`
resume debugging on <CPU>
:ref:`debugger-command-trace`
trace the specified CPU to a file
:ref:`debugger-command-traceover`
trace the specified CPU to a file skipping subroutines
:ref:`debugger-command-traceflush`
flush all open trace files.
.. _debugger-command-step:
.. _debugger-command-step:
step
----
| **s[tep] [<count>=1]**
|
| The step command single steps one or more instructions in the currently executing CPU. By default, step executes one instruction each time it is issued. You can also tell step to step multiple instructions by including the optional <count> parameter.
|
| Examples:
|
| s
|
| Steps forward one instruction on the current CPU.
|
| step 4
|
| Steps forward four instructions on the current CPU.
|
| Back to :ref:`debugger-execution-list`
**s[tep] [<count>]**
Single steps one or more instructions on the currently executing CPU.
Executes one instruction if **<count>** is omitted, or steps **<count>**
instructions if it is supplied.
Examples:
``s``
Steps forward one instruction on the current CPU.
``step 4``
Steps forward four instructions on the current CPU.
Back to :ref:`debugger-execution-list`
.. _debugger-command-over:
.. _debugger-command-over:
over
----
| **o[ver] [<count>=1]**
|
| The over command single steps "over" one or more instructions in the currently executing CPU, stepping over subroutine calls and exception handler traps and counting them as a single instruction. Note that when stepping over a subroutine call, code may execute on other CPUs before the subroutine call completes. By default, over executes one instruction each time it is issued. You can also tell step to step multiple instructions by including the optional <count> parameter.
|
| Note that the step over functionality may not be implemented on all CPU types. If it is not implemented, then 'over' will behave exactly like 'step'.
|
| Examples:
|
| o
|
| Steps forward over one instruction on the current CPU.
|
| over 4
|
| Steps forward over four instructions on the current CPU.
|
| Back to :ref:`debugger-execution-list`
**o[ver] [<count>]**
The over command single steps “over” one or more instructions on the
currently executing CPU, stepping over subroutine calls and exception
handler traps and counting them as a single instruction. Note that when
stepping over a subroutine call, code may execute on other CPUs before
the subroutine returns.
Steps over one instruction if **<count>** is omitted, or steps over
**<count>** instructions if it is supplied.
Note that the step over functionality may not be implemented for all CPU
types. If it is not implemented, then ``over`` will behave exactly like
:ref:`debugger-command-step`.
Examples:
``o``
Steps forward over one instruction on the current CPU.
``over 4``
Steps forward over four instructions on the current CPU.
Back to :ref:`debugger-execution-list`
.. _debugger-command-out:
.. _debugger-command-out:
out
---
| **out**
|
| The out command single steps until it encounters a return from subroutine or return from exception instruction. Note that because it detects return from exception conditions, if you attempt to step out of a subroutine and an interrupt/exception occurs before you hit the end, then you may stop prematurely at the end of the exception handler.
|
| Note that the step out functionality may not be implemented on all CPU types. If it is not implemented, then 'out' will behave exactly like 'step'.
|
| Examples:
|
| out
|
| Steps until the current subroutine or exception handler returns.
|
| Back to :ref:`debugger-execution-list`
**out**
Single steps until a return from subroutine or return from exception
instruction is encountered. Note that because it detects return from
exception conditions, if you attempt to step out of a subroutine and an
interrupt/exception occurs before the subroutine completes, execution
may stop prematurely at the end of the exception handler.
Note that the step out functionality may not be implemented for all CPU
types. If it is not implemented, then ``out`` will behave exactly like
:ref:`debugger-command-step`.
Example:
``out``
Steps until a subroutine or exception handler returns.
Back to :ref:`debugger-execution-list`
.. _debugger-command-go:
.. _debugger-command-go:
go
--
| **g[o] [<address>]**
|
| The go command resumes execution of the current code. Control will not be returned to the debugger until a breakpoint or watchpoint is hit, or until you manually break in using the assigned key. The go command takes an optional <address> parameter which is a temporary unconditional breakpoint that is set before executing, and automatically removed when hit.
|
| Examples:
|
| g
|
| Resume execution until the next break/watchpoint or until a manual break.
|
| g 1234
|
| Resume execution, stopping at address 1234 unless something else stops us first.
|
| Back to :ref:`debugger-execution-list`
**g[o] [<address>]**
Resumes execution. Control will not be returned to the debugger until a
breakpoint or watchpoint is triggered, or a debugger break is manually
requested. If the optional **<address>** is supplied, a temporary
unconditional breakpoint will be set for the visible CPU at the
specified address. It will be cleared automatically when triggered.
Examples:
``g``
Resume execution until a breakpoint/watchpoint is triggered, or a
break is manually requested.
``g 1234``
Resume execution, stopping at address 1234, unless another condition
causes execution to stop before then.
Back to :ref:`debugger-execution-list`
.. _debugger-command-gvblank:
.. _debugger-command-gex:
gvblank
-------
gex
---
| **gv[blank]**
|
| The gvblank command resumes execution of the current code. Control will not be returned to the debugger until a breakpoint or watchpoint is hit, or until the next VBLANK occurs in the emulator.
|
| Examples:
|
| gv
|
| Resume execution until the next break/watchpoint or until the next VBLANK.
|
| Back to :ref:`debugger-execution-list`
**ge[x] [<exception>,[<condition>]]**
Resumes execution. Control will not be returned to the debugger until
a breakpoint or watchpoint is triggered, or until an exception condition
is raised on the current CPU. Use the optional **<exception>**
parameter to stop execution only for a specific exception condition. If
**<exception>** is omitted, execution will stop for any exception
condition.
The optional **<condition>** parameter lets you specify an expression
that will be evaluated each time the specified exception condition
is raised. If the result of the expression is true (non-zero), the
exception will halt execution; otherwise, execution will continue with
no notification.
Examples:
``gex``
Resume execution until a breakpoint/watchpoint is triggered, or
until any exception condition is raised on the current CPU.
``ge 2``
Resume execution until a breakpoint/watchpoint is triggered, or
until exception condition 2 is raised on the current CPU.
Back to :ref:`debugger-execution-list`
.. _debugger-command-gint:
.. _debugger-command-gint:
gint
----
| **gi[nt] [<irqline>]**
|
| The gint command resumes execution of the current code. Control will not be returned to the debugger until a breakpoint or watchpoint is hit, or until an IRQ is asserted and acknowledged on the current CPU. You can specify <irqline> if you wish to stop execution only on a particular IRQ line being asserted and acknowledged. If <irqline> is omitted, then any IRQ line will stop execution.
|
| Examples:
|
| gi
|
| Resume execution until the next break/watchpoint or until any IRQ is asserted and acknowledged on the current CPU.
|
| gint 4
|
| Resume execution until the next break/watchpoint or until IRQ line 4 is asserted and acknowledged on the current CPU.
|
| Back to :ref:`debugger-execution-list`
**gi[nt] [<irqline>]**
Resumes execution. Control will not be returned to the debugger until a
breakpoint or watchpoint is triggered, or until an interrupt is asserted
and acknowledged on the current CPU. Use the optional **<irqline>**
parameter to stop execution only for a specific interrupt line being
asserted and acknowledged. If **<irqline>** is omitted, execution will
stop when any interrupt is acknowledged.
Examples:
``gi``
Resume execution until a breakpoint/watchpoint is triggered, or
any interrupt is asserted and acknowledged on the current CPU.
``gint 4``
Resume execution until a breakpoint/watchpoint is triggered, or
interrupt request line 4 is asserted and acknowledged on the current
CPU.
Back to :ref:`debugger-execution-list`
.. _debugger-command-gtime:
.. _debugger-command-gtime:
gtime
-----
| **gt[ime] <milliseconds>**
|
| The gtime command resumes execution of the current code. Control will not be returned to the debugger until a specified delay has elapsed. The delay is in milliseconds.
|
| Example:
|
| gtime #10000
|
| Resume execution for ten seconds
|
| Back to :ref:`debugger-execution-list`
**gt[ime] <milliseconds>**
Resumes execution. Control will not be returned to the debugger until a
specified interval of emulated time has elapsed. The interval is
specified in milliseconds.
Example:
``gtime #10000```
Resume execution for ten seconds of emulated time.
Back to :ref:`debugger-execution-list`
.. _debugger-command-next:
.. _debugger-command-gvblank:
gvblank
-------
**gv[blank]**
Resumes execution. Control will not be returned to the debugger until a
breakpoint or watchpoint is triggered, or until the beginning of the
vertical blanking interval for an emulated screen.
Example:
``gv``
Resume execution until a breakpoint/watchpoint is triggered, or a
vertical blanking interval starts.
Back to :ref:`debugger-execution-list`
.. _debugger-command-next:
next
----
| **n[ext]**
|
| The next command resumes execution and continues executing until the next time a different CPU is scheduled. Note that if you have used 'ignore' to ignore certain CPUs, you will not stop until a non-'ignore'd CPU is scheduled.
|
| Back to :ref:`debugger-execution-list`
**n[ext]**
Resumes execution until a different CPU is scheduled. Execution will
not stop when a CPU is scheduled if it is ignored due to the use of
:ref:`debugger-command-ignore` or :ref:`debugger-command-focus`.
Example:
``n``
Resume execution, stopping when a different CPU that is not ignored
is scheduled.
Back to :ref:`debugger-execution-list`
.. _debugger-command-focus:
.. _debugger-command-focus:
focus
-----
| **focus <cpu>**
|
| Sets the debugger focus exclusively to the given <cpu>. This is equivalent to specifying 'ignore' on all other CPUs.
|
| Example:
|
| focus 1
|
| Focus exclusively CPU #1 while ignoring all other CPUs when using the debugger.
|
| Back to :ref:`debugger-execution-list`
**focus <CPU>**
Focus exclusively on to the specified **<CPU>**, ignoring all other
CPUs. The **<CPU>** argument can be a device tag or debugger CPU number
(see :ref:`debugger-devicespec` for details). This is equivalent to
using the :ref:`debugger-command-ignore` command to ignore all CPUs
besides the specified CPU.
Examples:
``focus 1``
Focus exclusively on the second CPU in the system (zero-based
index), ignoring all other CPUs.
``focus audiopcb:melodycpu``
Focus exclusively on the CPU with the absolute tag path
``:audiopcb:melodycpu``.
Back to :ref:`debugger-execution-list`
.. _debugger-command-ignore:
.. _debugger-command-ignore:
ignore
------
| **ignore [<cpu>[,<cpu>[,...]]]**
|
| Ignores the specified <cpu> in the debugger. This means that you won't ever see execution on that CPU, nor will you be able to set breakpoints on that CPU. To undo this change use the 'observe' command. You can specify multiple <cpu>s in a single command. Note also that you are not permitted to ignore all CPUs; at least one must be active at all times.
|
| Examples:
|
| ignore 1
|
| Ignore CPU #1 when using the debugger.
|
| ignore 2,3,4
|
| Ignore CPU #2, #3 and #4 when using the debugger.
|
| ignore
|
| List the CPUs that are currently ignored.
|
| Back to :ref:`debugger-execution-list`
**ignore [<CPU>[,<CPU>[,...]]]**
Ignores the specified CPUs in the debugger. CPUs can be specified by
tag or debugger CPU number (see :ref:`debugger-devicespec` for details).
The debugger never shows execution for ignored CPUs, and breakpoints or
watchpoints on ignored CPUs have no effect. If no CPUs are specified,
currently ignored CPUs will be listed. Use the
:ref:`debugger-command-observe` command to stop ignoring a CPU.
Note that you cannot ignore all CPUs; at least CPU must be observed at
all times.
Examples:
``ignore audiocpu``
Ignore the CPU with the absolute tag path ``:audiocpu`` when using
the debugger.
``ignore 2,3,4``
Ignore the third, fourth and fifth CPUs in the system (zero-based
indices) when using the debugger.
``ignore``
List the CPUs that are currently being ignored by the debugger.
Back to :ref:`debugger-execution-list`
.. _debugger-command-observe:
.. _debugger-command-observe:
observe
-------
| **observe [<cpu>[,<cpu>[,...]]]**
|
| Re-enables interaction with the specified <cpu> in the debugger. This command undoes the effects of the 'ignore' command. You can specify multiple <cpu>s in a single command.
|
| Examples:
|
| observe 1
|
| Stop ignoring CPU #1 when using the debugger.
|
| observe 2,3,4
|
| Stop ignoring CPU #2, #3 and #4 when using the debugger.
|
| observe
|
| List the CPUs that are currently observed.
|
| Back to :ref:`debugger-execution-list`
**observe [<CPU>[,<CPU>[,...]]]**
Allow interaction with the specified CPUs in the debugger. CPUs can be
specified by tag or debugger CPU number (see :ref:`debugger-devicespec`
for details). This command reverses the effects of the
:ref:`debugger-command-ignore` command. If no CPUs are specified,
currently observed CPUs will be listed.
Examples:
``observe audiocpu``
Stop ignoring the CPU with the absolute tag path ``:audiocpu`` when
using the debugger.
``observe 2,3,4``
Stop ignoring the third, fourth and fifth CPUs in the system
(zero-based indices) when using the debugger.
``observe``
List the CPUs that are currently being observed by the debugger.
Back to :ref:`debugger-execution-list`
.. _debugger-command-trace:
.. _debugger-command-trace:
trace
-----
| **trace {<filename>|OFF}[,<cpu>[,[noloop|logerror][,<action>]]]**
|
| Starts or stops tracing of the execution of the specified <cpu>. If <cpu> is omitted, the currently active CPU is specified.
|
| When enabling tracing, specify the filename in the <filename> parameter. To disable tracing, substitute the keyword 'off' for <filename>.
|
| <detectloops> should be either true or false.
|
| If 'noloop' is omitted, the trace will have loops detected and condensed to a single line. If 'noloop' is specified, the trace will contain every opcode as it is executed.
|
| If 'logerror' is specified, logerror output will augment the trace. If you wish to log additional information on each trace, you can append an <action> parameter which is a command that is executed before each trace is logged. Generally, this is used to include a 'tracelog' command. Note that you may need to embed the action within braces { } in order to prevent commas and semicolons from being interpreted as applying to the trace command itself.
|
|
| Examples:
|
| trace joust.tr
|
| Begin tracing the currently active CPU, logging output to joust.tr.
|
| trace dribling.tr,0
|
| Begin tracing the execution of CPU #0, logging output to dribling.tr.
|
| trace starswep.tr,0,noloop
|
| Begin tracing the execution of CPU #0, logging output to starswep.tr, with loop detection disabled.
|
| trace starswep.tr,0,logerror
|
| Begin tracing the execution of CPU #0, logging output (along with logerror output) to starswep.tr.
|
| trace starswep.tr,0,logerror|noloop
|
| Begin tracing the execution of CPU #0, logging output (along with logerror output) to starswep.tr, with loop detection disabled.
|
| trace >>pigskin.tr
|
| Begin tracing the currently active CPU, appending log output to pigskin.tr.
|
| trace off,0
|
| Turn off tracing on CPU #0.
|
| trace asteroid.tr,0,,{tracelog "A=%02X ",a}
|
| Begin tracing the execution of CPU #0, logging output to asteroid.tr. Before each line, output A=<aval> to the tracelog.
|
| Back to :ref:`debugger-execution-list`
**trace {<filename>|off}[,<CPU>[,[noloop|logerror][,<action>]]]**
Starts or stops tracing for execution of the specified **<CPU>**, or the
currently visible CPU if no CPU is specified. To enable tracing,
specify the trace log file name in the **<filename>** parameter. To
disable tracing, use the keyword ``off`` for for the **<filename>**
parameter. If the **<filename>** argument begins with two right angle
brackets (**>>**), it is treated as a directive to open the file for
appending rather than overwriting.
The optional third parameter is a flags field. The supported flags are
``noloop`` and ``logerror``. Multiple flags must be separated by ``|``
(pipe) characters. By default, loops are detected and condensed to a
single line. If the ``noloop`` flag is specified, loops will not be
detected and every instruction will be logged as executed. If the
``logerror`` flag is specified, error log output will be included in the
trace log.
The optional **<action>** parameter is a debugger command to execute
before each trace message is logged. Generally, this will include a
:ref:`debugger-command-tracelog` or :ref:`debugger-command-tracesym`
command to include additional information in the trace log. Note that
you may need to surround the action within braces ``{ }`` to ensure
commas and semicolons within the command are not interpreted in the
context of the ``trace`` command itself.
Examples:
``trace joust.tr``
Begin tracing the execution of the currently visible CPU, logging
output to the file **joust.tr**.
``trace dribling.tr,maincpu``
Begin tracing the execution of the CPU with the absolute tag path
``:maincpu:``, logging output to the file **dribling.tr**.
``trace starswep.tr,,noloop``
Begin tracing the execution of the currently visible CPU, logging
output to the file **starswep.tr**, with loop detection disabled.
``trace starswep.tr,1,logerror``
Begin tracing the execution of the second CPU in the system
(zero-based index), logging output along with error log output to
the file **starswep.tr**.
``trace starswep.tr,0,logerror|noloop``
Begin tracing the execution of the first CPU in the system
(zero-based index), logging output along with error log output to
the file **starswep.tr**, with loop detection disabled.
``trace >>pigskin.tr``
Begin tracing execution of the currently visible CPU, appending log
output to the file **pigskin.tr**.
``trace off,0``
Turn off tracing for the first CPU in the system (zero-based index).
``trace asteroid.tr,,,{tracelog "A=%02X ",a}``
Begin tracing the execution of the currently visible CPU, logging
output to the file **asteroid.tr**. Before each line, output
**A=<aval>** to the trace log.
Back to :ref:`debugger-execution-list`
.. _debugger-command-traceover:
.. _debugger-command-traceover:
traceover
---------
| **traceover {<filename>|OFF}[,<cpu>[,<detectloops>[,<action>]]]**
|
| Starts or stops tracing of the execution of the specified <cpu>.
|
| When tracing reaches a subroutine or call, tracing will skip over the subroutine. The same algorithm is used as is used in the step over command. This means that traceover will not work properly when calls are recursive or the return address is not immediately following the call instruction.
|
| <detectloops> should be either true or false. If <detectloops> is *true or omitted*, the trace will have loops detected and condensed to a single line. If it is false, the trace will contain every opcode as it is executed.
| If <cpu> is omitted, the currently active CPU is specified.
| When enabling tracing, specify the filename in the <filename> parameter.
| To disable tracing, substitute the keyword 'off' for <filename>.
| If you wish to log additional information on each trace, you can append an <action> parameter which is a command that is executed before each trace is logged. Generally, this is used to include a 'tracelog' command. Note that you may need to embed the action within braces { } in order to prevent commas and semicolons from being interpreted as applying to the trace command itself.
|
|
| Examples:
|
| traceover joust.tr
|
| Begin tracing the currently active CPU, logging output to joust.tr.
|
| traceover dribling.tr,0
|
| Begin tracing the execution of CPU #0, logging output to dribling.tr.
|
| traceover starswep.tr,0,false
|
| Begin tracing the execution of CPU #0, logging output to starswep.tr, with loop detection disabled.
|
| traceover off,0
|
| Turn off tracing on CPU #0.
|
| traceover asteroid.tr,0,true,{tracelog "A=%02X ",a}
|
| Begin tracing the execution of CPU #0, logging output to asteroid.tr. Before each line, output A=<aval> to the tracelog.
|
| Back to :ref:`debugger-execution-list`
**traceover {<filename>|off}[,<CPU>[,[noloop|logerror][,<action>]]]**
Starts or stops tracing for execution of the specified **<CPU>**, or the
currently visible CPU if no CPU is specified. When a subroutine call is
encountered, tracing will skip over the subroutine. The same algorithm
is used as is used in the :ref:`step over <debugger-command-over>`
command. It will not work properly with recursive functions, or if the
return address does not immediately follow the call instruction.
This command accepts the same parameters as the
:ref:`debugger-command-trace` command. Please refer to the
corresponding section for a detailed description of options and more
examples.
Examples:
``traceover joust.tr``
Begin tracing the execution of the currently visible CPU, logging
output to the file **joust.tr**.
``traceover dribling.tr,maincpu``
Begin tracing the execution of the CPU with the absolute tag path
``:maincpu:``, logging output to the file **dribling.tr**.
``traceover starswep.tr,,noloop``
Begin tracing the execution of the currently visible CPU, logging
output to the file **starswep.tr**, with loop detection disabled.
``traceover off,0``
Turn off tracing for the first CPU in the system (zero-based index).
``traceover asteroid.tr,,,{tracelog "A=%02X ",a}``
Begin tracing the execution of the currently visible CPU, logging
output to the file **asteroid.tr**. Before each line, output
**A=<aval>** to the trace log.
Back to :ref:`debugger-execution-list`
.. _debugger-command-traceflush:
.. _debugger-command-traceflush:
traceflush
----------
| **traceflush**
|
| Flushes all open trace files.
|
| Back to :ref:`debugger-execution-list`
**traceflush**
Flushes all open trace log files to disk.
Example:
``traceflush``
Flush trace log files.
Back to :ref:`debugger-execution-list`

View File

@ -3,6 +3,8 @@
General Debugger Commands
=========================
:ref:`debugger-command-help`
displays built-in help in the console
:ref:`debugger-command-do`
evaluates the given expression
:ref:`debugger-command-symlist`
@ -43,6 +45,30 @@ General Debugger Commands
exit the debugger and end the emulation session
.. _debugger-command-help:
help
----
**help [<topic>]**
Displays built-in debugger help in the debugger console. If no
**<topic>** is specified, top-level topics are listed. Most debugger
commands have correspondingly named help topics.
Examples:
``help``
Lists top-level help topics.
``help expressions``
Displays built-in help for debugger expression syntax.
``help wpiset``
Displays built-in help for the
:ref:`wpiset <debugger-command-wpset>` command.
Back to :ref:`debugger-general-list`
.. _debugger-command-do:
do

View File

@ -274,14 +274,14 @@ The size may optionally be preceded by an access type specification:
(program)
* ``d`` or ``ld`` specifies a logical address defaulting to space 1
(data)
* ``i`` or ``li`` specifies a logical address defaulting to space 1
* ``i`` or ``li`` specifies a logical address defaulting to space 2
(I/O)
* ``3`` or ``l3`` specifies a logical address defaulting to space 1
* ``3`` or ``l3`` specifies a logical address defaulting to space 3
(opcodes)
* ``pp`` specifies a physical address defaulting to space 0 (program)
* ``pd`` specifies a physical address defaulting to space 1 (data)
* ``pi`` specifies a physical address defaulting to space 1 (I/O)
* ``p3`` specifies a physical address defaulting to space 1 (opcodes)
* ``pi`` specifies a physical address defaulting to space 2 (I/O)
* ``p3`` specifies a physical address defaulting to space 3 (opcodes)
* ``r`` specifies direct read/write pointer access defaulting to space 0
(program)
* ``o`` specifies direct read/write pointer access defaulting to space 3

View File

@ -3,126 +3,172 @@
Watchpoint Debugger Commands
============================
:ref:`debugger-command-wpset`
sets memory access watchpoints
:ref:`debugger-command-wpclear`
clears watchpoints
:ref:`debugger-command-wpdisable`
disables watchpoints
:ref:`debugger-command-wpenable`
enables enables watchpoints
:ref:`debugger-command-wplist`
lists watchpoints
You can also type **help <command>** for further details on each command in the MAME Debugger interface.
| :ref:`debugger-command-wpset` -- sets program, data, or I/O space watchpoint
| :ref:`debugger-command-wpclear` -- clears a given watchpoint or all if no <wpnum> specified
| :ref:`debugger-command-wpdisable` -- disables a given watchpoint or all if no <wpnum> specified
| :ref:`debugger-command-wpenable` -- enables a given watchpoint or all if no <wpnum> specified
| :ref:`debugger-command-wplist` -- lists all the watchpoints
.. _debugger-command-wpset:
.. _debugger-command-wpset:
wpset
-----
| **wp[{d|i}][set] <address>,<length>,<type>[,<condition>[,<action>]]**
|
| Sets a new watchpoint starting at the specified <address> and extending for <length>. The inclusive range of the watchpoint is <address> through <address> + <length> - 1.
| The 'wpset' command sets a watchpoint on program memory; the 'wpdset' command sets a watchpoint on data memory; and the 'wpiset' sets a watchpoint on I/O memory.
| The <type> parameter specifies which sort of accesses to trap on. It can be one of three values: 'r' for a read watchpoint 'w' for a write watchpoint, and 'rw' for a read/write watchpoint.
|
| The optional <condition> parameter lets you specify an expression that will be evaluated each time the watchpoint is hit. If the result of the expression is true (non-zero), the watchpoint will actually halt execution; otherwise, execution will continue with no notification.
| The optional <action> parameter provides a command that is executed whenever the watchpoint is hit and the <condition> is true. Note that you may need to embed the action within braces { } in order to prevent commas and semicolons from being interpreted as applying to the wpset command itself.
| Each watchpoint that is set is assigned an index which can be used in other watchpoint commands to reference this watchpoint.
| In order to help <condition> expressions, two variables are available. The variable 'wpaddr' is set to the address that actually triggered the watchpoint, the variable 'wpdata' is set to the data that is being read or written, and the variable 'wpsize' is set to the size of the data in bytes.
|
| Examples:
|
| wp 1234,6,rw
|
| Set a watchpoint that will halt execution whenever a read or write occurs in the address range 1234-1239 inclusive.
|
| wp 23456,a,w,wpdata == 1
|
| Set a watchpoint that will halt execution whenever a write occurs in the address range 23456-2345f AND the data written is equal to 1.
|
| wp 3456,20,r,1,{printf "Read @ %08X\\n",wpaddr; g}
|
| Set a watchpoint that will halt execution whenever a read occurs in the address range 3456-3475. When this happens, print Read @ <wpaddr> and continue executing.
|
| temp0 = 0; wp 45678,1,w,wpdata==f0,{temp0++; g}
|
| Set a watchpoint that will halt execution whenever a write occurs to the address 45678 AND the value being written is equal to f0. When that happens, increment the variable temp0 and resume execution.
|
| Back to :ref:`debugger-watchpoints-list`
**wp[{d|i|o}][set] <address>[:<space>],<length>,<type>[,<condition>[,<action>]]**
Sets a new watchpoint starting at the specified **<address>** and
extending for **<length>**. The range of the watchpoint is
**<address>** through **<address>+<length>-1**, inclusive. The
**<address>** may optionally be followed by a CPU and/or address space
(see :ref:`debugger-devicespec` for details). If an address space is
not specified, the command suffix sets the address space: ``wpset``
defaults to the first address space exposed by the CPU, ``wpdset``
defaults to the space with index 1 (data), ``wpiset`` defaults to the
space with index 2 (I/O), and ``wposet`` defaults to the space with
index 3 (opcodes). The **<type>** parameter specifies the access types
to trap on it can be one of three values: ``r`` for read accesses,
``w`` for write accesses, or ``rw`` for both read and write accesses.
The optional **<condition>** parameter lets you specify an expression
that will be evaluated each time the watchpoint is triggered. If the
result of the expression is true (non-zero), the watchpoint will halt
execution; otherwise, execution will continue with no notification. The
optional **<action>** parameter provides a command to be executed
whenever the watchpoint is triggered and the **<condition>** is true.
Note that you may need to surround the action with braces ``{ }`` to
ensure commas and semicolons within the command are not interpreted in
the context of the ``wpset`` command itself.
Each watchpoint that is set is assigned a numeric index which can be
used to refer to it in other watchpoint commands. Watchpoint indices
are unique throughout a session.
To make **<condition>** expressions more useful, two variables are
available: for all watchpoints, the variable **wpaddr** is set to the
access address that triggered the watchpoint; for write watchpoints, the
variable **wpdata** is set to the data being written.
Examples:
``wp 1234,6,rw``
Set a watchpoint for the visible CPU that will halt execution
whenever a read or write to the first address space occurs in the
address range 1234-1239, inclusive.
``wp 23456:data,a,w,wpdata == 1``
Set a watchpoint for the visible CPU that will halt execution
whenever a write to the ``data`` space occurs in the address range
23456-2345f and the data written is equal to 1.
``wp 3456:maincpu,20,r,1,{ printf "Read @ %08X\n",wpaddr ; g }``
Set a watchpoint for the CPU with the absolute tag path ``:maincpu``
that will halt execution whenever a read from the first address
space occurs in the address range 3456-3475. When this happens,
print **Read @ <wpaddr>** to the debugger console and resume
execution.
``temp0 = 0 ; wp 45678,1,w,wpdata==f0,{ temp0++ ; g }``
Set a watchpoint for the visible CPU that will halt execution
whenever a write do the first address space occurs at address 45678
where the value being written is equal to f0. When this happens,
increment the variable **temp0** and resume execution.
Back to :ref:`debugger-watchpoints-list`
.. _debugger-command-wpclear:
.. _debugger-command-wpclear:
wpclear
-------
| **wpclear [<wpnum>]**
|
| The wpclear command clears a watchpoint. If <wpnum> is specified, only the requested watchpoint is cleared, otherwise all watchpoints are cleared.
|
| Examples:
|
| wpclear 3
|
| Clear watchpoint index 3.
|
| wpclear
|
| Clear all watchpoints.
|
| Back to :ref:`debugger-watchpoints-list`
**wpclear [<wpnum>]**
Clear watchpoints. If **<wpnum>** is specified, the watchpoint it
refers to will be cleared. If **<wpnum>** is not specified, all
watchpoints will be cleared.
Examples:
``wpclear 3``
Clear the watchpoint with index 3.
``wpclear``
Clear all watchpoints.
Back to :ref:`debugger-watchpoints-list`
.. _debugger-command-wpdisable:
.. _debugger-command-wpdisable:
wpdisable
---------
| **wpdisable [<wpnum>]**
|
| The wpdisable command disables a watchpoint. If <wpnum> is specified, only the requested watchpoint is disabled, otherwise all watchpoints are disabled. Note that disabling a watchpoint does not delete it, it just temporarily marks the watchpoint as inactive.
|
| Examples:
|
| wpdisable 3
|
| Disable watchpoint index 3.
|
| wpdisable
|
| Disable all watchpoints.
|
| Back to :ref:`debugger-watchpoints-list`
**wpdisable [<wpnum>]**
Disable watchpoints. If **<wpnum>** is specified, the watchpoint it
refers to will be disabled. If **<wpnum>** is not specified, all
watchpoints will be disabled.
Note that disabling a watchpoint does not delete it, it just temporarily
marks the watchpoint as inactive. Disabled watchpoints will not cause
execution to halt, their associated condition expressions will not be
evaluated, and their associated commands will not be executed.
Examples:
``wpdisable 3``
Disable the watchpoint with index 3.
``wpdisable``
Disable all watchpoints.
Back to :ref:`debugger-watchpoints-list`
.. _debugger-command-wpenable:
.. _debugger-command-wpenable:
wpenable
--------
| **wpenable [<wpnum>]**
|
| The wpenable command enables a watchpoint. If <wpnum> is specified, only the requested watchpoint is enabled, otherwise all watchpoints are enabled.
|
| Examples:
|
| wpenable 3
|
| Enable watchpoint index 3.
|
| wpenable
|
| Enable all watchpoints.
|
| Back to :ref:`debugger-watchpoints-list`
**wpenable [<wpnum>]**
Enable watchpoints. If **<wpnum>** is specified, the watchpoint it
refers to will be enabled. If **<wpnum>** is not specified, all
watchpoints will be enabled.
Examples:
``wpenable 3``
Enable the watchpoint with index 3.
``wpenable``
Enable all watchpoints.
Back to :ref:`debugger-watchpoints-list`
.. _debugger-command-wplist:
.. _debugger-command-wplist:
wplist
------
| **wplist**
|
| The wplist command lists all the current watchpoints, along with their index and any conditions or actions attached to them.
|
| Back to :ref:`debugger-watchpoints-list`
**wplist [<CPU>]**
List current watchpoints, along with their indices and any associated
conditions or actions. If no **<CPU>** is specified, watchpoints for
all CPUs in the system will be listed; if a **<CPU>** is specified, only
watchpoints for that CPU will be listed. The **<CPU>** can be specified
by tag or by debugger CPU number (see :ref:`debugger-devicespec` for
details).
Examples:
``wplist``
List all watchpoints.
``wplist .``
List all watchpoints for the visible CPU.
``wplist maincpu``
List all watchpoints for the CPU with the absolute tag path
``:maincpu``.
Back to :ref:`debugger-watchpoints-list`

View File

@ -37,6 +37,13 @@ function make_collapsible(heading, section)
}
}
});
return function ()
{
hidden = true;
icon.setAttribute('src', assetsurl + '/discloseup.svg');
section.style.display = 'none';
};
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="16" width="16">
<path d="m 0,6 h 16 l -8,10 z" />
<path d="m 0,5 h 16 l -8,10 z" />
</svg>

Before

Width:  |  Height:  |  Size: 175 B

After

Width:  |  Height:  |  Size: 175 B

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="16" width="16">
<path d="m 6,0 v 16 l 10,-8 z" />
<path d="m 5,0 v 16 l 10,-8 z" />
</svg>

Before

Width:  |  Height:  |  Size: 175 B

After

Width:  |  Height:  |  Size: 175 B

View File

@ -32,8 +32,12 @@ class SchemaQueries(object):
' id INTEGER PRIMARY KEY,\n' \
' shortname TEXT NOT NULL,\n' \
' description TEXT NOT NULL,\n' \
' notes TEXT NULL,\n' \
' UNIQUE (shortname ASC))'
CREATE_SOFTWARELISTNOTES = \
'CREATE TABLE softwarelistnotes (\n' \
' softwarelist INTEGER PRIMARY KEY,\n' \
' notes TEXT NOT NULL,\n' \
' FOREIGN KEY (softwarelist) REFERENCES softwarelist (id))'
CREATE_SOFTWARE = \
'CREATE TABLE software (\n' \
' id INTEGER PRIMARY KEY,\n' \
@ -43,7 +47,6 @@ class SchemaQueries(object):
' description TEXT NOT NULL,\n' \
' year TEXT NOT NULL,\n' \
' publisher TEXT NOT NULL,\n' \
' notes TEXT NULL,\n' \
' UNIQUE (softwarelist ASC, shortname ASC),\n' \
' FOREIGN KEY (softwarelist) REFERENCES softwarelist (id))'
CREATE_SOFTWARECLONEOF = \
@ -52,6 +55,11 @@ class SchemaQueries(object):
' parent INTEGER NOT NULL,\n' \
' FOREIGN KEY (id) REFERENCES software (id),\n' \
' FOREIGN KEY (parent) REFERENCES software (id))'
CREATE_SOFTWARENOTES = \
'CREATE TABLE softwarenotes (\n' \
' software INTEGER PRIMARY KEY,\n' \
' notes TEXT NOT NULL,\n' \
' FOREIGN KEY (software) REFERENCES software (id))'
CREATE_SOFTWAREINFO = \
'CREATE TABLE softwareinfo (\n' \
' id INTEGER PRIMARY KEY,\n' \
@ -392,8 +400,10 @@ class SchemaQueries(object):
CREATE_SOFTWARESHAREDFEATTYPE,
CREATE_SOFTWAREPARTFEATURETYPE,
CREATE_SOFTWARELIST,
CREATE_SOFTWARELISTNOTES,
CREATE_SOFTWARE,
CREATE_SOFTWARECLONEOF,
CREATE_SOFTWARENOTES,
CREATE_SOFTWAREINFO,
CREATE_SOFTWARESHAREDFEAT,
CREATE_SOFTWAREPART,
@ -502,13 +512,13 @@ class UpdateQueries(object):
ADD_SOFTWARESHAREDFEATTYPE = 'INSERT OR IGNORE INTO softwaresharedfeattype (name) VALUES (?)'
ADD_SOFTWAREPARTFEATURETYPE = 'INSERT OR IGNORE INTO softwarepartfeaturetype (name) VALUES(?)'
ADD_SOFTWARELIST = 'INSERT INTO softwarelist (shortname, description) VALUES (?, ?)'
ADD_SOFTWARELISTNOTES = 'INSERT INTO softwarelistnotes (softwarelist, notes) VALUES (?, ?)'
ADD_SOFTWARE = 'INSERT INTO software (softwarelist, shortname, supported, description, year, publisher) VALUES (?, ?, ?, ?, ?, ?)'
ADD_SOFTWARENOTES = 'INSERT INTO softwarenotes (software, notes) VALUES (?, ?)'
ADD_SOFTWAREINFO = 'INSERT INTO softwareinfo (software, infotype, value) SELECT ?, id, ? FROM softwareinfotype WHERE name = ?'
ADD_SOFTWARESHAREDFEAT = 'INSERT INTO softwaresharedfeat (software, sharedfeattype, value) SELECT ?, id, ? FROM softwaresharedfeattype WHERE name = ?'
ADD_SOFTWAREPART = 'INSERT INTO softwarepart (software, shortname, interface) VALUES (?, ?, ?)'
ADD_SOFTWAREPARTFEATURE = 'INSERT INTO softwarepartfeature (part, featuretype, value) SELECT ?, id, ? FROM softwarepartfeaturetype WHERE name = ?'
UPDATE_SOFTWARELIST_NOTES = 'UPDATE softwarelist SET notes = ? WHERE id = ?'
UPDATE_SOFTWARE_NOTES = 'UPDATE software SET notes = ? WHERE id = ?'
# machines
ADD_FEATURETYPE = 'INSERT OR IGNORE INTO featuretype (name) VALUES (?)'
@ -777,7 +787,7 @@ class QueryCursor(object):
def get_machine_softwarelists(self, machine):
return self.dbcurs.execute(
'SELECT machinesoftwarelist.tag AS tag, machinesoftwareliststatustype.value AS status, softwarelist.shortname AS shortname, softwarelist.description AS description, softwarelist.notes AS notes, COUNT(software.id) AS total, COUNT(CASE software.supported WHEN 0 THEN 1 ELSE NULL END) AS supported, COUNT(CASE software.supported WHEN 1 THEN 1 ELSE NULL END) AS partiallysupported, COUNT(CASE software.supported WHEN 2 THEN 1 ELSE NULL END) AS unsupported ' \
'SELECT machinesoftwarelist.tag AS tag, machinesoftwareliststatustype.value AS status, softwarelist.shortname AS shortname, softwarelist.description AS description, COUNT(software.id) AS total, COUNT(CASE software.supported WHEN 0 THEN 1 ELSE NULL END) AS supported, COUNT(CASE software.supported WHEN 1 THEN 1 ELSE NULL END) AS partiallysupported, COUNT(CASE software.supported WHEN 2 THEN 1 ELSE NULL END) AS unsupported ' \
'FROM machinesoftwarelist LEFT JOIN machinesoftwareliststatustype ON machinesoftwarelist.status = machinesoftwareliststatustype.id LEFT JOIN softwarelist ON machinesoftwarelist.softwarelist = softwarelist.id LEFT JOIN software ON softwarelist.id = software.softwarelist ' \
'WHERE machinesoftwarelist.machine = ? ' \
'GROUP BY machinesoftwarelist.id',
@ -789,15 +799,15 @@ class QueryCursor(object):
def get_softwarelist_details(self, shortname, pattern):
if pattern is not None:
return self.dbcurs.execute(
'SELECT softwarelist.id AS id, softwarelist.shortname AS shortname, softwarelist.description AS description, softwarelist.notes AS notes, COUNT(software.id) AS total, COUNT(CASE software.supported WHEN 0 THEN 1 ELSE NULL END) AS supported, COUNT(CASE software.supported WHEN 1 THEN 1 ELSE NULL END) AS partiallysupported, COUNT(CASE software.supported WHEN 2 THEN 1 ELSE NULL END) AS unsupported ' \
'FROM softwarelist LEFT JOIN software ON softwarelist.id = software.softwarelist ' \
'SELECT softwarelist.id AS id, softwarelist.shortname AS shortname, softwarelist.description AS description, softwarelistnotes.notes AS notes, COUNT(software.id) AS total, COUNT(CASE software.supported WHEN 0 THEN 1 ELSE NULL END) AS supported, COUNT(CASE software.supported WHEN 1 THEN 1 ELSE NULL END) AS partiallysupported, COUNT(CASE software.supported WHEN 2 THEN 1 ELSE NULL END) AS unsupported ' \
'FROM softwarelist LEFT JOIN softwarelistnotes ON softwarelist.id = softwarelistnotes.softwarelist LEFT JOIN software ON softwarelist.id = software.softwarelist ' \
'WHERE softwarelist.shortname = ? AND software.shortname GLOB ? ' \
'GROUP BY softwarelist.id',
(shortname, pattern))
else:
return self.dbcurs.execute(
'SELECT softwarelist.id AS id, softwarelist.shortname AS shortname, softwarelist.description AS description, softwarelist.notes AS notes, COUNT(software.id) AS total, COUNT(CASE software.supported WHEN 0 THEN 1 ELSE NULL END) AS supported, COUNT(CASE software.supported WHEN 1 THEN 1 ELSE NULL END) AS partiallysupported, COUNT(CASE software.supported WHEN 2 THEN 1 ELSE NULL END) AS unsupported ' \
'FROM softwarelist LEFT JOIN software ON softwarelist.id = software.softwarelist ' \
'SELECT softwarelist.id AS id, softwarelist.shortname AS shortname, softwarelist.description AS description, softwarelistnotes.notes AS notes, COUNT(software.id) AS total, COUNT(CASE software.supported WHEN 0 THEN 1 ELSE NULL END) AS supported, COUNT(CASE software.supported WHEN 1 THEN 1 ELSE NULL END) AS partiallysupported, COUNT(CASE software.supported WHEN 2 THEN 1 ELSE NULL END) AS unsupported ' \
'FROM softwarelist LEFT JOIN softwarelistnotes ON softwarelist.id = softwarelistnotes.softwarelist LEFT JOIN software ON softwarelist.id = software.softwarelist ' \
'WHERE softwarelist.shortname = ? ' \
'GROUP BY softwarelist.id',
(shortname, ))
@ -840,8 +850,8 @@ class QueryCursor(object):
def get_software_details(self, softwarelist, software):
return self.dbcurs.execute(
'SELECT software.id AS id, software.shortname AS shortname, software.supported AS supported, software.description AS description, software.year AS year, software.publisher AS publisher, software.notes AS notes, softwarelist.shortname AS softwarelist, softwarelist.description AS softwarelistdescription, parent.shortname AS parent, parent.description AS parentdescription, parentsoftwarelist.shortname AS parentsoftwarelist, parentsoftwarelist.description AS parentsoftwarelistdescription ' \
'FROM software LEFT JOIN softwarelist ON software.softwarelist = softwarelist.id LEFT JOIN softwarecloneof ON software.id = softwarecloneof.id LEFT JOIN software AS parent ON softwarecloneof.parent = parent.id LEFT JOIN softwarelist AS parentsoftwarelist ON parent.softwarelist = parentsoftwarelist.id ' \
'SELECT software.id AS id, software.shortname AS shortname, software.supported AS supported, software.description AS description, software.year AS year, software.publisher AS publisher, softwarelist.shortname AS softwarelist, softwarelist.description AS softwarelistdescription, parent.shortname AS parent, parent.description AS parentdescription, parentsoftwarelist.shortname AS parentsoftwarelist, parentsoftwarelist.description AS parentsoftwarelistdescription, softwarenotes.notes AS notes ' \
'FROM software LEFT JOIN softwarelist ON software.softwarelist = softwarelist.id LEFT JOIN softwarecloneof ON software.id = softwarecloneof.id LEFT JOIN software AS parent ON softwarecloneof.parent = parent.id LEFT JOIN softwarelist AS parentsoftwarelist ON parent.softwarelist = parentsoftwarelist.id LEFT JOIN softwarenotes ON softwarenotes.software = software.id ' \
'WHERE software.softwarelist = (SELECT id FROM softwarelist WHERE shortname = ?) AND software.shortname = ?',
(softwarelist, software))
@ -916,8 +926,8 @@ class UpdateCursor(object):
self.dbcurs.execute(UpdateQueries.ADD_SOFTWARELIST, (shortname, description))
return self.dbcurs.lastrowid
def update_softwarelist_notes(self, id, notes):
self.dbcurs.execute(UpdateQueries.UPDATE_SOFTWARELIST_NOTES, (notes, id))
def add_softwarelistnotes(self, softwarelist, notes):
self.dbcurs.execute(UpdateQueries.ADD_SOFTWARELISTNOTES, (softwarelist, notes))
def add_softwareinfotype(self, name):
self.dbcurs.execute(UpdateQueries.ADD_SOFTWAREINFOTYPE, (name, ))
@ -932,13 +942,13 @@ class UpdateCursor(object):
self.dbcurs.execute(UpdateQueries.ADD_SOFTWARE, (softwarelist, shortname, supported, description, year, publisher))
return self.dbcurs.lastrowid
def update_software_notes(self, id, notes):
self.dbcurs.execute(UpdateQueries.UPDATE_SOFTWARE_NOTES, (notes, id))
def add_softwarecloneof(self, software, parent):
self.dbcurs.execute(UpdateQueries.ADD_TEMPORARY_SOFTWARECLONEOF, (software, parent))
return self.dbcurs.lastrowid
def add_softwarenotes(self, software, notes):
self.dbcurs.execute(UpdateQueries.ADD_SOFTWARENOTES, (software, notes))
def add_softwareinfo(self, software, infotype, value):
self.dbcurs.execute(UpdateQueries.ADD_SOFTWAREINFO, (software, value, infotype))
return self.dbcurs.lastrowid

View File

@ -258,10 +258,6 @@ SOFTWARE_PROLOGUE = string.Template(
' <tr><th>Year:</th><td>${year}</td></tr>\n' \
' <tr><th>Publisher:</th><td>${publisher}</td></tr>\n');
SOFTWARE_EPILOGUE = string.Template(
'<h2>Notes</h2>\n' \
'<p><pre>${notes}<p></pre>\n');
SOFTWARE_CLONES_PROLOGUE = string.Template(
'<h2 id="heading-clones">Clones</h2>\n' \
'<table id="tbl-clones">\n' \
@ -285,11 +281,27 @@ SOFTWARE_CLONES_ROW = string.Template(
' <td>${supported}</td>\n' \
' </tr>\n')
SOFTWARE_NOTES_PROLOGUE = string.Template(
'<h2 id="heading-notes">Notes</h2>\n' \
'<div id="div-notes">\n')
SOFTWARE_NOTES_EPILOGUE = string.Template(
'</div>\n' \
'<script>make_collapsible(document.getElementById("heading-notes"), document.getElementById("div-notes"))();</script>\n\n')
SOFTWARE_PARTS_PROLOGUE = string.Template(
'<h2 id="heading-parts">Parts</h2>\n' \
'<div id="div-parts">\n\n')
SOFTWARE_PARTS_EPILOGUE = string.Template(
'</div>\n' \
'<script>make_collapsible(document.getElementById("heading-parts"), document.getElementById("div-parts"));</script>\n\n')
SOFTWARE_PART_PROLOGUE = string.Template(
'<h3>${heading}</h3>\n' \
'<table class="sysinfo">\n' \
' <tr><th>Short name:</th><td>${shortname}</td></tr>\n' \
' <tr><th>Interface:</th><td>${interface}</td></tr>\n')
' <h3>${heading}</h3>\n' \
' <table class="sysinfo">\n' \
' <tr><th>Short name:</th><td>${shortname}</td></tr>\n' \
' <tr><th>Interface:</th><td>${interface}</td></tr>\n')
SOFTWARELIST_PROLOGUE = string.Template(
@ -332,9 +344,13 @@ SOFTWARELIST_PROLOGUE = string.Template(
' </tr>\n' \
'</table>\n')
SOFTWARELIST_EPILOGUE = string.Template(
'<h2>Notes</h2>\n' \
'<p><pre>${notes}<p></pre>\n')
SOFTWARELIST_NOTES_PROLOGUE = string.Template(
'<h2 id="heading-notes">Notes</h2>\n' \
'<div id="div-notes">\n')
SOFTWARELIST_NOTES_EPILOGUE = string.Template(
'</div>\n' \
'<script>make_collapsible(document.getElementById("heading-notes"), document.getElementById("div-notes"))();</script>\n\n')
SOFTWARELIST_MACHINE_TABLE_HEADER = string.Template(
'<h2 id="heading-machines">Machines</h2>\n' \

View File

@ -391,11 +391,14 @@ class SoftwareHandler(ElementHandler):
if self.cloneof is not None:
self.dbcurs.add_softwarecloneof(self.id, self.cloneof)
elif name == 'notes':
self.notes = handler.text
self.dbcurs.update_software_notes(self.id, self.notes)
self.dbcurs.add_softwarenotes(self.id, handler.text)
class SoftwareListHandler(ElementHandler):
CHILD_HANDLERS = {
'notes': TextAccumulator,
'software': SoftwareHandler }
def __init__(self, dbconn, **kwargs):
super(SoftwareListHandler, self).__init__(parent=None, **kwargs)
self.dbconn = dbconn
@ -414,33 +417,28 @@ class SoftwareListHandler(ElementHandler):
locator=self.locator)
self.shortname = attrs['name']
self.description = attrs['description']
self.notes = None
self.entries = 0
dbcurs = self.dbconn.cursor()
self.id = dbcurs.add_softwarelist(self.shortname, self.description)
dbcurs.close()
def endMainElement(self, name):
if self.notes:
dbcurs = self.dbconn.cursor()
dbcurs.update_softwarelist_notes(self.id, self.notes)
dbcurs.close()
self.dbconn.commit()
def startChildElement(self, name, attrs):
if name == 'notes':
self.setChildHandler(name, attrs, TextAccumulator(self))
elif name == 'software':
self.setChildHandler(name, attrs, SoftwareHandler(self))
if name in self.CHILD_HANDLERS:
self.setChildHandler(name, attrs, self.CHILD_HANDLERS[name](self))
else:
raise xml.sax.SAXParseException(
msg=('Expected "software" element but found "%s"' % (name, )),
msg=('Found unexpected element "%s"' % (name, )),
exception=None,
locator=self.locator)
def endChildHandler(self, name, handler):
if name == 'notes':
self.notes = handler.text
dbcurs = self.dbconn.cursor()
dbcurs.add_softwarelistnotes(self.id, handler.text)
dbcurs.close()
elif name == 'software':
if self.entries >= 1023:
self.dbconn.commit()

View File

@ -661,6 +661,20 @@ class SoftwareListHandler(QueryPageHandler):
unsupported=htmlescape('%d' % (softwarelist_info['unsupported'], )),
unsupportedpc=htmlescape('%.1f' % (softwarelist_info['unsupported'] * 100.0 / (softwarelist_info['total'] or 1), ))).encode('utf-8')
if softwarelist_info['notes'] is not None:
yield htmltmpl.SOFTWARELIST_NOTES_PROLOGUE.substitute().encode('utf-8')
first = True
for line in softwarelist_info['notes'].strip().splitlines():
if line:
yield (('<p>%s' if first else '<br />\n%s') % (htmlescape(line), )).encode('utf-8')
first = False
elif not first:
yield '</p>\n'.encode('utf-8')
first = True
if not first:
yield '</p>\n'.encode('utf-8')
yield htmltmpl.SOFTWARELIST_NOTES_EPILOGUE.substitute().encode('utf-8')
first = True
for machine_info in self.dbcurs.get_softwarelist_machines(softwarelist_info['id']):
if first:
@ -689,9 +703,6 @@ class SoftwareListHandler(QueryPageHandler):
yield htmltmpl.SORTABLE_TABLE_EPILOGUE.substitute(id='tbl-software').encode('utf-8')
yield '<script>make_collapsible(document.getElementById("heading-software"), document.getElementById("tbl-software"));</script>\n'.encode('utf-8')
if (softwarelist_info['notes']):
yield htmltmpl.SOFTWARELIST_EPILOGUE.substitute(notes=htmlescape(softwarelist_info['notes'])).encode('utf-8')
yield '</body>\n</html>\n'.encode('utf-8')
def software_page(self, software_info):
@ -722,22 +733,35 @@ class SoftwareListHandler(QueryPageHandler):
yield htmltmpl.SORTABLE_TABLE_EPILOGUE.substitute(id='tbl-clones').encode('utf-8')
yield '<script>make_collapsible(document.getElementById("heading-clones"), document.getElementById("tbl-clones"));</script>\n'.encode('utf-8')
if software_info['notes'] is not None:
yield htmltmpl.SOFTWARE_NOTES_PROLOGUE.substitute().encode('utf-8')
first = True
for line in software_info['notes'].strip().splitlines():
if line:
yield (('<p>%s' if first else '<br />\n%s') % (htmlescape(line), )).encode('utf-8')
first = False
elif not first:
yield '</p>\n'.encode('utf-8')
first = True
if not first:
yield '</p>\n'.encode('utf-8')
yield htmltmpl.SOFTWARE_NOTES_EPILOGUE.substitute().encode('utf-8')
parts = self.dbcurs.get_software_parts(software_info['id']).fetchall()
first = True
for id, partname, interface, part_id in parts:
if first:
yield '<h2>Parts</h2>\n'.encode('utf-8')
yield htmltmpl.SOFTWARE_PARTS_PROLOGUE.substitute().encode('utf-8')
first = False
yield htmltmpl.SOFTWARE_PART_PROLOGUE.substitute(
heading=htmlescape(('%s (%s)' % (part_id, partname)) if part_id is not None else partname),
shortname=htmlescape(partname),
interface=htmlescape(interface)).encode('utf-8')
for name, value in self.dbcurs.get_softwarepart_features(id):
yield (' <tr><th>%s:</th><td>%s</td>\n' % (htmlescape(name), htmlescape(value))).encode('utf-8')
yield '</table>\n\n'.encode('utf-8')
if (software_info['notes']):
yield htmltmpl.SOFTWARE_EPILOGUE.substitute(notes=htmlescape(software_info['notes'])).encode('utf-8')
yield (' <tr><th>%s:</th><td>%s</td>\n' % (htmlescape(name), htmlescape(value))).encode('utf-8')
yield ' </table>\n\n'.encode('utf-8')
if not first:
yield htmltmpl.SOFTWARE_PARTS_EPILOGUE.substitute().encode('utf-8')
yield '</body>\n</html>\n'.encode('utf-8')

View File

@ -252,7 +252,7 @@ debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu
m_console.register_command("bpclear", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_bpclear, this, _1));
m_console.register_command("bpdisable", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_bpdisenable, this, false, _1));
m_console.register_command("bpenable", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_bpdisenable, this, true, _1));
m_console.register_command("bplist", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_bplist, this, _1));
m_console.register_command("bplist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_bplist, this, _1));
m_console.register_command("wpset", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, -1, _1));
m_console.register_command("wp", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, -1, _1));
@ -265,7 +265,7 @@ debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu
m_console.register_command("wpclear", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_wpclear, this, _1));
m_console.register_command("wpdisable", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_wpdisenable, this, false, _1));
m_console.register_command("wpenable", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_wpdisenable, this, true, _1));
m_console.register_command("wplist", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_wplist, this, _1));
m_console.register_command("wplist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_wplist, this, _1));
m_console.register_command("rpset", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_rpset, this, _1));
m_console.register_command("rp", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_rpset, this, _1));
@ -337,8 +337,8 @@ debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu
m_console.register_command("dasm", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_dasm, this, _1));
m_console.register_command("trace", CMDFLAG_NONE, 1, 4, std::bind(&debugger_commands::execute_trace, this, _1));
m_console.register_command("traceover", CMDFLAG_NONE, 1, 4, std::bind(&debugger_commands::execute_traceover, this, _1));
m_console.register_command("trace", CMDFLAG_NONE, 1, 4, std::bind(&debugger_commands::execute_trace, this, _1, false));
m_console.register_command("traceover", CMDFLAG_NONE, 1, 4, std::bind(&debugger_commands::execute_trace, this, _1, true));
m_console.register_command("traceflush",CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_traceflush, this, _1));
m_console.register_command("history", CMDFLAG_NONE, 0, 2, std::bind(&debugger_commands::execute_history, this, _1));
@ -1816,29 +1816,45 @@ void debugger_commands::execute_bplist(const std::vector<std::string> &params)
{
int printed = 0;
std::string buffer;
/* loop over all CPUs */
for (device_t &device : device_enumerator(m_machine.root_device()))
if (!device.debug()->breakpoint_list().empty())
{
m_console.printf("Device '%s' breakpoints:\n", device.tag());
/* loop over the breakpoints */
for (const auto &bpp : device.debug()->breakpoint_list())
auto const apply =
[this, &printed, &buffer] (device_t &device)
{
debug_breakpoint &bp = *bpp.second;
buffer = string_format("%c%4X @ %0*X", bp.enabled() ? ' ' : 'D', bp.index(), device.debug()->logaddrchars(), bp.address());
if (std::string(bp.condition()).compare("1") != 0)
buffer.append(string_format(" if %s", bp.condition()));
if (std::string(bp.action()).compare("") != 0)
buffer.append(string_format(" do %s", bp.action()));
m_console.printf("%s\n", buffer);
printed++;
}
}
if (!device.debug()->breakpoint_list().empty())
{
m_console.printf("Device '%s' breakpoints:\n", device.tag());
if (printed == 0)
m_console.printf("No breakpoints currently installed\n");
// loop over the breakpoints
for (const auto &bpp : device.debug()->breakpoint_list())
{
debug_breakpoint &bp = *bpp.second;
buffer = string_format("%c%4X @ %0*X", bp.enabled() ? ' ' : 'D', bp.index(), device.debug()->logaddrchars(), bp.address());
if (std::string(bp.condition()).compare("1") != 0)
buffer.append(string_format(" if %s", bp.condition()));
if (std::string(bp.action()).compare("") != 0)
buffer.append(string_format(" do %s", bp.action()));
m_console.printf("%s\n", buffer);
printed++;
}
}
};
if (!params.empty())
{
device_t *cpu;
if (!validate_cpu_parameter(params[0], cpu))
return;
apply(*cpu);
if (!printed)
m_console.printf("No breakpoints currently installed for CPU %s\n", cpu->tag());
}
else
{
// loop over all CPUs
for (device_t &device : device_enumerator(m_machine.root_device()))
apply(device);
if (!printed)
m_console.printf("No breakpoints currently installed\n");
}
}
@ -1977,35 +1993,57 @@ void debugger_commands::execute_wplist(const std::vector<std::string> &params)
{
int printed = 0;
std::string buffer;
/* loop over all CPUs */
for (device_t &device : device_enumerator(m_machine.root_device()))
for (int spacenum = 0; spacenum < device.debug()->watchpoint_space_count(); ++spacenum)
if (!device.debug()->watchpoint_vector(spacenum).empty())
auto const apply =
[this, &printed, &buffer] (device_t &device)
{
static const char *const types[] = { "unkn ", "read ", "write", "r/w " };
m_console.printf("Device '%s' %s space watchpoints:\n", device.tag(),
device.debug()->watchpoint_vector(spacenum).front()->space().name());
/* loop over the watchpoints */
for (const auto &wp : device.debug()->watchpoint_vector(spacenum))
for (int spacenum = 0; spacenum < device.debug()->watchpoint_space_count(); ++spacenum)
{
buffer = string_format("%c%4X @ %0*X-%0*X %s", wp->enabled() ? ' ' : 'D', wp->index(),
wp->space().addrchars(), wp->address(),
wp->space().addrchars(), wp->address() + wp->length() - 1,
types[int(wp->type())]);
if (std::string(wp->condition()).compare("1") != 0)
buffer.append(string_format(" if %s", wp->condition()));
if (std::string(wp->action()).compare("") != 0)
buffer.append(string_format(" do %s", wp->action()));
m_console.printf("%s\n", buffer);
printed++;
}
}
if (!device.debug()->watchpoint_vector(spacenum).empty())
{
static const char *const types[] = { "unkn ", "read ", "write", "r/w " };
if (printed == 0)
m_console.printf("No watchpoints currently installed\n");
m_console.printf(
"Device '%s' %s space watchpoints:\n",
device.tag(),
device.debug()->watchpoint_vector(spacenum).front()->space().name());
// loop over the watchpoints
for (const auto &wp : device.debug()->watchpoint_vector(spacenum))
{
buffer = string_format(
"%c%4X @ %0*X-%0*X %s",
wp->enabled() ? ' ' : 'D', wp->index(),
wp->space().addrchars(), wp->address(),
wp->space().addrchars(), wp->address() + wp->length() - 1,
types[int(wp->type())]);
if (std::string(wp->condition()).compare("1") != 0)
buffer.append(string_format(" if %s", wp->condition()));
if (std::string(wp->action()).compare("") != 0)
buffer.append(string_format(" do %s", wp->action()));
m_console.printf("%s\n", buffer);
printed++;
}
}
}
};
if (!params.empty())
{
device_t *cpu;
if (!validate_cpu_parameter(params[0], cpu))
return;
apply(*cpu);
if (!printed)
m_console.printf("No watchpoints currently installed for CPU %s\n", cpu->tag());
}
else
{
// loop over all CPUs
for (device_t &device : device_enumerator(m_machine.root_device()))
apply(device);
if (!printed)
m_console.printf("No watchpoints currently installed\n");
}
}
@ -3576,11 +3614,11 @@ void debugger_commands::execute_dasm(const std::vector<std::string> &params)
/*-------------------------------------------------
execute_trace_internal - functionality for
execute_trace - functionality for
trace over and trace info
-------------------------------------------------*/
void debugger_commands::execute_trace_internal(const std::vector<std::string> &params, bool trace_over)
void debugger_commands::execute_trace(const std::vector<std::string> &params, bool trace_over)
{
const char *action = nullptr;
bool detect_loops = true;
@ -3647,26 +3685,6 @@ void debugger_commands::execute_trace_internal(const std::vector<std::string> &p
}
/*-------------------------------------------------
execute_trace - execute the trace command
-------------------------------------------------*/
void debugger_commands::execute_trace(const std::vector<std::string> &params)
{
execute_trace_internal(params, false);
}
/*-------------------------------------------------
execute_traceover - execute the trace over command
-------------------------------------------------*/
void debugger_commands::execute_traceover(const std::vector<std::string> &params)
{
execute_trace_internal(params, true);
}
/*-------------------------------------------------
execute_traceflush - execute the trace flush command
-------------------------------------------------*/

View File

@ -100,8 +100,6 @@ private:
int mini_printf(char *buffer, const char *format, int params, u64 *param);
void execute_trace_internal(const std::vector<std::string> &params, bool trace_over);
void execute_help(const std::vector<std::string> &params);
void execute_print(const std::vector<std::string> &params);
void execute_printf(const std::vector<std::string> &params);
@ -160,8 +158,7 @@ private:
void execute_dasm(const std::vector<std::string> &params);
void execute_find(int spacenum, const std::vector<std::string> &params);
void execute_fill(int spacenum, const std::vector<std::string> &params);
void execute_trace(const std::vector<std::string> &params);
void execute_traceover(const std::vector<std::string> &params);
void execute_trace(const std::vector<std::string> &params, bool trace_over);
void execute_traceflush(const std::vector<std::string> &params);
void execute_history(const std::vector<std::string> &params);
void execute_trackpc(const std::vector<std::string> &params);

View File

@ -157,7 +157,7 @@ const help_item f_static_help_list[] =
" bpclear [<bpnum>] -- clears a given breakpoint or all if no <bpnum> specified\n"
" bpdisable [<bpnum>] -- disables a given breakpoint or all if no <bpnum> specified\n"
" bpenable [<bpnum>] -- enables a given breakpoint or all if no <bpnum> specified\n"
" bplist -- lists all the breakpoints\n"
" bplist [<CPU>] -- lists all the breakpoints\n"
},
{
"watchpoints",
@ -172,7 +172,7 @@ const help_item f_static_help_list[] =
" wpclear [<wpnum>] -- clears a given watchpoint or all if no <wpnum> specified\n"
" wpdisable [<wpnum>] -- disables a given watchpoint or all if no <wpnum> specified\n"
" wpenable [<wpnum>] -- enables a given watchpoint or all if no <wpnum> specified\n"
" wplist -- lists all the watchpoints\n"
" wplist [<CPU>] -- lists all the watchpoints\n"
},
{
"registerpoints",
@ -896,7 +896,7 @@ const help_item f_static_help_list[] =
"Note that the step out functionality may not be implemented on all CPU types. If it is not "
"implemented, then 'out' will behave exactly like 'step'.\n"
"\n"
"Examples:\n"
"Example:\n"
"\n"
"out\n"
" Steps until the current subroutine or exception handler returns.\n"
@ -920,35 +920,45 @@ const help_item f_static_help_list[] =
" Resume execution, stopping at address 1234 unless something else stops us first.\n"
},
{
"gvblank",
"gex",
"\n"
" gv[blank]\n"
" ge[x] [<exception>,[<condition>]]\n"
"\n"
"The gvblank command resumes execution of the current code. Control will not be returned to "
"the debugger until a breakpoint or watchpoint is hit, or until the next VBLANK occurs in the "
"emulator.\n"
"The gex command resumes execution of the current code. Control will not be returned to "
"the debugger until a breakpoint or watchpoint is hit, or until an exception condition "
"is raised on the current CPU. You can specify <exception> if you wish to stop execution "
"only on a particular exception condition occurring. If <exception> is omitted, then any "
"exception condition will stop execution. The optional <condition> parameter lets you "
"specify an expression that will be evaluated each time the specified exception condition "
"is raised. If the result of the expression is true (non-zero), the exception will halt "
"execution; otherwise, execution will continue with no notification.\n"
"\n"
"Examples:\n"
"\n"
"gv\n"
" Resume execution until the next break/watchpoint or until the next VBLANK.\n"
"gex\n"
" Resume execution until the next break/watchpoint or until any exception condition is "
"raised on the current CPU.\n"
"\n"
"ge 2\n"
" Resume execution until the next break/watchpoint or until exception condition 2 is "
"raised on the current CPU.\n"
},
{
"gint",
"\n"
" gi[nt] [<irqline>]\n"
"\n"
"The gint command resumes execution of the current code. Control will not be returned to the "
"debugger until a breakpoint or watchpoint is hit, or until an IRQ is asserted and acknowledged "
"on the current CPU. You can specify <irqline> if you wish to stop execution only on a particular "
"IRQ line being asserted and acknowledged. If <irqline> is omitted, then any IRQ line will stop "
"execution.\n"
"The gint command resumes execution of the current code. Control will not be returned to "
"the debugger until a breakpoint or watchpoint is hit, or until an IRQ is asserted and "
"acknowledged on the current CPU. You can specify <irqline> if you wish to stop execution "
"only on a particular IRQ line being asserted and acknowledged. If <irqline> is omitted, "
"then any IRQ line will stop execution.\n"
"\n"
"Examples:\n"
"\n"
"gi\n"
" Resume execution until the next break/watchpoint or until any IRQ is asserted and acknowledged "
"on the current CPU.\n"
" Resume execution until the next break/watchpoint or until any IRQ is asserted and "
"acknowledged on the current CPU.\n"
"\n"
"gint 4\n"
" Resume execution until the next break/watchpoint or until IRQ line 4 is asserted and "
@ -967,14 +977,33 @@ const help_item f_static_help_list[] =
"gtime #10000\n"
" Resume execution for ten seconds\n"
},
{
"gvblank",
"\n"
" gv[blank]\n"
"\n"
"The gvblank command resumes execution of the current code. Control will not be returned to "
"the debugger until a breakpoint or watchpoint is hit, or until the beginning of the "
" vertical blanking interval for an emulated screen.\n"
"\n"
"Example:\n"
"\n"
"gv\n"
" Resume execution until the next break/watchpoint or until the next VBLANK.\n"
},
{
"next",
"\n"
" n[ext]\n"
"\n"
"The next command resumes execution and continues executing until the next time a different "
"CPU is scheduled. Note that if you have used 'ignore' to ignore certain CPUs, you will not "
"stop until a non-'ignore'd CPU is scheduled.\n"
"CPU is scheduled. Note that if you have used 'focus' or 'ignore' to ignore certain CPUs, "
"execution will continue until a non-'ignore'd CPU is scheduled.\n"
"\n"
"Example:\n"
"\n"
"n\n"
" Resume execution, stopping when a different CPU that is not ignored is scheduled.\n"
},
{
"focus",
@ -988,21 +1017,25 @@ const help_item f_static_help_list[] =
"\n"
"focus 1\n"
" Focus exclusively CPU #1 while ignoring all other CPUs when using the debugger.\n"
"\n"
"focus audiopcb:melodycpu\n"
" Focus exclusively on the CPU ':audiopcb:melodycpu'.\n"
},
{
"ignore",
"\n"
" ignore [<CPU>[,<CPU>[,...]]]\n"
"\n"
"Ignores the specified <CPU> in the debugger. This means that you won't ever see execution "
"on that CPU, nor will you be able to set breakpoints on that CPU. To undo this change use "
"the 'observe' command. You can specify multiple <CPU>s in a single command. Note also that "
"you are not permitted to ignore all CPUs; at least one must be active at all times.\n"
"Ignores the specified CPUs in the debugger. CPUs can be specified by tag or debugger CPU "
"number. The debugger never shows execution for ignored CPUs, and breakpoints or "
"watchpoints on ignored CPUs have no effect. If no CPUs are specified, currently ignored "
"CPUs will be listed. Use the 'observe' command to stop ignoring a CPU. Note that you "
"cannot ignore all CPUs; at least CPU must be observed at all times.\n"
"\n"
"Examples:\n"
"\n"
"ignore 1\n"
" Ignore CPU #1 when using the debugger.\n"
"ignore audiocpu\n"
" Ignore the CPU ':audiocpu' when using the debugger.\n"
"\n"
"ignore 2,3,4\n"
" Ignore CPU #2, #3 and #4 when using the debugger.\n"
@ -1020,8 +1053,8 @@ const help_item f_static_help_list[] =
"\n"
"Examples:\n"
"\n"
"observe 1\n"
" Stop ignoring CPU #1 when using the debugger.\n"
"observe audiocpu\n"
" Stop ignoring the CPU ':audiocpu' when using the debugger.\n"
"\n"
"observe 2,3,4\n"
" Stop ignoring CPU #2, #3 and #4 when using the debugger.\n"
@ -1032,91 +1065,101 @@ const help_item f_static_help_list[] =
{
"trace",
"\n"
" trace {<filename>|OFF}[,<CPU>[,[noloop|logerror][,<action>]]]\n"
" trace {<filename>|off}[,<CPU>[,[noloop|logerror][,<action>]]]\n"
"\n"
"Starts or stops tracing of the execution of the specified <CPU>. If <CPU> is omitted, "
"the currently active CPU is specified. When enabling tracing, specify the filename in the "
"<filename> parameter. To disable tracing, substitute the keyword 'off' for <filename>. "
"<detectloops> should be either true or false. If 'noloop' is omitted, the trace "
"will have loops detected and condensed to a single line. If 'noloop' is specified, the trace "
"will contain every opcode as it is executed. If 'logerror' is specified, logerror output "
"will augment the trace. If you "
"wish to log additional information on each trace, you can append an <action> parameter which "
"is a command that is executed before each trace is logged. Generally, this is used to include "
"a 'tracelog' command. Note that you may need to embed the action within braces { } in order "
"to prevent commas and semicolons from being interpreted as applying to the trace command "
"itself.\n"
"Starts or stops tracing of the execution of the specified <CPU>, or the currently visible "
"CPU if no CPU is specified. To enable tracing, specify the trace log file name in the "
"<filename> parameter. To disable tracing, use the keyword 'off' for <filename> "
"parameter. If the **<filename>** begins with two right angle brackets (>>), it is treated "
"as a directive to open the file for appending rather than overwriting.\n"
"\n"
"Examples:\n"
"The optional third parameter is a flags field. The supported flags are 'noloop' and "
"'logerror'. Multiple flags must be separated by | (pipe) characters. By default, loops "
"are detected and condensed to a single line. If the 'noloop' flag is specified, loops "
"will not be detected and every instruction will be logged as executed. If the 'logerror' "
"flag is specified, error log output will be included in the trace log.\n"
"\n"
"trace joust.tr\n"
" Begin tracing the currently active CPU, logging output to joust.tr.\n"
"\n"
"trace dribling.tr,0\n"
" Begin tracing the execution of CPU #0, logging output to dribling.tr.\n"
"\n"
"trace starswep.tr,0,noloop\n"
" Begin tracing the execution of CPU #0, logging output to starswep.tr, with loop detection disabled.\n"
"\n"
"trace starswep.tr,0,logerror\n"
" Begin tracing the execution of CPU #0, logging output (along with logerror output) to starswep.tr.\n"
"\n"
"trace starswep.tr,0,logerror|noloop\n"
" Begin tracing the execution of CPU #0, logging output (along with logerror output) to starswep.tr, with loop detection disabled.\n"
"\n"
"trace >>pigskin.tr\n"
" Begin tracing the currently active CPU, appending log output to pigskin.tr.\n"
"\n"
"trace off,0\n"
" Turn off tracing on CPU #0.\n"
"\n"
"trace asteroid.tr,0,,{tracelog \"A=%02X \",a}\n"
" Begin tracing the execution of CPU #0, logging output to asteroid.tr. Before each line, "
"output A=<aval> to the tracelog.\n"
},
{
"traceover",
"\n"
" traceover {<filename>|OFF}[,<CPU>[,<detectloops>[,<action>]]]\n"
"\n"
"Starts or stops tracing of the execution of the specified <CPU>. When tracing reaches "
"a subroutine or call, tracing will skip over the subroutine. The same algorithm is used as is "
"used in the step over command. This means that traceover will not work properly when calls "
"are recusive or the return address is not immediately following the call instruction. If "
"<detectloops> should be either true or false. If <detectloops> is true or omitted, the trace "
"will have loops detected and condensed to a single line. If it is false, the trace will contain "
"every opcode as it is executed. If <CPU> is omitted, the currently active CPU is specified. When "
"enabling tracing, specify the filename in the <filename> parameter. To disable tracing, substitute "
"the keyword 'off' for <filename>. If you wish to log additional information on each trace, you can "
"append an <action> parameter which is a command that is executed before each trace is logged. "
"Generally, this is used to include a 'tracelog' command. Note that you may need to embed the "
"action within braces { } in order to prevent commas and semicolons from being interpreted as "
"The optional <action> parameter is a debugger command to execute before each trace message "
"is logged. Generally, this will include a 'tracelog' or 'tracesym' command to include "
"additional information in the trace log. Note that you may need to embed the action "
"within braces { } in order to prevent commas and semicolons from being interpreted as "
"applying to the trace command itself.\n"
"\n"
"Examples:\n"
"\n"
"trace joust.tr\n"
" Begin tracing the execution of the currently visible CPU, logging output to joust.tr.\n"
"\n"
"trace dribling.tr,maincpu\n"
" Begin tracing the execution of the CPU ':maincpu', logging output to dribling.tr.\n"
"\n"
"trace starswep.tr,,noloop\n"
" Begin tracing the execution of the currently visible CPU, logging output to starswep.tr, "
"with loop detection disabled.\n"
"\n"
"trace starswep.tr,1,logerror\n"
" Begin tracing the execution of CPU #1, logging output (along with logerror output) to "
"starswep.tr.\n"
"\n"
"trace starswep.tr,0,logerror|noloop\n"
" Begin tracing the execution of CPU #0, logging output (along with logerror output) to "
"starswep.tr, with loop detection disabled.\n"
"\n"
"trace >>pigskin.tr\n"
" Begin tracing execution of the currently visible CPU, appending log output to "
"pigskin.tr.\n"
"\n"
"trace off,0\n"
" Turn off tracing on CPU #0.\n"
"\n"
"trace asteroid.tr,,,{tracelog \"A=%02X \",a}\n"
" Begin tracing the execution of the currently visible CPU, logging output to asteroid.tr. "
"Before each line, output A=<aval> to the trace log.\n"
},
{
"traceover",
"\n"
" traceover {<filename>|off}[,<CPU>[,[noloop|logerror][,<action>]]]\n"
"\n"
"Starts or stops tracing for execution of the specified **<CPU>**, or the currently visible "
"CPU if no CPU is specified. When a subroutine call is encountered, tracing will skip over "
"the subroutine. The same algorithm is used as is used in the step over command. It will "
"not work properly with recursive functions, or if the return address does not immediately "
"follow the call instruction.\n"
"\n"
"This command accepts the same parameters as the 'trace' command. Please refer to the "
"corresponding section for a detailed description of options and more examples.\n"
"\n"
"Examples:\n"
"\n"
"traceover joust.tr\n"
" Begin tracing the currently active CPU, logging output to joust.tr.\n"
" Begin tracing the execution of the currently visible CPU, logging output to joust.tr.\n"
"\n"
"traceover dribling.tr,0\n"
" Begin tracing the execution of CPU #0, logging output to dribling.tr.\n"
"traceover dribling.tr,maincpu\n"
" Begin tracing the execution of the CPU ':maincpu', logging output to dribling.tr.\n"
"\n"
"traceover starswep.tr,0,false\n"
" Begin tracing the execution of CPU #0, logging output to starswep.tr, with loop detection disabled.\n"
"traceover starswep.tr,,noloop\n"
" Begin tracing the execution of the currently visible CPU, logging output to starswep.tr, "
"with loop detection disabled.\n"
"\n"
"traceover off,0\n"
" Turn off tracing on CPU #0.\n"
"\n"
"traceover asteroid.tr,0,true,{tracelog \"A=%02X \",a}\n"
" Begin tracing the execution of CPU #0, logging output to asteroid.tr. Before each line, "
"output A=<aval> to the tracelog.\n"
"traceover asteroid.tr,,,{tracelog \"A=%02X \",a}\n"
" Begin tracing the execution of the currently visible CPU, logging output to "
"asteroid.tr. Before each line, output A=<aval> to the trace log.\n"
},
{
"traceflush",
"\n"
" traceflush\n"
"\n"
"Flushes all open trace files.\n"
"Flushes all open trace log files to disk.\n"
"\n"
"Example:\n"
"\n"
"traceflush\n"
" Flush trace log files.\n"
},
{
"bpset",
@ -1124,15 +1167,15 @@ const help_item f_static_help_list[] =
" bp[set] <address>[:<CPU>][,<condition>[,<action>]]\n"
"\n"
"Sets a new execution breakpoint at the specified <address>. The <address> may optionally "
"be followed by a colon and the tag or debuggers CPU number to specify a CPU explicitly. "
"If no CPU is specified, the CPU currently visible in the debugger is assumed. The "
"optional <condition> parameter lets you specify an expression that will be evaluated each "
"time the breakpoint is hit. If the result of the expression is true (non-zero), the "
"breakpoint will halt execution; otherwise, execution will continue with no notification. "
"The optional <action> parameter provides a command that is executed whenever the "
"breakpoint is hit and the <condition> is true. Note that you may need to embed the action "
"within braces { } in order to prevent commas and semicolons from being interpreted as "
"applying to the 'bpset' command itself.\n"
"be followed by a colon and a tag or debugger CPU number to specify a CPU explicitly. If "
"no CPU is specified, the CPU currently visible in the debugger is assumed. The optional "
"<condition> parameter lets you specify an expression that will be evaluated each time the "
"breakpoint is hit. If the result of the expression is true (non-zero), the breakpoint "
"will halt execution; otherwise, execution will continue with no notification. The "
"optional <action> parameter provides a command that is executed whenever the breakpoint is "
"hit and the <condition> is true. Note that you may need to embed the action within braces "
"{ } in order to prevent commas and semicolons from being interpreted as applying to the "
"'bpset' command itself.\n"
"\n"
"Each breakpoint that is set is assigned an index which can be used to refer to it in other "
"breakpoint commands.\n"
@ -1212,10 +1255,23 @@ const help_item f_static_help_list[] =
{
"bplist",
"\n"
" bplist\n"
" bplist [<CPU>]\n"
"\n"
"The bplist command lists all the current breakpoints, along with their indices and any "
"conditions or actions attached to them.\n"
"The bplist list current breakpoints, along with their indices and any associated "
"conditions or actions. If no <CPU> is specified, breakpoints for all CPUs in the system "
"will be listed; if a <CPU> is specified, only breakpoints for that CPU will be listed. "
"The <CPU> can be specified by tag or by debugger CPU number.\n"
"\n"
"Examples:\n"
"\n"
"bplist\n"
" List all breakpoints.\n"
"\n"
"bplist .\n"
" List all breakpoints for the visible CPU.\n"
"\n"
"bplist maincpu\n"
" List all breakpoints for the CPU ':maincpu'.\n"
},
{
"wpset",
@ -1230,8 +1286,8 @@ const help_item f_static_help_list[] =
"suffix sets the address space: 'wpset' defaults to the first address space exposed by the "
"CPU, 'wpdset' defaults to the data space, 'wpiset' defaults to the I/O space, and 'wposet' "
"defaults to the opcodes space. The <type> parameter specifies the access types to trap "
"on - it can be one of three values: 'r' for read accesses, 'w' for write accesses, and "
"'rw' for both read and write accesses.\n"
"on - it can be one of three values: 'r' for read accesses, 'w' for write accesses, or 'rw' "
"for both read and write accesses.\n"
"\n"
"The optional <condition> parameter lets you specify an expression that will be evaluated "
"each time the watchpoint is triggered. If the result of the expression is true "
@ -1246,7 +1302,7 @@ const help_item f_static_help_list[] =
"\n"
"To make <condition> expressions more useful, two variables are available: for all "
"watchpoints, the variable 'wpaddr' is set to the access address that triggered the "
"watchpoint; for write watchpoints, the variable 'wpdata' is set to the data that is being "
"watchpoint; for write watchpoints, the variable 'wpdata' is set to the data being "
"written.\n"
"\n"
"Examples:\n"
@ -1324,10 +1380,23 @@ const help_item f_static_help_list[] =
{
"wplist",
"\n"
" wplist\n"
" wplist [<CPU>]\n"
"\n"
"The wplist command lists all the current watchpoints, along with their indices and any "
"conditions or actions attached to them.\n"
"The wplist list current watchpoints, along with their indices and any associated "
"conditions or actions. If no <CPU> is specified, watchpoints for all CPUs in the system "
"will be listed; if a <CPU> is specified, only watchpoints for that CPU will be listed. "
"The <CPU> can be specified by tag or by debugger CPU number.\n"
"\n"
"Examples:\n"
"\n"
"wplist\n"
" List all watchpoints.\n"
"\n"
"wplist .\n"
" List all watchpoints for the visible CPU.\n"
"\n"
"wplist maincpu\n"
" List all watchpoints for the CPU ':maincpu'.\n"
},
{
"rpset",

View File

@ -46,6 +46,7 @@
#include "logmacro.h"
namespace {
/***************************************************************************
CONSTANTS
@ -103,7 +104,7 @@ enum
//**************************************************************************
// TYPE DEFINITIONS
// REGISTER SYMBOL ENTRY
//**************************************************************************
// a symbol entry representing a register, with read/write callbacks
@ -116,8 +117,8 @@ public:
integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format);
// symbol access
virtual bool is_lval() const override;
virtual u64 value() const override;
virtual bool is_lval() const override { return m_setter != nullptr; }
virtual u64 value() const override { return m_getter(); }
virtual void set_value(u64 newvalue) override;
private:
@ -128,6 +129,61 @@ private:
};
//-------------------------------------------------
// integer_symbol_entry - constructor
//-------------------------------------------------
integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::read_write rw, u64 *ptr)
: symbol_entry(table, SMT_INTEGER, name, ""),
m_getter(ptr
? symbol_table::getter_func([ptr] () { return *ptr; })
: symbol_table::getter_func([this] () { return m_value; })),
m_setter((rw == symbol_table::READ_ONLY)
? symbol_table::setter_func(nullptr)
: ptr
? symbol_table::setter_func([ptr] (u64 value) { *ptr = value; })
: symbol_table::setter_func([this] (u64 value) { m_value = value; })),
m_value(0)
{
}
integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, u64 constval)
: symbol_entry(table, SMT_INTEGER, name, ""),
m_getter([this] () { return m_value; }),
m_setter(nullptr),
m_value(constval)
{
}
integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format)
: symbol_entry(table, SMT_INTEGER, name, format),
m_getter(std::move(getter)),
m_setter(std::move(setter)),
m_value(0)
{
}
//-------------------------------------------------
// set_value - set the value of this symbol
//-------------------------------------------------
void integer_symbol_entry::set_value(u64 newvalue)
{
if (m_setter != nullptr)
m_setter(newvalue);
else
throw emu_fatalerror("Symbol '%s' is read-only", m_name);
}
//**************************************************************************
// FUNCTION SYMBOL ENTRY
//**************************************************************************
// a symbol entry representing a function
class function_symbol_entry : public symbol_entry
{
@ -140,7 +196,7 @@ public:
u16 maxparams() const { return m_maxparams; }
// symbol access
virtual bool is_lval() const override;
virtual bool is_lval() const override { return false; }
virtual u64 value() const override;
virtual void set_value(u64 newvalue) override;
@ -155,6 +211,82 @@ private:
};
//-------------------------------------------------
// function_symbol_entry - constructor
//-------------------------------------------------
function_symbol_entry::function_symbol_entry(symbol_table &table, const char *name, int minparams, int maxparams, symbol_table::execute_func execute)
: symbol_entry(table, SMT_FUNCTION, name, ""),
m_minparams(minparams),
m_maxparams(maxparams),
m_execute(std::move(execute))
{
}
//-------------------------------------------------
// value - return the value of this symbol
//-------------------------------------------------
u64 function_symbol_entry::value() const
{
throw emu_fatalerror("Symbol '%s' is a function and cannot be used in this context", m_name);
}
//-------------------------------------------------
// set_value - set the value of this symbol
//-------------------------------------------------
void function_symbol_entry::set_value(u64 newvalue)
{
throw emu_fatalerror("Symbol '%s' is a function and cannot be written", m_name);
}
//-------------------------------------------------
// execute - execute the function
//-------------------------------------------------
u64 function_symbol_entry::execute(int numparams, const u64 *paramlist)
{
if (numparams < m_minparams)
throw emu_fatalerror("Function '%s' requires at least %d parameters", m_name, m_minparams);
if (numparams > m_maxparams)
throw emu_fatalerror("Function '%s' accepts no more than %d parameters", m_name, m_maxparams);
return m_execute(numparams, paramlist);
}
/***************************************************************************
INLINE FUNCTIONS
***************************************************************************/
inline std::pair<device_t &, char const *> get_device_search(running_machine &machine, device_memory_interface *memintf, char const *tag)
{
if (tag)
{
if (('.' == tag[0]) && (!tag[1] || (':' == tag[1]) || ('^' == tag[1])))
return std::pair<device_t &, char const *>(memintf ? memintf->device() : machine.root_device(), tag + ((':' == tag[1]) ? 2 : 1));
else if (('^' == tag[0]) && memintf)
return std::pair<device_t &, char const *>(memintf->device(), tag);
else
return std::pair<device_t &, char const *>(machine.root_device(), tag);
}
else if (memintf)
{
return std::pair<device_t &, char const *>(memintf->device(), "");
}
else
{
return std::pair<device_t &, char const *>(machine.root_device(), "");
}
}
} // anonymous namespace
//**************************************************************************
// EXPRESSION ERROR
@ -223,143 +355,6 @@ symbol_entry::~symbol_entry()
//**************************************************************************
// REGISTER SYMBOL ENTRY
//**************************************************************************
//-------------------------------------------------
// integer_symbol_entry - constructor
//-------------------------------------------------
integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::read_write rw, u64 *ptr)
: symbol_entry(table, SMT_INTEGER, name, ""),
m_getter(ptr
? symbol_table::getter_func([ptr] () { return *ptr; })
: symbol_table::getter_func([this] () { return m_value; })),
m_setter((rw == symbol_table::READ_ONLY)
? symbol_table::setter_func(nullptr)
: ptr
? symbol_table::setter_func([ptr] (u64 value) { *ptr = value; })
: symbol_table::setter_func([this] (u64 value) { m_value = value; })),
m_value(0)
{
}
integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, u64 constval)
: symbol_entry(table, SMT_INTEGER, name, ""),
m_getter([this] () { return m_value; }),
m_setter(nullptr),
m_value(constval)
{
}
integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format)
: symbol_entry(table, SMT_INTEGER, name, format),
m_getter(std::move(getter)),
m_setter(std::move(setter)),
m_value(0)
{
}
//-------------------------------------------------
// is_lval - is this symbol allowable as an lval?
//-------------------------------------------------
bool integer_symbol_entry::is_lval() const
{
return (m_setter != nullptr);
}
//-------------------------------------------------
// value - return the value of this symbol
//-------------------------------------------------
u64 integer_symbol_entry::value() const
{
return m_getter();
}
//-------------------------------------------------
// set_value - set the value of this symbol
//-------------------------------------------------
void integer_symbol_entry::set_value(u64 newvalue)
{
if (m_setter != nullptr)
m_setter(newvalue);
else
throw emu_fatalerror("Symbol '%s' is read-only", m_name);
}
//**************************************************************************
// FUNCTION SYMBOL ENTRY
//**************************************************************************
//-------------------------------------------------
// function_symbol_entry - constructor
//-------------------------------------------------
function_symbol_entry::function_symbol_entry(symbol_table &table, const char *name, int minparams, int maxparams, symbol_table::execute_func execute)
: symbol_entry(table, SMT_FUNCTION, name, ""),
m_minparams(minparams),
m_maxparams(maxparams),
m_execute(std::move(execute))
{
}
//-------------------------------------------------
// is_lval - is this symbol allowable as an lval?
//-------------------------------------------------
bool function_symbol_entry::is_lval() const
{
return false;
}
//-------------------------------------------------
// value - return the value of this symbol
//-------------------------------------------------
u64 function_symbol_entry::value() const
{
throw emu_fatalerror("Symbol '%s' is a function and cannot be used in this context", m_name);
}
//-------------------------------------------------
// set_value - set the value of this symbol
//-------------------------------------------------
void function_symbol_entry::set_value(u64 newvalue)
{
throw emu_fatalerror("Symbol '%s' is a function and cannot be written", m_name);
}
//-------------------------------------------------
// execute - execute the function
//-------------------------------------------------
u64 function_symbol_entry::execute(int numparams, const u64 *paramlist)
{
if (numparams < m_minparams)
throw emu_fatalerror("Function '%s' requires at least %d parameters", m_name, m_minparams);
if (numparams > m_maxparams)
throw emu_fatalerror("Function '%s' accepts no more than %d parameters", m_name, m_maxparams);
return m_execute(numparams, paramlist);
}
//**************************************************************************
// SYMBOL TABLE
//**************************************************************************
@ -551,17 +546,13 @@ expression_error symbol_table::expression_get_space(const char *tag, int &spacen
if (tag)
{
// convert to lowercase then lookup the name (tags are enforced to be all lower case)
device_t *base;
if ((('^' == tag[0]) || (('.' == tag[0]) && ((':' == tag[1]) || !tag[1]))) && m_memintf)
base = &m_memintf->device();
else
base = &m_machine.root_device();
device = base->subdevice(strmakelower(tag));
auto base = get_device_search(m_machine, m_memintf, tag);
device = base.first.subdevice(strmakelower(base.second));
// if that failed, treat the last component as an address space
if (!device)
{
std::string_view t = tag;
std::string_view t = base.second;
auto const delimiter = t.find_last_of(":^");
bool const found = std::string_view::npos != delimiter;
if (!found || (':' == t[delimiter]))
@ -569,7 +560,7 @@ expression_error symbol_table::expression_get_space(const char *tag, int &spacen
spacename = strmakelower(t.substr(found ? (delimiter + 1) : 0));
t = t.substr(0, !found ? 0 : !delimiter ? 1 : delimiter);
if (!t.empty())
device = base->subdevice(strmakelower(t));
device = base.first.subdevice(strmakelower(t));
else
device = m_memintf ? &m_memintf->device() : &m_machine.root_device();
}
@ -740,11 +731,12 @@ u64 symbol_table::read_program_direct(address_space &space, int opcode, offs_t a
u64 symbol_table::read_memory_region(const char *rgntag, offs_t address, int size)
{
memory_region *region = m_machine.root_device().memregion(rgntag);
auto search = get_device_search(m_machine, m_memintf, rgntag);
memory_region *const region = search.first.memregion(search.second);
u64 result = ~u64(0) >> (64 - 8*size);
// make sure we get a valid base before proceeding
if (region != nullptr)
if (region)
{
// call ourself recursively until we are byte-sized
if (size > 1)
@ -900,10 +892,11 @@ void symbol_table::write_program_direct(address_space &space, int opcode, offs_t
void symbol_table::write_memory_region(const char *rgntag, offs_t address, int size, u64 data)
{
memory_region *region = m_machine.root_device().memregion(rgntag);
auto search = get_device_search(m_machine, m_memintf, rgntag);
memory_region *const region = search.first.memregion(search.second);
// make sure we get a valid base before proceeding
if (region != nullptr)
if (region)
{
// call ourself recursively until we are byte-sized
if (size > 1)
@ -984,9 +977,16 @@ expression_error::error_code symbol_table::memory_valid(const char *name, expres
case EXPSPACE_REGION:
if (!name)
{
return expression_error::MISSING_MEMORY_NAME;
if (!m_machine.root_device().memregion(name) || !m_machine.root_device().memregion(name)->base())
return expression_error::INVALID_MEMORY_NAME;
}
else
{
auto search = get_device_search(m_machine, m_memintf, name);
memory_region *const region = search.first.memregion(search.second);
if (!region || !region->base())
return expression_error::INVALID_MEMORY_NAME;
}
break;
default:

View File

@ -190,7 +190,6 @@ public:
std::string_view filename,
std::string &listname,
std::string &description,
std::string &notes,
std::list<software_info> &infolist,
std::ostream &errors);
@ -241,8 +240,8 @@ private:
struct XML_ParserStruct * m_parser;
std::string & m_listname;
std::string & m_description;
std::string & m_notes;
bool m_data_accum_expected;
bool m_ignore_cdata;
std::string m_data_accum;
software_info * m_current_info;
software_part * m_current_part;
@ -259,7 +258,6 @@ softlist_parser::softlist_parser(
std::string_view filename,
std::string &listname,
std::string &description,
std::string &notes,
std::list<software_info> &infolist,
std::ostream &errors) :
m_filename(filename),
@ -267,8 +265,8 @@ softlist_parser::softlist_parser(
m_errors(errors),
m_listname(listname),
m_description(description),
m_notes(notes),
m_data_accum_expected(false),
m_ignore_cdata(false),
m_current_info(nullptr),
m_current_part(nullptr),
m_pos(POS_ROOT)
@ -488,6 +486,7 @@ void softlist_parser::end_handler(void *data, const char *name)
// stop accumulating
state->m_data_accum_expected = false;
state->m_ignore_cdata = false;
state->m_data_accum.clear();
}
@ -500,7 +499,11 @@ void softlist_parser::data_handler(void *data, const char *s, int len)
{
softlist_parser *state = reinterpret_cast<softlist_parser *>(data);
if (state->m_data_accum_expected)
if (state->m_ignore_cdata)
{
// allowed, but we don't use it
}
else if (state->m_data_accum_expected)
{
// if we have an std::string to accumulate data in, do it
state->m_data_accum.append(s, len);
@ -548,9 +551,9 @@ void softlist_parser::parse_root_start(const char *tagname, const char **attribu
void softlist_parser::parse_main_start(const char *tagname, const char **attributes)
{
// <software name='' cloneof='' supported=''>
if (strcmp(tagname, "software") == 0)
{
// <software name='' cloneof='' supported=''>
static char const *const attrnames[] = { "name", "cloneof", "supported" };
auto attrvalues = parse_attributes(attributes, attrnames);
@ -562,10 +565,10 @@ void softlist_parser::parse_main_start(const char *tagname, const char **attribu
else
parse_error("No name defined for item");
}
// <notes>
else if (strcmp(tagname, "notes") == 0)
{
m_data_accum_expected = true;
// <notes>
m_ignore_cdata = true;
}
else
unknown_tag(tagname);
@ -574,8 +577,6 @@ void softlist_parser::parse_main_start(const char *tagname, const char **attribu
void softlist_parser::parse_main_end(const char *tagname)
{
if (strcmp(tagname, "notes") == 0)
m_notes = std::move(m_data_accum);
}
@ -607,7 +608,7 @@ void softlist_parser::parse_soft_start(const char *tagname, const char **attribu
// <notes>
else if (strcmp(tagname, "notes") == 0)
m_data_accum_expected = true;
m_ignore_cdata = true;
// <info name='' value=''>
else if (strcmp(tagname, "info") == 0)
@ -884,10 +885,6 @@ void softlist_parser::parse_soft_end(const char *tagname)
else if (strcmp(tagname, "publisher") == 0)
m_current_info->m_publisher = std::move(m_data_accum);
// <notes>
else if (strcmp(tagname, "notes") == 0)
m_current_info->m_notes = std::move(m_data_accum);
// </part>
else if (strcmp(tagname, "part") == 0)
{
@ -915,11 +912,10 @@ void parse_software_list(
std::string_view filename,
std::string &listname,
std::string &description,
std::string &notes,
std::list<software_info> &infolist,
std::ostream &errors)
{
detail::softlist_parser(file, filename, listname, description, notes, infolist, errors);
detail::softlist_parser(file, filename, listname, description, infolist, errors);
}

View File

@ -133,7 +133,6 @@ public:
const std::string &parentname() const { return m_parentname; }
const std::string &year() const { return m_year; }
const std::string &publisher() const { return m_publisher; }
const std::string &notes() const { return m_notes; }
const std::list<software_info_item> &info() const { return m_info; }
const software_info_item::set &shared_features() const { return m_shared_features; }
software_support supported() const { return m_supported; }
@ -151,7 +150,6 @@ private:
std::string m_parentname;
std::string m_year; // Copyright year on title screen, actual release dates can be tracked in external resources
std::string m_publisher;
std::string m_notes;
std::list<software_info_item> m_info; // Here we store info like developer, serial #, etc. which belong to the software entry as a whole
software_info_item::set m_shared_features; // Here we store info like TV standard compatibility, or add-on requirements, etc. which get inherited
// by each part of this software entry (after loading these are stored in partdata->features)
@ -167,7 +165,6 @@ void parse_software_list(
std::string_view filename,
std::string &listname,
std::string &description,
std::string &notes,
std::list<software_info> &infolist,
std::ostream &errors);

View File

@ -89,8 +89,7 @@ software_list_device::software_list_device(const machine_config &mconfig, const
m_list_type(softlist_type::ORIGINAL_SYSTEM),
m_filter(nullptr),
m_parsed(false),
m_description(""),
m_notes("")
m_description("")
{
}
@ -181,7 +180,6 @@ void software_list_device::release()
m_filename.clear();
m_shortname.clear();
m_description.clear();
m_notes.clear();
m_errors.clear();
m_infolist.clear();
}
@ -296,7 +294,7 @@ void software_list_device::parse()
{
// parse if no error
std::ostringstream errs;
parse_software_list(file, m_filename, m_shortname, m_description, m_notes, m_infolist, errs);
parse_software_list(file, m_filename, m_shortname, m_description, m_infolist, errs);
file.close();
m_errors = errs.str();
}

View File

@ -116,7 +116,6 @@ public:
// getters that may trigger a parse
const std::string &description() { if (!m_parsed) parse(); return m_description; }
const std::string &notes() { if (!m_parsed) parse(); return m_notes; }
bool valid() { if (!m_parsed) parse(); return !m_infolist.empty(); }
const char *errors_string() { if (!m_parsed) parse(); return m_errors.c_str(); }
const std::list<software_info> &get_info() { if (!m_parsed) parse(); return m_infolist; }
@ -153,7 +152,6 @@ private:
std::string m_filename;
std::string m_shortname;
std::string m_description;
std::string m_notes;
std::string m_errors;
std::list<software_info> m_infolist;
};

View File

@ -1118,8 +1118,6 @@ const char cli_frontend::s_softlist_xml_dtd[] =
void cli_frontend::output_single_softlist(std::ostream &out, software_list_device &swlistdev)
{
util::stream_format(out, "\t<softwarelist name=\"%s\" description=\"%s\">\n", swlistdev.list_name(), util::xml::normalize_string(swlistdev.description().c_str()));
if (!swlistdev.notes().empty())
util::stream_format(out, "\t\t<notes>%s</notes>\n", util::xml::normalize_string(swlistdev.notes().c_str()));
for (const software_info &swinfo : swlistdev.get_info())
{
util::stream_format(out, "\t\t<software name=\"%s\"", util::xml::normalize_string(swinfo.shortname().c_str()));
@ -1133,8 +1131,6 @@ void cli_frontend::output_single_softlist(std::ostream &out, software_list_devic
util::stream_format(out, "\t\t\t<description>%s</description>\n", util::xml::normalize_string(swinfo.longname().c_str()));
util::stream_format(out, "\t\t\t<year>%s</year>\n", util::xml::normalize_string(swinfo.year().c_str()));
util::stream_format(out, "\t\t\t<publisher>%s</publisher>\n", util::xml::normalize_string(swinfo.publisher().c_str()));
if (!swinfo.notes().empty())
util::stream_format(out, "\t\t\t<notes>%s</notes>\n", util::xml::normalize_string(swinfo.notes().c_str()));
for (const auto &flist : swinfo.info())
util::stream_format(out, "\t\t\t<info name=\"%s\" value=\"%s\"/>\n", flist.name(), util::xml::normalize_string(flist.value().c_str()));

View File

@ -556,20 +556,31 @@ void menu::draw(uint32_t flags)
float const line_y0 = visible_top + (float)linenum * line_height;
float const line_y1 = line_y0 + line_height;
// set the hover if this is our item
if (mouse_in_rect(line_x0, line_y0, line_x1, line_y1) && is_selectable(pitem))
m_hover = itemnum;
// work out what we're dealing with
bool const uparrow = !linenum && show_top_arrow;
bool const downarrow = (linenum == (m_visible_lines - 1)) && show_bottom_arrow;
// set the hover if this is our item
bool const hovered = mouse_in_rect(line_x0, line_y0, line_x1, line_y1);
if (hovered)
{
if (uparrow)
m_hover = HOVER_ARROW_UP;
else if (downarrow)
m_hover = HOVER_ARROW_DOWN;
else if (is_selectable(pitem))
m_hover = itemnum;
}
// if we're selected, draw with a different background
if (is_selected(itemnum))
{
// if we're selected, draw with a different background
fgcolor = fgcolor2 = fgcolor3 = ui().colors().selected_color();
bgcolor = ui().colors().selected_bg_color();
}
// else if the mouse is over this item, draw with a different background
else if (itemnum == m_hover)
else if (hovered && (uparrow || downarrow || is_selectable(pitem)))
{
// else if the mouse is over this item, draw with a different background
fgcolor = fgcolor2 = fgcolor3 = ui().colors().mouseover_color();
bgcolor = ui().colors().mouseover_bg_color();
}
@ -578,7 +589,7 @@ void menu::draw(uint32_t flags)
if (bgcolor != ui().colors().text_bg_color())
highlight(line_x0, line_y0, line_x1, line_y1, bgcolor);
if (linenum == 0 && show_top_arrow)
if (uparrow)
{
// if we're on the top line, display the up arrow
draw_arrow(
@ -588,10 +599,8 @@ void menu::draw(uint32_t flags)
line_y0 + 0.75f * line_height,
fgcolor,
ROT0);
if (m_hover == itemnum)
m_hover = HOVER_ARROW_UP;
}
else if (linenum == m_visible_lines - 1 && show_bottom_arrow)
else if (downarrow)
{
// if we're on the bottom line, display the down arrow
draw_arrow(
@ -601,8 +610,6 @@ void menu::draw(uint32_t flags)
line_y0 + 0.75f * line_height,
fgcolor,
ROT0 ^ ORIENTATION_FLIP_Y);
if (m_hover == itemnum)
m_hover = HOVER_ARROW_DOWN;
}
else if (pitem.type == menu_item_type::SEPARATOR)
{
@ -677,23 +684,25 @@ void menu::draw(uint32_t flags)
// apply arrows
if (is_selected(itemnum) && (pitem.flags & FLAG_LEFT_ARROW))
{
float const l = effective_left + effective_width - subitem_width - gutter_width;
float const r = l + lr_arrow_width;
draw_arrow(
effective_left + effective_width - subitem_width - gutter_width,
line_y0 + 0.1f * line_height,
effective_left + effective_width - subitem_width - gutter_width + lr_arrow_width,
line_y0 + 0.9f * line_height,
l, line_y0 + 0.1f * line_height, r, line_y0 + 0.9f * line_height,
fgcolor,
ROT90 ^ ORIENTATION_FLIP_X);
if (mouse_in_rect(l, line_y0 + 0.1f * line_height, r, line_y0 + 0.9f * line_height))
m_hover = HOVER_UI_LEFT;
}
if (is_selected(itemnum) && (pitem.flags & FLAG_RIGHT_ARROW))
{
float const r = effective_left + effective_width + gutter_width;
float const l = r - lr_arrow_width;
draw_arrow(
effective_left + effective_width + gutter_width - lr_arrow_width,
line_y0 + 0.1f * line_height,
effective_left + effective_width + gutter_width,
line_y0 + 0.9f * line_height,
l, line_y0 + 0.1f * line_height, r, line_y0 + 0.9f * line_height,
fgcolor,
ROT90);
if (mouse_in_rect(l, line_y0 + 0.1f * line_height, r, line_y0 + 0.9f * line_height))
m_hover = HOVER_UI_RIGHT;
}
}
}
@ -856,13 +865,15 @@ void menu::handle_events(uint32_t flags, event &ev)
if (custom_mouse_down())
return;
if ((flags & PROCESS_ONLYCHAR) == 0)
if (!(flags & PROCESS_ONLYCHAR))
{
if (m_hover >= 0 && m_hover < m_items.size())
{
m_selected = m_hover;
}
else if (m_hover == HOVER_ARROW_UP)
{
if ((flags & FLAG_UI_DATS) != 0)
if (flags & FLAG_UI_DATS)
{
top_line -= m_visible_items - (last_item_visible() ? 1 : 0);
return;
@ -884,6 +895,16 @@ void menu::handle_events(uint32_t flags, event &ev)
m_selected = m_items.size() - 1;
top_line += m_visible_lines - 2;
}
else if (m_hover == HOVER_UI_LEFT)
{
ev.iptkey = IPT_UI_LEFT;
stop = true;
}
else if (m_hover == HOVER_UI_RIGHT)
{
ev.iptkey = IPT_UI_RIGHT;
stop = true;
}
}
break;

View File

@ -152,9 +152,9 @@ submenu::submenu(mame_ui_manager &mui, render_container &container, std::vector<
{
core_options *opts = nullptr;
if (m_driver == nullptr)
opts = dynamic_cast<core_options*>(&mui.machine().options());
opts = dynamic_cast<core_options *>(&mui.machine().options());
else
opts = dynamic_cast<core_options*>(options);
opts = dynamic_cast<core_options *>(options);
for (option & sm_option : m_options)
{
@ -358,9 +358,9 @@ void submenu::populate(float &customtop, float &custombottom)
{
case OPTION_BOOLEAN:
item_append_on_off(_(sm_option->description),
sm_option->options->bool_value(sm_option->name),
0,
static_cast<void*>(&(*sm_option)));
sm_option->options->bool_value(sm_option->name),
0,
static_cast<void*>(&(*sm_option)));
break;
case OPTION_INTEGER:
{
@ -378,9 +378,9 @@ void submenu::populate(float &customtop, float &custombottom)
}
arrow_flags = get_arrow_flags(i_min, i_max, i_cur);
item_append(_(sm_option->description),
sm_option->entry->value(),
arrow_flags,
static_cast<void*>(&(*sm_option)));
sm_option->entry->value(),
arrow_flags,
static_cast<void*>(&(*sm_option)));
}
break;
case OPTION_FLOAT:
@ -400,9 +400,9 @@ void submenu::populate(float &customtop, float &custombottom)
arrow_flags = get_arrow_flags(f_min, f_max, f_cur);
std::string tmptxt = string_format("%g", f_cur);
item_append(_(sm_option->description),
tmptxt,
arrow_flags,
static_cast<void*>(&(*sm_option)));
tmptxt,
arrow_flags,
static_cast<void*>(&(*sm_option)));
}
break;
case OPTION_STRING:
@ -418,8 +418,8 @@ void submenu::populate(float &customtop, float &custombottom)
default:
arrow_flags = FLAG_RIGHT_ARROW;
item_append(_(sm_option->description),
sm_option->options->value(sm_option->name),
arrow_flags, static_cast<void*>(&(*sm_option)));
sm_option->options->value(sm_option->name),
arrow_flags, static_cast<void*>(&(*sm_option)));
break;
}
break;

View File

@ -12,9 +12,10 @@
#pragma once
#include "emuopts.h"
#include "ui/menu.h"
#include "emuopts.h"
#include <string>
#include <vector>