Sega Genesis #5 のアセンブリでの書き込み

このメモでは、ジョイスティックの読み取り、スプライトの位置の変更、水平方向の反転、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 で宣言され、水平方向の反転は、skeletonhorizo​​ntalflip で宣言されます。

    ジョイパッド

    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 エミュレータを使用してスプライトを描画する方法を説明します。
    スプライトのレンダリングのプロセスは、タイルのレンダリングと非常に似ています。

    <オル>

  • カラーを CRAM にロードする
  • スプライト 8×8 の一部を VRAM にアップロードする
  • VRAM にスプライト テーブルを埋める
  • たとえば、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 を色とタイルに分けたアセンブリ データ コードに変換します
  • カラー パレットを CRAM にロードする
  • タイル/パターンを VRAM にロードする
  • VRAM のプレーン A/B アドレスにタイル インデックスをロードする
  • Blender などのお気に入りのグラフィック エディタを使用して、画像を将棋画面のサイズに縮小できます。
  • 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 エミュレータ ツールをお勧めします。

    <オル>

  • M68k プロセッサ デバッガ
  • m68k プロセッサ サイクル数の変更 (デバッガのスローモーション モードの場合)
  • ビューア CRAM、VRAM、プレーン A/B
  • m68k のドキュメントと使用されているオペコードを注意深く読んでください (すべてが一見したほど明白であるわけではありません)
  • Github でゲーム コード/逆アセンブリのサンプルを表示する
  • プロセッサ例外のサブルーチンを実装して処理する
  • プロセッサ例外サブルーチンへのポインタは、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/

    このツールは 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

    http://68k.hax.com/

    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