介绍
MAME是一个交互式仿真系统的调式器,用于调试或开发老式系统的仿真工具。可以用来创建作弊,HACKROM,或者研究软件的工作原理。
用-debug参数可以激活自带的调试器。默认设置中,用键盘的"~"符号键可以进入调试器。按键也可以在设置中更改。
调试器的界面取决于你的操作系统和构建MAME时的参数选项。调试器提供有多窗口界面,可以方便地查看内存或反汇编代码的内容。
而调试器控制台窗口是一个特殊窗口,可以显示当前程序计数器地址,CPU和寄存器的值,和反汇编代码的内容,并提供了命令行界面。
调试器命令
下面将介绍调试器的命令行。或者你可以在调试器控制台窗口命令行中键入help <topic>,其中<topic>是命令的名称,直按查看该命令的帮助文档。
指定设备和地址空间
调试器命令需要指定要在哪个设备上操作的参数。如果未明确指定设备,则使用调试器中当前可见的CPU。可以通过标签或CPU编号指定设备。
标签是用冒号分隔的路径,MAME使用它来识别系统中的设备。您可以在配置选项、调试器反汇编和内存查看器源列表以及MAME UI的各种地方看到它们
CPU编号是调试器分配给系统中CPU设备的递增编号,从零开始。cpunum符号保存着调试器中当前可见CPU的CPU号(可以通过在调试器控制台中输入print cpunum命令来查看)。
如果一个标记以插入符号(^)或点(.)开头,它将相对于调试器中当前可见的CPU进行解释,否则将相对于根机器设备进行解释。如果设备参数既是标记又是CPU号,那么它将被解释为标记。
Examples:
例:
maincpu
带有绝对标签的设备:maincpu。
^melodypsg
带有melodypsg标记的可见CPU的同层级设备。
.:adc
带有adc标记的可见CPU的子层级设备。
2
系统中的第3个cpu设备(从0开始的索引)。
操作内存的命令通过允许设备标记或CPU号后面可选地跟着地址空间标识符来扩展这一点。地址空间标识符是类似标签的字符串。您可以在调试器内存查看器源列表中看到它们。如果省略地址空间标识符,将使用默认地址空间。通常,这是设备最先出现的地址空间。许多命令都有d、i和o(数据、I/O和操作码)后缀的变体,默认分别对应索引1、2、3处的地址空间,因为这些对cpu设备具有特殊意义。
在未指定的情况下,将使用子设备的默认地址空间而不是特定的地址空间。
例:
ram
带有绝对标记的设备的默认地址空间:ram,或可见CPU的ram空间。
.:io
带有io标记的可见CPU的子设备的默认地址空间,或可见CPU的io空间。
:program
带有绝对标记的设备的默认地址空间:program,或根机器设备的程序空间。
^vdp
带有标记vdp的可见CPU的同层级设备的默认地址空间。
^:data
带有标记数据的可见CPU的同层级设备的默认地址空间,或者可见CPU的父设备的数据空间。
1:rom
带有rom标记的系统中第2个CPU(从0开始的索引)的子设备的默认地址空间,或者系统中第二个CPU的rom空间。
2
系统中第3个cpu设备的默认地址空间(从0开始的索引)。
如果一个命令接受一个模拟的内存地址作为参数,该地址后面可以有选择地跟一个地址空间规范,如上所述。
Examples:
例:
0220
可见CPU的默认地址空间中的地址0220。
0378:io
带有绝对标记的设备的默认地址空间中的地址0378,或者可见CPU的io空间。
1234:.:rom
地址1234在带有标签:rom的可见CPU的子设备的默认地址空间中,或者可见CPU的rom空间。
1260:^vdp
带有标记vdp的可见CPU的同层级设备的默认地址空间中的地址1260。
8008:^:data
地址8008在带有标记data的可见CPU的同层级设备的默认地址空间中,或者可见CPU的父设备的数据空间中。
9660::ram
带有绝对标记的设备的默认地址空间中的地址9660:ram,或根机器设备的ram空间。
这里的示例包含了许多极端情况,但一般来说,调试器应该将最可能的含义用于设备或地址空间规范。
调试器表达式语法
表达式可以在任何需要数字或布尔形参的地方使用。表达式的语法类似于C语言风格的表达式语法,具有和C相同的的运算符优先级和圆括号。缺少了一些操作符(比如三元条件运算符)和一些新操作符(内存存取器)。
下表列出了所有的操作符,按优先级从高到低排列:
( )
圆括号
++ --
增量和减量操作符
++ -- ~ ! - + b@ w@ d@ q@ b! w! d! q!
递增/递减,二进制补码,逻辑补码,单目运算符,内存访问
* / %
乘、除、求余
+ -
加、减
<< >>
移位运算
< ``<= > >= == !=
比较运算符
&
按位与运算
^
按位异或运算
|
按位或运算
&&
逻辑与运算
||
逻辑或运算
= *= /= %= += -= <<= >>= &= |= ^=
赋值运算
,
逗号运算
与C表达式的主要区别:
所有数字都是无符号的64位值。这意味着负数是不可能的。
逻辑连接和分离运算符&&和||没有短路属性,表达式的两边总是被求值
数字
数字根据其基数加前缀:
16进制数:"$" 或 "0x"
10进制数:"#"
8进制数:“0o”
2进制数:"0b"
无前缀的数字是十六进制(以16为基数)。
例:
123 为16进制数的 123 (10进制为291)
$123 为16进制数的 123 (10进制为291)
0x123 为16进制数的 123 (10进制为291)
#123 为10进制数的123
0o123 为8进制数的123(10进制为83)
0b1001 为二进制的1001(10进制为9)
0b123 为无效值
布尔值
任何计算结果为数字的表达式都可以在需要布尔值的地方使用。0被视为假,所有非零值都被视为真。此外,字符串true被视为真,字符串false被视为假。
空字符串可以作为布尔参数的参数提供给调试器,以便使用默认值,即使在指定了后续参数时也是如此。
内存访问
内存访问前缀操作符允许对模拟地址空间进行读写。内存前缀操作符指定访问大小和是否禁用副作用(边际效应),并可以在前面随意地加上地址空间规范。支持的访问大小和副作用(边际效应)模式如下:
b :指定存取大小为字节 8-bit
w:指定存取大小为字16-bit
d:指定存取大小为双字32-bit
q:指定存取大小为四字64-bit
@:禁止副作用(边际效应)
!:不禁止副作用(边际效应)
以下原文中为解释side effects(副作用/边际效应)的原文,比较艰涩难懂,可以去搜索一下关于内存的边际效应的文章
Suppressing side effects of a read access yields the value reading from address would, with no further effects. For example reading a mailbox with side effects disabled will not clear the pending flag, and reading a FIFO with side effects disabled will not remove an item.
For write accesses, suppressing side effects doesn’t change behaviour in most cases – you want to see the effects of writing to a location. However, there are some exceptions where it is useful to separate multiple effects of a write access. For example:
Some registers need to be written in sequence to avoid race conditions. The debugger can issue multiple writes at the same point in emulated time, so these race conditions can be avoided trivially. For example writing to the MC68HC05 output compare register high byte (OCRH) inhibits compare until the output compare register low byte (OCRL) is written to prevent race conditions. Since the debugger can write to both locations at the same instant from the emulated machine’s point of view, the race condition is not usually relevant. It’s more error-prone if you can accidentally set hidden state when all you really want to do is change the value, so writing to OCRH with side effects suppressed does not inhibit compare, it just changes the value in the output compare register.
Writing to some registers has multiple effects that may be useful to separate for debugging purposes. Using the MC68HC05 as an example again, writing to OCRL changes the value in the output compare register, and also clears the output compare flag (OCF) and enables compare if it was previously inhibited by writing to OCRH. Writing to OCRL with side effects disabled just changes the value in the register without clearing OCF or enabling compare, since it’s useful for debugging purposes. Writing to OCRL with side effects enabled has the additional effects.
存取大小前面选择地加上一个访问类型规范:
p或lp指定逻辑地址,默认为空间0(program)
d或ld指定逻辑地址,默认为空间1 (data)
i或li指定逻辑地址,默认为空间2 (I/O)
3或l3指定默认为空间3的逻辑地址(opcodes)
pp指定一个物理地址,默认为空间0(program)
pd指定物理地址默认为空间1 (data)
pi指定物理地址,默认为空间2 (I/O)
p3指定一个物理地址,默认为空间3(opcodes)
r指定直接读/写指针访问,默认为空间0(program)
o指定直接读/写指针访问,默认为空间3 (opcodes)
m表示内存区域
最后,它的前面可能是一个标记或地址空间名称,后面跟着一个点(.)。
一些最简单的例子:
b@<addr>
引用当前CPU程序空间中<addr>的字节,抑制副作用(边际效应)。
b!<addr>
引用当前CPU程序空间中<addr>的字节,不抑制副作用(边际效应)。
添加了访问类型的例子:
dw@300
在当前CPU中引用逻辑地址(地址空间data)300中的字(16-bit)大小的数据
maincpu:status.b@<addr>
引用CPU状态空间中地址<addr>的字节,带有绝对标记:maincpu
有些组合是没有用的。例如,物理地址和逻辑地址对于某些cpu是等价的,直接读/写指针访问不存在副作用。但访问内存区域(m访问类型)需要指定一个标记
内存访问可以用作左值和右值,因此可以写入b@100 = ff来在内存中写入一个字节。
函数
调试器在表达式中支持许多实用函数
min(<a>, <b>)
返回两个参数中较小的值 。
max(<a>, <b>)
返回两个参数中较大的值 。
if(<cond>, <trueval>, <falseval>)
如果<cond>为真(非零)则返回<true >,否则返回<false >。注意,无论<cond>是真还是假,<true >和<false >的表达式都要求值。
abs(<x>)
将实参重新解析为64位有符号整数并返回绝对值。
bit(<x>, <n>[, <w>])
从<x>提取并右对齐一个<w>位宽的位域,最低有效位位置位置<n>,从最低有效位开始计数。如果省略<w>,则提取单个位。
s8(<x>)
将参数从8位扩展到64位(覆盖第8位到63位,包含第7位的值,从最低有效位开始计数)。
s16(<x>)
将参数从16位扩展到64位(覆盖16到63位,包括第15位的值,从最低有效位开始计数)
s32(<x>)
将参数从32位扩展到64位(覆盖第32位到63位,包含第31位的值,从最低有效位开始计数)。