游戏机stm32

stm32的一些射手;怎么,为什么,发生了什么。



前言


我一方面是“古老”的射击游戏爱好者,另一方面是嵌入式开发人员,所以我一直想知道那个时代的作者如何以及为什么设法在非常“适中”的硬件上实现需要全新方法的新类型。我决定尝试使用基于现代MK的解决方案来启动类似的东西-这里有裸机和“适度”的资源以及相当强大的调试工具(stm32,恕我直言)。因此,我的选择落在了stm32f769i发现开发板上。

笔记


目前,只能从Keil MDK环境(引导加载程序,游戏)或使用arm-gcc + make(仅引导加载程序)进行组装。当前可用的端口-Quake I(+ mods),Doom(+ mods),Nukem Duke(+ mods),Hexen,Heretic。通过所有修改,可以显着扩展该列表。

开始吧


在本文中,我将尝试简要概述其实现的主要思想和原理,特别是针对stm32f769i发现的游戏控制台的创建方法。另外,我将尽量避免详细的技术细节,而是追求向读者介绍使用现代MK的另一种选择的目标。所谓“游戏机”,是指能够运行“用户”应用程序而无需更新主软件的独立设备。

建筑


由于最终的实现需要在不更新MK固件的情况下独立运行各种游戏的能力,因此需要某些版本的“ bootloader”,因此:

  1. 装载机; 使用内存-应用程序(游戏)的“安装”,启动。
  2. 司机; 服务HAL系统级别和相关功能,将API传输到应用程序。
  3. 应用; 最终程序不会将控制权交还给引导加载程序。

1.引导程序


它基于IAP(应用程序内编程)模型-驱动程序,使用意法半导体的示例。

这种方法的独特之处在于,无需更改MK引导程序的配置,引导程序的
整个“主体”都位于主内存中,这又使“ stm32f769i”发现的使用可以“开箱即用”。

该级别的主要功能是读取.bin文件,将其内容写入MK存储器并传输控制。在这一阶段,关键点是读取入口点的地址和堆栈指针的地址,不需要第二个,因为 应用程序不返回控制权-
堆栈是共享的,不需要重写指针。也可以通过指向函数的指针进行调用。因此,结果将是一个“混合”加载程序-它的“驱动程序”部分继续为应用程序提供服务,同时卸载加载程序本身的资源。

2.司机


该驱动程序以HAL级别以上的“包装器”形式制成,可以访问必要的资源-文件系统,显示\监视器,声音,游戏控制器\显示传感器。为了进一步使用,驱动程序API通过“共享内存”以指针结构的形式传输,“共享内存”是为引导加载程序和返回端保留的一块内存。这种操作需要占用内存,可能最好的解决方案是使用SWI(软中断,svc调用),但是为此,您必须能够更改上下文-因为并非所有呼叫都可以在中断中处理。另外,“共享”内存用于传递用户参数(例如,通过控制台),前提条件是为此部分添加no-init属性,这将避免在用户应用程序初始化时用运行时库覆盖它。

3.申请


结果,在构建应用程序时,您唯一需要了解的就是处理器内核的体系结构,没有来自HAL的依赖关系,也没有中断向量表,所有中断都由加载程序处理。因此,应用程序在程序存储器中使用的空间要少得多,这是因为部分功能与引导加载程序/驱动程序一起受到“保护”,这使您可以将其安装在SRAM数据区(内部RAM)中。反过来,这可以显着减少闪存的写周期数,并且通常还可以加快调试和执行过程。缺点-在调试时,仅可以从外部调用应用程序,例如,使用控制台上的命令(ST-Link上的COM端口,VCOM),为此使用了非常简化的命令行版本。

资源:

装载程序hal驱动程序

开发特色


记忆


