1

リアル モード アドレッシングについて、私が誤解している基本的なことがあるはずです。リアル モードで BIOS 割り込みを介してテキストを印刷する機能を設定しようとしています。DOSBox で実行される .com ファイルを使用してコードをテストしています。.text セクションは 0x1000 (.com ファイルでは 0x0F00) で終わります。それで、そのテキストの最初の文字を印刷したいとしましょう。

xor ebx, ebx
mov ecx, 1
mov ah, 10
mov al, ds:[0x1000]
int 0x10

私には想像力がないので、それは機能し、「H」を出力します。しかし、同じ文字を印刷したくありません。ポインターを渡したいのですが、さらにテキストを出力するときにそのポインターをインクリメントしたいと考えています。この段階では、レジスタからオフセットを読み取るだけで十分です。そこで、以下の変更を行います。

mov edx, 0x1000
mov al, ds:[edx]

そして、文字は印刷されません。esi および edi レジスタを使用してみましたが、同じ結果です。lea edx を使用すると、バイト ptr [0x1000] は同じ結果になります。さらに悪いことに、同等の 16 ビット (dx、si、di) を使用しようとすると、プログラムがハングアップします。.com ファイル内のマシン コードを調べてみましたが、明らかに間違っているものは見つかりませんでした。

カスタム リンカー スクリプトと objcopy 呼び出しを使用して gcc でコードをコンパイルし、.com ファイルを作成しています。ライブラリはリンクされておらず、ターゲット アーキテクチャは 386 です。

どんな助けでも大歓迎です。

編集:完全なリスト。

方向

.intel_syntax
.global _printChar

_printChar:
    push ebp;
    mov ebp, esp;

    xor edx, edx;
    xor ebx, ebx;
    xor eax, eax;
    mov ecx, 1;

    mov ah, 10;
    mov edx, 0x1000;
    mov al, ds:[edx];
    int 0x10;

    mov esp, ebp
    pop ebp;
    ret;

dirTest.c

asm
(
    ".code16gcc;\n" \
    "call _dosmain;\n" \
    "mov ah, 0x4C;\n" \
    "int 0x21;\n"
);

#include "directio.h"

int dosmain(void)
{
    printChar("Hello World!");
    return 0;
}

com_mingw.ld

SECTIONS
{
    . = 0x0100;
    .text :
    {
        *(.text);
    }
    .data :
    {
        *(.data);
        *(.bss);
        *(.rodata);
    }
    _heap = ALIGN(4);
}

これらはすべて、次のコマンド ラインでコンパイルされます。

gcc -std=gnu99 -Os -nostdlib -m32 -masm=intel -march=i386 -ffreestanding -o dirTest.com -Wl,--nmagic,--script=com_mingw.ld dirTest.c directio.s

に続く

objcopy dirTest.com -O binary
4

2 に答える 2

1

_printChar16 ビット関数であるべきなので、32 ビットとしてアセンブルしないでください。.s ファイルの先頭に a を追加し、.code16gcc32 ビット レジスタを 16 ビットに変更します。

.code16gcc
.intel_syntax
.global _printChar

_printChar:
    push bp;
    mov bp, sp

    xor dx, dx
    xor bx, bx
    xor ax, ax
    mov cx, 1

    mov ah, 10
    mov dx, 0x1000
    mov al, ds:[dx]     ; ERROR! See comments.
    int 0x10

    mov sp, bp
    pop bp
    ret

これで、(うまくいけば) 動作するはずです。

于 2015-10-05T07:43:46.850 に答える
-1

最後にリアル モードのコードを.comイメージ形式で書いてから約 30 年経ったに違いありませんが、いくつかの観察結果を以下に示します。

  • .comフォーマット コードを記述するときに、複数のセクションを定義することは通常ありません。この形式では、最大初期画像サイズが 64Kb の1 つのセグメントのみが許可されます。通常、これは「CODE」と「DATA」、または「TEXT」と「DATA」と呼ばれることが多い論理セクションで構成されますが、これらは単一の物理セグメントにグループ化する必要があります。「STACK」クラスの明示的なセグメントは禁止されています。
  • MS-DOS は 16 ビット オペレーティング システムであるため、16 ビット レジスタ (またはその 8 ビット サブレジスタ)のみを使用してコードを記述する必要があります。
  • The 16-bit processors did not allow you to use any registers, other than BX, SI, DI, and BP, as base registers for indirect memory addressing; you cannot use DX, (as you do), CX, or AX for this purpose; (you likely can use SP, but you normally leave that alone, reserving it for its intended use as the stack pointer).
  • オペレーティング システムがフォーマット プロセス イメージをロードするとき、.com最初に環境ブロックを割り当て、次にプログラム セグメントを割り当てます。次に、プログラム セグメントの最初の 256 バイトが管理データで埋められ (一般にプログラム セグメント プレフィックス (PSP) と呼ばれるものが作成されます)、その.com直後にイメージがアドレス 0x100 (0x1000、あなたがほのめかすのは、プログラムセグメント内の任意のオフセットであり、(おそらく)データが定義されていますが、それについて神聖なものは何もありません-プログラムコードはそこに簡単に表示できます。
  • イメージがロードされると、オペレーティング システムは 4 つのセグメント レジスタすべてをプログラム セグメントの先頭に設定し、CS:IP を設定して (FAR JMP または FAR CALL を実行することにより)、プロセスの実行を開始します。プログラムセグメント内のオフセット0x100(0x1000データアドレスとまったく関係ありません)に常にある適切なエントリアドレス。
于 2015-10-05T09:54:17.837 に答える