このメモでは、ジョイスティックの読み取り、スプライトの位置の変更、水平方向の反転、Sega Genesis エミュレータ、および場合によってはコンソール自体のプロセスについて説明します。
将棋ジョイスティックのクリックの読み取りと「イベント」の処理は、次のスキームに従って行われます。
<オル>
スケルトン スプライトを移動するには、現在の位置の変数を保存する必要があります。
RAM
ゲーム ロジック変数は 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 のメモリが利用可能です。スケルトンの位置は、skeletonXpos、skeletonYpos で宣言され、水平方向の反転は、skeletonhorizontalflip で宣言されます。
ジョイパッド
VDP と同様に、ジョイパッドの操作は 2 つのポートを介して個別に行われます。制御ポートとデータ ポート。最初のポートは 0xA10009 と 0xA10003 の共通番号です。ジョイパッドを使用する場合、興味深い機能が 1 つあります。まず、ポーリング用のボタンの組み合わせをリクエストする必要があります。次に、バス上の更新を待った後、必要なボタンの押し方を読み取ります。 C/B および D パッド ボタンの場合、これは 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 エミュレータを使用してスプライトを描画する方法を説明します。
スプライトのレンダリングのプロセスは、タイルのレンダリングと非常に似ています。
<オル>
たとえば、32×32 ピクセルの剣を持ったスケルトンのスプライトを考えてみましょう。![]()
Skeleton Guy [Animated] by Disthorn
クラム
ImaGenesis を使って、アセンブラ用の CRAM カラーと VRAM パターンに変換してみましょう。この後、asm形式のファイルを2つ取得し、色をワードサイズに書き換え、タイルを正しい順序で配置して描画する必要があります
。興味深い情報: 0xF レジスタを介して VDP 自動インクリメントをワード サイズに切り替えることができます。これにより、CRAM カラー フィル コードからアドレス インクリメントが削除されます。
VRAM
将棋マニュアルには、大きなスプライトに対する正しい牌の順序が記載されていますが、私たちはより賢いので、ブログからインデックスを取得します。ちび悪魔、インデックス 0 から数え始めましょう:
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15
なぜすべてが逆さまなのですか?どうしたものか、コンソールは日本語です!右から左でも構いません
よ!asm スプライト ファイル内の順序を手動で変更してみましょう:
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 は目的のアドレスのビット位置です。最初の 2 ビットは、要求されているコマンドのタイプです (例: 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 変換
画像を変換するには、ImaGenesis ツールを使用できます。wine で作業するには、Visual Basic 6 ライブラリが必要です。これらのライブラリは、 winetricks (winetricks vb6run) を使用してインストールするか、インターネットから RICHTX32.OCX をダウンロードして配置できます。正しく動作するためにアプリケーション フォルダーを参照してください。
ImaGenesis では、4 ビット カラーを選択し、カラーとタイルを 2 つのアセンブラ形式ファイルにエクスポートする必要があります。次に、色を含むファイルで、各色を単語 (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 の色
CRAM へのロードは、制御ポート (vdp 制御) への特定の 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 と同様に、VRAM に書き込むコマンドと開始アドレスを使用して VDP 制御ポートに接続します。
この後、ロングワードを VRAM にアップロードできます。VRAM 自動インクリメント モードがあるため、CRAM とは異なり、各ロングワードにアドレスを指定する必要がありません。 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 のタイル インデックス
次に、インデックスに従って画面をタイルで埋める必要があります。これを行うには、VDP レジスタ (0x02、0x04) に入力されるプレーン A/B アドレスで VRAM が埋められます。難しいアドレス指定の詳細については、Sega のマニュアルを参照してください。この例では、VRAM アドレスは 0xC000 です。そこにインデックスをアップロードしましょう。
いずれにせよ、画像はオフスクリーン VRAM スペースを埋めるため、スクリーンスペースを描画した後、レンダラーは描画を停止し、カーソルが新しい行に移動したときに再び続行する必要があります。これを実装する方法には多くのオプションがあります。私は、画像幅カウンタとカーソル位置カウンタの 2 つのレジスタをカウントする最も単純なバージョンを使用しました。
コード例:
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 エミュレータ ツールをお勧めします。
<オル>
プロセッサ例外サブルーチンへのポインタは、ROM ヘッダーに配置されます。GitHub には、genesis-debugger と呼ばれる、Sega 用の対話型ランタイム デバッガーを備えたプロジェクトもあります。
利用可能なツールをすべて使用し、昔ながらの優れたコーディングを行い、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#レッスンH5
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 の逆アセンブルもサポートされており、そのためには、.asm または .gen68KryBabyDisasm 拡張子を付けずに、ROM ファイルを入力として送信する必要があります。オペコードのサポートは、トピックに対する私の関心と貢献者の参加に応じて増減します。
構造
Sega rom ヘッダーは最初の 512 バイトを占めます。これには、ゲーム、名前、サポートされている周辺機器、チェックサム、およびその他のシステム フラグに関する情報が含まれています。タイトルがなければ、コンソールはラム酒を確認することさえせず、それが間違っていると判断し、「ここで何をくれるのですか?」と言うと思います。
ヘッダーの後にサブルーチン/リセット サブルーチンが続き、ここで m68K プロセッサが作業を開始します。さて、それは小さな問題です –オペコード (オペレーション コード) を見つけます。つまり、何もせず (!)、メモリ内のアドレスのサブルーチンに切り替えます。グーグルで検索すると、何もしない NOP オペコードと、引数アドレスへの無条件ジャンプを実行する JSR オペコードを見つけることができます。つまり、何の気まぐれもせずに、要求した場所にキャリッジを移動するだけです。
すべてをまとめる
ROM のヘッダードナーはベータ版のゲームの 1 つであり、現在は 16 進データとして記録されています。
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