我必须处理的第一件事是分配外部内存资源(SDRAM)的问题。这是由于某些游戏在.bss节中需要更多内存(nukem〜5.5mbytes)。只能在sdram中放置这样的卷,但是因为引导加载程序使用相同的内存来存储临时数据-图像,声音,文件内容等..仅在应用程序启动之前才需要-决定拆分对这部分内存的管理-在每一侧都有自己的malloc / free。启动后,驱动程序使用指向malloc \ free函数的指针,如有必要,该指针将作为参数传递给调用函数。即,在开始游戏之后,驾驶员不能直接从sdram执行分配。关于D-Cache和I-Cache的一个有趣事实-由于处理外部内存的特殊性-您必须在启动之前关闭这两行,因为重新初始化sdram,一切都会好起来,但是有一个“但是”-您必须始终使缓存无效,否则,默认情况下,它将保留所有行的有效状态,而在关闭缓存的间隔中它们将被覆盖。
另一个功能-所有引导加载程序数据都放置在DTCM部分中,这允许访问内存时不使用高速缓存(MPU允许相同),结果-使用DMA时解决了一致性问题;
结合使用-CPU-> D-Cache->内存<-DMA

平面艺术


在大多数情况下,这些是几个图像缩放功能(2x2、3x3),
初始化功能和调色板的加载。关键是正确指示帧存储器属性(通过MPU配置执行)-对于引导加载程序,这将是“回写,无写分配”,以消除闪烁效应,因为使用单缓冲区模式,而应用程序使用“ Write through,no writealloc”(写入,无写分配),可以实现最高的FPS(Doom〜28-40)。

现有的所有游戏端口均使用8位图形进行操作,但也可以切换到16位真彩色模式(需要从游戏侧面进行修改)。
可以使用DMA2D缩放8位图像,但是这种方法并没有得到回报-处理最终分辨率为640x480像素的图像需要花费1000次中断,它还会在游戏中产生很多假象-单个图像(子画面,多边形)将无法完全呈现,因为 在这种情况下,游戏中的整个渲染过程将与屏幕草图并行进行。

声音


该部分以简单的软件16位,16通道混频器的形式制成,该混频器基于ST-Microelectronics的示例,还使用了I2S控制器。目前,还没有办法在它们之间转换音频格式-这一部分是根据要求在游戏级别实现的。我认为Duke Nukem具有最丰富的声音处理实用程序集,其中包括 和混响。

输入


操纵杆驱动程序也使用usb-hid类制作(实际上,游戏手柄将定义为计算机鼠标..)。显示传感器(如游戏手柄)使用相同的通道来传输事件,这是非常不方便的事情。

游戏类


厄运


stm32doom端口为基础
添加了声音支持,并添加了最新的巧克力厄运变化,并添加了来自prBoom的Killough所作的一些更正。该游戏可让您使用可用于巧克力世界的所有修改和地图,包括从3DO和PS1版本的游戏中添加了风景和声音。添加了图形优化-纹理渲染的分辨率取决于距离,解决方案在不同位置均如此-每秒+ 3-7帧。可用。最新版本还增加了对“透明”精灵的支持-一切均基于生成的调色板元素组合表-在Quake II和基于Build引擎的游戏中使用了类似的用法。游戏包括修改可以完全完成。

资源:

stm32doom3DO毁灭巧克力毁灭当前版本

努克公爵


基于巧克力公爵由于没有时间完全理解游戏的源代码,因此所有内容均保持“原样”,仅修复了一些小缺陷。该端口还允许您运行官方的,不太完全的修改-原子版,《核冬天》等。
注意-目前,由于存在缺陷,无法完全完成游戏的每一集。

资源:

巧克力公爵当前版本

雷神之锤


不幸的是,到原始存储库的链接没有保留,我找不到它,该
端口以sdl quake的名称执行。在这些功能中,值得注意的是客户端-服务器体系结构的存在,一个非常“繁琐的”堆栈(〜700kb),由于它的出现,第一次引起了很多有趣的情况(armcc并未真正监视其使用),存在对齐方面的广泛问题-也许这值得关注只有编译器的armcc,但几乎到处都需要一个大于一个字节的结构元素-您需要使用包装函数来读取/写入字节,否则-硬故障异常。这款游戏相当不错,平均fps约为15。也可以完成几个情节,或者仅在第一个难度级别时才或多或少感到舒服:)

资源:

当前版本

赫克森,异端


在大多数情况下,它们继承了Doom引擎,因此从移植的角度来看,它们几乎是相同的(IMHO)。在hexen中添加了在选定位置启动游戏的功能,游戏无法完全完成。

资源:

hexen stm32异端stm32

结果


末日努克公爵雷神之锤

谢谢您的关注。

All Articles