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: