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 #2 のアセンブリでの書き込み

    この投稿では、アセンブリ言語で将棋パレットに色を読み込む方法について説明します。
    Exodus エミュレータの最終結果は次のようになります。

    プロセスを簡単にするために、Genesis Software Manual (1989) という名前の PDF をインターネット上で見つけてください。このマニュアルにはプロセス全体が詳細に説明されています。実際、このメモはオリジナルのマニュアルの解説です。< /p>

    Sega エミュレータの VDP チップに色を書き込むには、次のことを行う必要があります。

    • TMSS 保護を無効にする
    • 正しいパラメータを VDP レジスタに書き込む
    • 希望の色を CRAM に書き込みます

    アセンブリには、vasmm68k_mot とお気に入りのテキスト エディタ (echo など) を使用します。アセンブリは次のコマンドで実行されます。

    Порты VDP

    VDP чип общается с M68K через два порта в оперативной памяти – порт контроля и порт данных.
    По сути:

    1. Через порт контроля можно выставлять значения регистрам VDP.
    2. Также порт контроля является указателем на ту часть VDP (VRAM, CRAM, VSRAM etc.) через которую передаются данные через порт данных

    Интересная информация: Сега сохранила совместимость с играми Master System, на что указывает MODE 4 из мануала разработчика, в нем VDP переключается в режим Master System.

    Объявим порты контроля и данных:

    vdp_data_port        = $C00000

    Отключить систему защиты TMSS

    Защита от нелицензионных игр TMSS имеет несколько вариантов разблокировки, например требуется чтобы до обращения к VDP в адресном регистре A1 лежала строка “SEGA”.

    MOVE.B A1,D0; Получаем версию хардвары цифрой из A1 в регистр D0 
    ANDI.B 0x0F,D0; По маске берем последние биты, чтобы ничего не сломать 
    BEQ.B SkipTmss; Если версия равна 0, скорее всего это японка или эмулятор без включенного TMSS, тогда идем в сабрутину SkipTmss 
    MOVE.L "SEGA",A1; Или записываем строку SEGA в A1 
    

    正しいパラメータを VDP レジスタに書き込みます

    そもそも、なぜ VDP レジスタに正しいパラメータを設定する必要があるのでしょうか? VDP は多くの機能を備えているため、レンダリングする前に必要な機能を使用して VDP を初期化する必要があるという考えです。そうしないと、VDP が VDP に何を求めているかを理解できなくなります。

    各レジスタは、特定の設定/動作モードを担当します。 Segov マニュアルには、24 の各レジスタのすべてのビット/フラグと、レジスタ自体の説明が記載されています。

    bigevilcorporation ブログからのコメントを含む既製のパラメーターを使用してみましょう。

    
    VDPReg0:   dc.b $14 ;  0: H interrupt on, palettes on 
    VDPReg1:   dc.b $74 ;  1: V interrupt on, display on, DMA on, Genesis mode on 
    VDPReg2:   dc.b $30 ;  2: Pattern table for Scroll Plane A at VRAM $C000 
                        ;     (bits 3-5 = bits 13-15) 
    VDPReg3:   dc.b $00 ;  3: Pattern table for Window Plane at VRAM $0000 
                        ;     (disabled) (bits 1-5 = bits 11-15) 
    VDPReg4:   dc.b $07 ;  4: Pattern table for Scroll Plane B at VRAM $E000 
                        ;     (bits 0-2 = bits 11-15) 
    VDPReg5:   dc.b $78 ;  5: Sprite table at VRAM $F000 (bits 0-6 = bits 9-15) 
    VDPReg6:   dc.b $00 ;  6: Unused 
    VDPReg7:   dc.b $00 ;  7: Background colour - bits 0-3 = colour, 
                        ;     bits 4-5 = palette 
    VDPReg8:   dc.b $00 ;  8: Unused 
    VDPReg9:   dc.b $00 ;  9: Unused 
    VDPRegA:   dc.b $FF ; 10: Frequency of Horiz. interrupt in Rasters 
                        ;     (number of lines travelled by the beam) 
    VDPRegB:   dc.b $00 ; 11: External interrupts off, V scroll fullscreen, 
                        ;     H scroll fullscreen 
    VDPRegC:   dc.b $81 ; 12: Shadows and highlights off, interlace off, 
                        ;     H40 mode (320 x 224 screen res) 
    VDPRegD:   dc.b $3F ; 13: Horiz. scroll table at VRAM $FC00 (bits 0-5) 
    VDPRegE:   dc.b $00 ; 14: Unused 
    VDPRegF:   dc.b $02 ; 15: Autoincrement 2 bytes 
    VDPReg10:  dc.b $01 ; 16: Vert. scroll 32, Horiz. scroll 64 
    VDPReg11:  dc.b $00 ; 17: Window Plane X pos 0 left 
                        ;     (pos in bits 0-4, left/right in bit 7) 
    VDPReg12:  dc.b $00 ; 18: Window Plane Y pos 0 up 
                        ;     (pos in bits 0-4, up/down in bit 7) 
    VDPReg13:  dc.b $FF ; 19: DMA length lo byte 
    VDPReg14:  dc.b $FF ; 20: DMA length hi byte 
    VDPReg15:  dc.b $00 ; 21: DMA source address lo byte 
    VDPReg16:  dc.b $00 ; 22: DMA source address mid byte 
    VDPReg17:  dc.b $80 ; 23: DMA source address hi byte, 
                        ;     memory-to-VRAM mode (bits 6-7)  
    

    OK、制御ポートに移動して、すべてのフラグを VDP レジスタに書き込みましょう。

        move.l  #VDPRegisters,a0 ; Пишем адрес таблицы параметров в A1 
        move.l  #$18,d0          ; Счетчик цикла - 24 = 18 (HEX) в D0 
        move.l  #$00008000,d1    ; Готовим команду на запись в регистр VDP по индексу 0, по мануалу - 1000 0000 0000 0000 (BIN) = 8000 (HEX) 
    
    FillInitialStateForVDPRegistersLoop: 
        move.b  (a0)+,d1         ; Записываем в D1 итоговое значение регистра VDP из таблицы параметров, на отправку в порт контроля VDP  
        move.w  d1,vdp_control_port     ; Отправляем итоговую команду + значение из D1 в порт контроля VDP 
        add.w   #$0100,d1        ; Поднимаем индекс регистра VDP на 1 (бинарное сложение +1 к индексу по мануалу Сеги) 
        dbra    d0,FillInitialStateForVDPRegistersLoop ; Уменьшаем счетчик регистров, продолжаем цикл если необходимо

    Самое сложное это прочитать мануал и понять в каком формате подаются данные на порт контроля, опытные разработчики разберутся сразу, а вот неопытные… Немного подумают и поймут, что синтаксис для записи регистров такой:

    0B100(5 бит – индекс регистра)(8 бит/байт – значение)

    0B1000001001000101 – записать в регистр VDP 2 (00010), значение флажков 01000101.

    Записать нужные цвета в CRAM

    Далее идем писать два цвета в память цветов CRAM (Color RAM). Для этого пишем в порт контроля команду на доступ к цвету по индексу 0 в CRAM и отправляем по дата порту цвет. Все!

    Пример:

        move.l  #$C0000000,vdp_control_port ; Доступ к цвету по индексу 0 в CRAM через порт контроля  
        move.w  #228,d0; Цвет в D0 
        move.w  d0,vdp_data_port; Отправляем цвет в порт данных 
    

    Exodus でエミュレータを構築して実行すると、画面がカラー 228 で塗りつぶされるはずです。

    最後のバイト 127 に基づいて、2 番目の色で塗りつぶしてみましょう。

    <コード>

      move.l  #$C07f0000,vdp_control_port ; Доступ к цвету по байту 127 в CRAM через порт контроля 
      move.w  #69,d0; Цвет в D0 
      move.w  d0,vdp_data_port; Отправляем цвет в порт данных 
    

    リンク

    https://gitlab.com/demensdeum/segagenesissamples
    https://www.exodusemulator.com/
    http://sun.hasenbraten.de/vasm/
    https://tomeko.net/online_tools/bin_to_32bit_hex.php?lang=en

    ソース

    https://namelessalgorithm.com/genesis/blog/genesis/https://plutiedev.com/vdp-commands
    https://huguesjohnson.com/programming/genesis/palettes/
    https://www.chibiakumas.com/68000/helloworld.php#LessonH5
    https://blog.bigevilcorporation.co.uk/2012/03/09/sega-megadrive-3-awaking-the-beast/

    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

    x86_64 アセンブラー + C = One Love

    このノートでは、アセンブラから C 関数を呼び出すプロセスについて説明します。
    printf(“Hello World!\n”); を呼び出してみましょう。そして exit(0);

        message: db "Hello, world!", 10, 0
    
    section .text
        extern printf
        extern exit
        global main
    
    main:
        xor	rax, rax
        mov	rdi, message    
        call printf
        xor rdi, rdi
        call exit
    

    すべては見かけよりもはるかに単純です。.rodata セクションでは静的データを記述します。この場合、行「Hello, world!」、10 は改行文字であり、null にすることも忘れません。

    コード セクションでは、外部関数 printf、stdio の出口、stdlib ライブラリを宣言し、入力関数 main も宣言します。

        extern printf
        extern exit
        global main
    

    rax 関数から戻りレジスタに 0 を渡します。mov rax, 0; を使用できます。しかし、高速化するために、xor rax、rax が使用されます。次に、文字列へのポインタを最初の引数に渡します。

    Далее вызываем внешнюю функцию Си printf:

        xor	rax, rax
        mov	rdi, message    
        call printf
        xor rdi, rdi
        call exit
    

    同様に、最初の引数に 0 を渡して exit を呼び出します。

        call exit
    

    アメリカ人はこう言います。
    誰の言うことも聞かない
    人は。そのピラフは @ Alexander Pelevin

    を食べています。

    ソース

    https://www.devdungeon. com/content/how-mix-c-and-assembly
    https://nekosecurity.com/x86-64-assembly/part-3-nasm-anatomy-syscall-passing-argument
    https://www.cs.uaf.edu/2017/fall/cs301/reference/x86_64.html

    ソースコード

    https://gitlab.com/demensdeum/assembly-playground

    Hello World x86_64 アセンブラ

    この投稿では、IDE をセットアップし、Ubuntu Linux オペレーティング システム用の x86_64 アセンブラで最初の Hello World を作成するプロセスについて説明します。
    まず、SASM IDE、nasm アセンブラをインストールしましょう。

    Далее запустим SASM и напишем Hello World:

    
    section .text
    
    main:
        mov rbp, rsp      ; for correct debugging
        mov rax, 1        ; write(
        mov rdi, 1        ;   STDOUT_FILENO,
        mov rsi, msg      ;   "Hello, world!\n",
        mov rdx, msglen   ;   sizeof("Hello, world!\n")
        syscall           ; );
    
        mov rax, 60       ; exit(
        mov rdi, 0        ;   EXIT_SUCCESS
        syscall           ; );
    
    section .rodata
        msg: db "Hello, world!"
        msglen: equ $-msg
    

    ブログから取得した Hello World コード James Fisher は、SASM でのアセンブリとデバッグに適合しています。 SASM のドキュメントには、エントリ ポイントは main という名前の関数である必要があると記載されています。そうしないと、コードのデバッグとコンパイルが正しく行われません。
    このコードでは何をしたのでしょうか?システムコール – を呼び出しました。レジスタ内の正しい引数、データ セクション内の文字列へのポインタを使用して、Linux オペレーティング システム カーネルにアクセスします。

    虫眼鏡の下

    コードをさらに詳しく見てみましょう:

    global – директива ассемблера позволяющая задавать глобальные символы со строковыми именами. Хорошая аналогия – интерфейсы заголовочных файлов языков C/C++. В данном случае мы задаем символ main для функции входа.

    section – директива ассемблера позволяющая задавать секции (сегменты) кода. Директивы section или segment равнозначны. В секции .text помещается код программы.

    Обьявляем начало функции main. В ассемблере функции называются подпрограммами (subroutine)

    Первая машинная команда mov – помещает значение из аргумента 1 в аргумент 2. В данном случае мы переносим значение регистра rbp в rsp. Из комментария можно понять что эту строку добавил SASM для упрощения отладки. Видимо это личные дела между SASM и дебаггером gdb.

    Далее посмотрим на код до сегмента данных .rodata, два вызова syscall, первый выводит строку Hello World, второй обеспечивает выход из приложения с корректным кодом 0.

    Представим себе что регистры это переменные с именами rax, rdi, rsi, rdx, r10, r8, r9. По аналогии с высокоуровневыми языками, перевернем вертикальное представление ассемблера в горизонтальное, тогда вызов syscall будет выглядеть так:

    Тогда вызов печати текста:

    Вызов exit с корректным кодом 0:

    Рассмотрим аргументы подробнее, в заголовочном файле asm/unistd_64.h находим номер функции __NR_write – 1, далее в документации смотрим аргументы для write:
    ssize_t write(int fd, const void *buf, size_t count);

    Первый аргумент – файловый дескриптор, второй – буфер с данными, третий – счетчик байт для записи в дескриптор. Ищем номер файлового дескриптора для стандартного вывода, в мануале по stdout находим код 1. Далее дело за малым, передать указатель на буфер строки Hello World из секции данных .rodata – msg, счетчик байт – msglen, передать в регистры rax, rdi, rsi, rdx корректные аргументы и вызвать syscall.

    Обозначение константных строк и длины описывается в мануале nasm: