在这篇文章中,我将描述读取操纵杆、更改精灵位置、水平翻转、Sega Genesis 模拟器以及可能的控制台本身的过程。
读取点击并处理将棋操纵杆的“事件”按照以下方案进行:
- 请求按下按钮的位组合
- 读取按下的按钮的位
- 游戏逻辑级别的处理
要移动骨架精灵,我们需要存储当前位置的变量。
内存
游戏逻辑变量存储在RAM中;到目前为止人们还没有想出更好的办法。让我们声明变量地址并更改渲染代码:
skeletonYpos = $FF0002
frameCounter = $FF0004
skeletonHorizontalFlip = $FF0006
move.w #$0100,skeletonXpos
move.w #$0100,skeletonYpos
move.w #$0001,skeletonHorizontalFlip
FillSpriteTable:
move.l #$70000003,vdp_control_port
move.w skeletonYpos,vdp_data_port
move.w #$0F00,vdp_data_port
move.w skeletonHorizontalFlip,vdp_data_port
move.w skeletonXpos,vdp_data_port
如您所见,可用于工作的地址从 0xFF0000 开始,到 0xFFFFFF 结束,总共有 64 KB 的内存可供我们使用。骨架位置在 sculptureXpos、sculptureYpos 处声明,水平翻转在 sculptureHorizontalFlip 处声明。
手柄
与 VDP 类比,游戏手柄的工作分别通过两个端口进行:控制端口和数据端口,第一个为0xA10009和0xA10003共号。使用游戏手柄时有一个有趣的功能——首先,您需要请求用于轮询的按钮组合,然后在等待总线上的更新后,读取所需的按键。对于 C/B 和 D-pad 按钮,这是 0x40,示例如下:
move.b #$40,joypad_one_control_port; C/B/Dpad
nop ; bus sync
nop ; bus sync
move.b joypad_one_data_port,d2
rts
在寄存器 d2 中,按钮按下或未按下的状态将保留,一般来说,通过日期端口请求的内容将保留。之后,转到您最喜欢的模拟器的 Motorola 68000 寄存器查看器,根据击键查看 d2 寄存器等于什么。您可以通过聪明的方式在手册中找到答案,但我们并不相信他们的话。进一步处理 d2
寄存器中按下的按钮
cmp #$FFFFFF7B,d2; handle left
beq MoveLeft
cmp #$FFFFFF77,d2; handle right
beq MoveRight
cmp #$FFFFFF7E,d2; handle up
beq MoveUp
cmp #$FFFFFF7D,d2; handle down
beq MoveDown
rts
Проверять нужно конечно отдельные биты, а не целыми словами, но пока и так сойдет. Теперь осталось самое простое – написать обработчики всех событий перемещения по 4-м направлениям. Для этого меняем переменные в RAM, и запускаем процедуру перерисовки.
Пример для перемещения влево + изменение горизонтального флипа:
move.w skeletonXpos,d0
sub.w #1,d0
move.w d0,skeletonXpos
move.w #$0801,skeletonHorizontalFlip
jmp FillSpriteTable
После добавления всех обработчиков и сборки, вы увидите как скелет перемещается и поворачивается по экрану, но слишком быстро, быстрее самого ежа Соника.
Не так быстро!
Чтобы замедлить скорость игрового цикла, существуют несколько техник, я выбрал самую простую и не затрагивающую работу с внешними портами – подсчет цифры через регистр пока она не станет равна нулю.
Пример замедляющего цикла и игрового цикла:
move.w #512,frameCounter
WaitFrame:
move.w frameCounter,d0
sub.w #1,d0
move.w d0,frameCounter
dbra d0,WaitFrame
GameLoop:
jsr ReadJoypad
jsr HandleJoypad
jmp GameLoop
此后,骨架运行速度变慢,这正是所需要的。据我所知,“放慢速度”的最常见选项是计算垂直同步标志;您可以计算屏幕被绘制的次数,从而与特定的 fps 相关联。
链接
https://gitlab .com/demensdeum/segagenesisamples/-/blob/main/8Joypad/vasm/main.asm
来源
https://www.chibiakumas.com/68000/platform2.php
https://huguesjohnson.com/programming/genesis/tiles-sprites/
为 Sega Genesis #4 编写汇编
在这篇文章中,我将介绍如何使用 Sega Genesis 控制台的 VDP 模拟器绘制精灵。
渲染精灵的过程与渲染图块非常相似:
- 将颜色加载到 CRAM
- 将部分精灵 8×8 上传到 VRAM
- 在 VRAM 中填充精灵表
例如,让我们取一个带有剑的骷髅精灵,大小为 32×32 像素![]()
Skeleton Guy [Animated] by Disthorn
CRAM
使用 ImaGenesis,我们将其转换为用于汇编程序的 CRAM 颜色和 VRAM 模式。之后,我们将得到两个asm格式的文件,然后我们将颜色重写为字大小,并且图块必须按照正确的顺序放置才能绘制。
有趣的信息:您可以通过 0xF 寄存器将 VDP 自动递增切换为字大小,这将从 CRAM 颜色填充代码中删除地址增量。
显存
将棋手册对于大精灵有正确的图块顺序,但我们更聪明,所以我们将从博客中获取索引ChibiAkumas,让我们从索引 0 开始计数:
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15
为什么一切都是颠倒的?你想要什么,控制台是日本的!甚至可以从右到左!
让我们手动更改 asm sprite 文件中的顺序:
dc.l $11111111 ; Tile #0
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111 ; Tile #4
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111 ; Tile #8
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111122
dc.l $11111122
dc.l $11111166
dc.l $11111166 ; Tile #12
dc.l $11111166
dc.l $11111166
и т.д.
像常规图块/图案一样加载精灵:
lea Sprite,a0
move.l #$40200000,vdp_control_port; write to VRAM command
move.w #128,d0 ; (16*8 rows of sprite) counter
SpriteVRAMLoop:
move.l (a0)+,vdp_data_port;
dbra d0,SpriteVRAMLoop
要绘制精灵,剩下的就是填写精灵表
精灵表
精灵表被填充在VRAM中,其所在位置的地址被输入到VDP寄存器0x05中,这个地址又很棘手,你可以在手册中看到它,地址F000的例子:
Ок, теперь запишем наш спрайт в таблицу. Для этого нужно заполнить “структуру” данных состоящую из четырех word. Бинарное описание структуры вы можете найти в мануале. Лично я сделал проще, таблицу спрайтов можно редактировать вручную в эмуляторе Exodus.![]()
结构体参数从名字上就很明显了,例如XPos、YPos–坐标,瓷砖–绘制起始图块的编号、HSize、VSize –精灵尺寸通过添加第 8×8、HFlip、VFlip– 部分来实现。精灵水平和垂直的硬件旋转。![]()
记住精灵可以离开屏幕非常重要,这是正确的行为,因为……从内存中卸载离屏精灵 –相当耗费资源的活动。
模拟器中填充数据后,需要从VRAM复制到地址0xF000,Exodus也支持此功能。
类比绘制图块,我们首先访问VDP控制端口从地址0xF000开始写入,然后将结构体写入数据端口。
让我提醒您,VRAM寻址的描述可以在手册或博客中阅读 无名算法 .
简而言之,VDP 寻址的工作原理如下:
[..DC BA98 7654 3210 …. …。 …。 ..FE]
其中 hex 是所需地址中的位位置。前两位是请求的命令类型,例如 01 –写入 VRAM。那么对于地址 0XF000 结果是:
0111 0000 0000 0000 0000 0000 0000 0011 (70000003)
结果我们得到代码:
move.l #$70000003,vdp_control_port
move.w #$0100,vdp_data_port
move.w #$0F00,vdp_data_port
move.w #$0001,vdp_data_port
move.w #$0100,vdp_data_port
之后,骨骼精灵将显示在坐标 256, 256 处。很酷吧?
链接
https://gitlab.com/demensdeum /segagenesissamples/-/tree/main/7Sprite/vasm
https://opengameart.org/content/skeleton-guy-animated
来源
https://namelessalgorithm.com/genesis/blog/vdp/一个>
https://www.chibiakumas.com/68000/platform3.php#LessonP27
https://plutiedev.com/sprites
为 Sega Genesis #3 编写汇编
在这篇文章中,我将描述如何使用汇编程序在 Sega Genesis 模拟器上显示图块中的图像。
Exodus 模拟器中的 Demens Deum 初始图像将如下所示:

使用图块输出PNG图像的过程是逐步完成的:
- 将图像缩小到将棋屏幕的大小
- 将 PNG 转换为汇编数据代码,分为颜色和图块
- 将调色板加载到 CRAM
- 将图块/图案加载到 VRAM
- 加载 VRAM 中平面 A/B 地址处的切片索引
- 您可以使用您最喜欢的图形编辑器(例如 Blender)将图像缩小到将棋屏幕的大小。
PNG 转换
要转换图像,可以使用ImaGenesis工具在wine下工作;需要Visual Basic 6库,可以使用winetricks (winetricks vb6run)安装,或者可以从网上下载RICHTX32.OCX并放在正确操作的应用程序文件夹。
在 ImaGenesis 中,您需要选择 4 位颜色,将颜色和图块导出到两个汇编格式文件。接下来,在带有颜色的文件中,您需要将每种颜色放入一个单词(2 个字节)中,为此您使用 dc.w 操作码。
例如 CRAM 启动屏幕:
dc.w $0000
dc.w $0000
dc.w $0222
dc.w $000A
dc.w $0226
dc.w $000C
dc.w $0220
dc.w $08AA
dc.w $0446
dc.w $0EEE
dc.w $0244
dc.w $0668
dc.w $0688
dc.w $08AC
dc.w $0200
dc.w $0000
保持图块文件不变,它已经包含了正确的加载格式。部分图块文件的示例:
dc.l $11111111 ; Tile #0
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111 ; Tile #1
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
正如您从上面的示例中看到的,图块是由 CRAM 调色板索引组成的 8×8 网格。
CRAM 中的颜色
通过将颜色加载命令设置到控制端口(vdp 控制)中的特定 CRAM 地址来完成加载到 CRAM 中。命令格式在Sega Genesis软件手册(1989)中有描述,我只是补充一下,您只需在地址中添加0x20000即可移动到下一个颜色。
接下来需要将颜色加载到数据端口(vdp数据);理解加载的最简单方法是使用下面的示例:
lea Colors,a0 ; pointer to Colors label
move.l #15,d7; colors counter
VDPCRAMFillLoopStep:
move.l d0,vdp_control_port ;
move.w (a0)+,d1;
move.w d1,(vdp_data_port);
add.l #$20000,d0 ; increment CRAM address
dbra d7,VDPCRAMFillLoopStep
VRAM 中的图块
接下来是将图块/图案加载到 VRAM 视频内存中。为此,请在 VRAM 中选择一个地址,例如 0x00000000。与 CRAM 类比,我们通过命令联系 VDP 控制端口以写入 VRAM 和起始地址。
此后,您可以将长字上传到VRAM;与CRAM相比,您不需要为每个长字指定地址,因为有VRAM自动递增模式。您可以使用 VDP 寄存器标志 0x0F (dc.b $02) 启用它
lea Tiles,a0
move.l #$40200000,vdp_control_port; write to VRAM command
move.w #6136,d0 ; (767 tiles * 8 rows) counter
TilesVRAMLoop:
move.l (a0)+,vdp_data_port;
dbra d0,TilesVRAMLoop
平面 A/B 中的平铺索引
现在我们必须根据瓷砖的索引来填充屏幕。为此,VRAM 被填充到平面 A/B 地址,该地址被输入到 VDP 寄存器(0x02、0x04)中。有关棘手寻址的更多信息,请参见 Sega 的手册;在我的示例中,VRAM 地址是 0xC000,让我们将索引上传到那里。
无论如何,您的图像都会填充屏幕外的 VRAM 空间,因此在绘制屏幕空间后,您的渲染器应该停止绘制,并在光标移动到新行时再次继续。如何实现这一点有很多选择;我使用了最简单的版本,即对图像宽度计数器和光标位置计数器的两个寄存器进行计数。
代码示例:
move.w #0,d0 ; column index
move.w #1,d1 ; tile index
move.l #$40000003,(vdp_control_port) ; initial drawing location
move.l #2500,d7 ; how many tiles to draw (entire screen ~2500)
imageWidth = 31
screenWidth = 64
FillBackgroundStep:
cmp.w #imageWidth,d0
ble.w FillBackgroundStepFill
FillBackgroundStep2:
cmp.w #imageWidth,d0
bgt.w FillBackgroundStepSkip
FillBackgroundStep3:
add #1,d0
cmp.w #screenWidth,d0
bge.w FillBackgroundStepNewRow
FillBackgroundStep4:
dbra d7,FillBackgroundStep ; loop to next tile
Stuck:
nop
jmp Stuck
FillBackgroundStepNewRow:
move.w #0,d0
jmp FillBackgroundStep4
FillBackgroundStepFill:
move.w d1,(vdp_data_port) ; copy the pattern to VPD
add #1,d1
jmp FillBackgroundStep2
FillBackgroundStepSkip:
move.w #0,(vdp_data_port) ; copy the pattern to VPD
jmp FillBackgroundStep3
之后,剩下的就是使用 vasm 组装 rom,启动模拟器,然后查看图片。
调试
并不是所有事情都能立即解决,所以我想推荐以下 Exodus 模拟器工具:
- M68k 处理器调试器
- 更改 m68k 处理器周期数(针对调试器中的慢动作模式)
- 查看器 CRAM、VRAM、平面 A/B
- 仔细阅读 m68k 的文档、所使用的操作码(并非所有内容都像乍一看那么明显)
- 在 github 上查看游戏代码/反汇编示例
- 实现处理器异常的子例程并进行处理
指向处理器异常子例程的指针放置在 ROM 标头中;GitHub 上还有一个带有 Sega 交互式运行时调试器的项目,称为 genesis-debugger。
使用所有可用的工具,进行良好的老式编码,并且Blast Processing可能会与您同在!
链接
https://gitlab.com/demensdeum /segagenesisamples/-/tree/main/6Image/vasm
http://devster.monkeeh.com/sega/imagenesis/
https://github.com/flamewing/genesis-debugger
来源
https://www.chibiakumas.com/68000/helloworld .php#LessonH5
https://huguesjohnson.com/programming/genesis/tiles-sprites/
为 Sega Genesis #1 编写汇编
第一篇专门为 Motorola 68000 Assembly 中的经典 Sega Genesis 控制台编写游戏的文章。
让我们为 Sega 编写最简单的无限循环。为此,我们需要:一个汇编器、一个带有反汇编器的模拟器、一个最喜欢的文本编辑器、对 Sega rum 结构的基本了解。
对于开发,我使用自己的汇编器/反汇编器 Gen68KryBaby:
https://gitlab.com/demensdeum/gen68krybaby/ p>
该工具是用 Python 3 开发的,用于汇编时提供扩展名为 .asm 或 .gen68KryBabyDisasm 的文件作为输入,输出是扩展名为 .gen68KryBabyAsm.bin 的文件,该文件可以在模拟器或计算机上运行一个真正的控制台(小心,走开,控制台可能会爆炸!)
还支持反汇编 rom,为此您需要提交一个 rom 文件作为输入,不带 .asm 或 .gen68KryBabyDisasm 扩展名。操作码支持将根据我对该主题的兴趣和贡献者的参与而增加或减少。
结构
Sega ROM 标头占据前 512 个字节。它包含有关游戏、名称、支持的外围设备、校验和以及其他系统标志的信息。我认为如果没有标题,控制台甚至不会看朗姆酒,认为它是不正确的,并说“你在这里给我什么?”
标头之后是子例程/重置子例程,这是 m68K 处理器开始工作的地方。好吧,这是一件小事……查找操作码(操作码),即什么都不做(!)并切换到内存中地址处的子程序。通过谷歌搜索,您可以找到 NOP 操作码,它不执行任何操作,以及 JSR 操作码,它执行无条件跳转到参数地址,也就是说,它只是将回车移动到我们要求的位置,没有任何突发奇想。
将它们放在一起
ROM 的标头捐赠者是 Beta 版本中的游戏之一,目前记录为十六进制数据。
00 ff 2b 52 00 00 02 00 00 00 49 90 00 00 49 90 00 00 49 90 00...и т.д.
Код программы со-но представляет из себя объявление сабрутины Reset/EntryPoint в 512 (0x200) байте, NOP, возврат каретки к 0x00000200, таким образом мы получим бесконечный цикл.
Ассемблерный код сабрутины Reset/EntryPoint:
NOP
NOP
NOP
NOP
NOP
JSR 0x00000200
完整示例以及 rom 标头:
https://gitlab.com /demensdeum/segagenesisamples/-/blob/main/1InfiniteLoop/1infiniteloop.asm
我们接下来收集:
Запускаем ром 1infiniteloop.asm.gen68KryBabyAsm.bin в режиме дебаггера эмулятора Exodus/Gens, смотрим что m68K корректно считывает NOP, и бесконечно прыгает к EntryPoint в 0x200 на JSR

Здесь должен быть Соник показывающий V, но он уехал на Вакен.
Ссылки
https://gitlab.com/demensdeum/gen68krybaby/
https://gitlab.com/demensdeum/segagenesissamples
https://www.exodusemulator.com/downloads/release-archive
Источники
ROM Hacking Demo – Genesis and SNES games in 480i
https://www.chibiakumas.com/68000/genesis.php
https://plutiedev.com/rom-header
https://blog.bigevilcorporation.co.uk/2012/02/28/sega-megadrive-1-getting-started/
https://opensource.apple.com/source/cctools/cctools-836/as/m68k-opcode.h.auto.html