MENU

MAME移植1

2024 年 06 月 10 日 • 嵌入式

1. MAME是啥

MAME最初是一款街机模拟器,可以在现代的计算机上运行以前的街机游戏。不过按照官网的介绍来看,现在更偏向于一个模拟器平台,类似QEMU。 MAME官网。

c3Ewzx

2. 为什么研究MAME

原因其实比较简单,我想做一个掌上可以玩的街机游戏机🎮。
为什么不去买一个,如果这么想很多事情都会失去热情,自己做一个意义更大一点,另外还可以将成果开源出去。一句话,酷炫就完了。

3. MAME初探

本次探究采用的是MAME 0122版本,而不是最新的版本,因为街机本身就是80/90年代的流行的游戏软件,并不会由于模拟器越新支持的街机功能就越强,而且越早的MAME版本实现也越简单,可以减少代码阅读的复杂度。

后续的架构和流程总结均是建立在0122版本以及个人理解之上,如果有不对的地方还请留言指正。

3.1 MAME架构

kqgX4T

MAME是一个模拟器,对街机所需的硬件进行了模拟,分为如下几个部分:

  • CPU。使用软件对街机的CPU进行了模拟,不过以前CPU都是一些简单的芯片,比如摩托罗拉68000;
  • 输入。输入这块对街机本身来说是内存中的一段地址,读取电平的高低。MAME在此基础之上进行了抽象,分为键盘/摇杆/光电枪等;
  • 视频。视频是输出设备,这块比较复杂,因为涉及到显示的分层,这块后面再详细说;
  • 音频。街机的音频一般是独立的芯片,MAME对不同的街机设备的芯片进行了模拟,再对喇叭进行了模拟,最终的输出是对音频进行编码后的数字信号。

而我目前需要做的事情就是对输入输出设备进行改造。输入设备修改成手柄输入,视频输出到TFT LCD屏幕,音频输出到喇叭硬件上。

3.2 代码移植

3.2.1 构建

MAME代码中对系统适配做了很好的隔离,所以系统适配都放在了osd中,也就是下面目录中。其中osdminiwindows是原先代码仓就有的,unix是我新建的目录。
d0VYu6

为了构建出unix版本的MAME,我们需要编译osd的时候不将windows编译进去,而编译unix。这个修改Makefile就可以了,Makefile中已经预留了编译的目录跟随编译时OSD的值。

ZhG3GT

src/osd目录下新增unix.mak,用来编译源码,其中内容如下:

#-------------------------------------------------
# object and source roots
#-------------------------------------------------

UNIXSRC = $(SRC)/osd/$(OSD)
UNIXOBJ = $(OBJ)/osd/$(OSD)

OBJDIRS += $(UNIXOBJ)

#-------------------------------------------------
# OSD core library
#-------------------------------------------------

OSDCOREOBJS = \
    $(UNIXOBJ)/main.o

#-------------------------------------------------
# OSD UNIX library
#-------------------------------------------------

OSDOBJS = \
    $(UNIXOBJ)/osd_stub.o

#-------------------------------------------------
# rules for building the libaries
#-------------------------------------------------

$(LIBOCORE): $(OSDOBJS)

$(LIBOSD): $(OSDOBJS) $(OSDCOREOBJS)

当前共分为两个文件,一个是main.c,用来作为程序的入口;另外一个是osd_stub.c,是用来打桩用的,因为真正要运行MAME,需要实现很多平台相关函数,如果只是为了编译过打桩即可。

至此,unix版本的MAME即可构建出来,当然功能是不好使的。

3.2.2 原型验证适配

这里的原型验证只是为了验证在Linux(unix)下,MAME可以正常工作,至少街机的ROM可以正常加载,CPU模拟这块正常运行。

ROM
为了验证游戏ROM可以正常加载运行,我挑了一款简单的游戏,1942(ROM可以咕噜咕噜一下搜到)。下载后的ROM需要解压,文件夹命名为1942。
Gf6g5e

main函数
main函数中只需要适配一句话即可,其中mame_unix_options拷贝自windowsosd中的定义。

int main(int argc, const char **argv)
{
    return cli_execute(argc, argv, mame_unix_options);
}

视频输出

模拟器每次刷新屏幕都会调用osd_update函数,所以这里需要对osd_update函数进行实现。
由于我用的linux系统是没有图形界面的,所以采用将视频输出转储到图片,这里的图片类型采用PPM格式。

osd_update中我们需要将待输出的图像原语转换为我们需要的格式,获取当前一帧图像采用的是如下函数:

render_target_get_primitives

获取之后采用MAME自带的RGB转换函数进行转换:

drawdd_rgb888_draw_primitives(head, g_draw_buff, DRAW_WIDTH_MAX, DRAW_HEIGHT_MAX, DRAW_WIDTH_MAX);

其中head为图像原语链表头,g_draw_buff为输出的内存地址,我们再将这块内存写入文件就获得图片。

按键输入

按键输入需要注册键盘设备,以及按键的键值对应的处理函数。采用如下方式即可。

input_device *input_device = input_device_add(DEVICE_CLASS_KEYBOARD, "my keyboard", NULL);

input_device_item_add(input_device, "O", name_O, ITEM_ID_O, get_key_state);
input_device_item_add(input_device, "K", name_K, ITEM_ID_K, get_key_state);

其中第四个参数为注册的键值,键值和街机的按键功能映射可以参见src/emu/inptport.c

至此,也就完成了基本的图形输出和键盘输入了。

运行效果

街机菜单界面
3ijqe7

1942游戏界面
milgtP

试了下合金弹头也是OK的
kIfTfI

不过都只是运行的截图图片,离显示视频还有一段距离。