いくつかのハッキングといじりの後、これを機能させることができました。思ったほど簡単ではありませんので、席を立ってお待ちください。
まず、DOS がシングルユーザーの非マルチタスク システムであることを (抽象的に聞こえるかもしれませんが) 理解する必要があります。この特定のケースでは、2 つのプロセスを同時に実行できないことを意味します。別のプロセスに移動する前に、1 つのプロセスが実行を終了するのを待つ必要があります。プロセスの同時実行性は、TSR (Terminate and Stay Resident) プロセスである程度エミュレートされる場合があります。TSR プロセスは終了してもメモリ内に留まり、コードからいくつかの割り込みをフックし、後で他のコードから呼び出すことで実行を再開できます。それでも、Windows や Linux などの最新の OS で使用されているのと同じ種類の同時実行ではありません。しかし、それは重要ではありませんでした。
NASM を選択したアセンブラーとして使用しているとのことでしたので、コードを COM ファイルに出力し、それが DOS コマンド プロンプトによって実行されると仮定しました。COM ファイルはコマンド プロンプトによってオフセットで読み込まれ100h
(その場所へのジャンプが実行された後)、「無駄のない」コードとデータ以外は何も含まれていません。つまり、ヘッダーがないため、最も簡単に作成できます。
アセンブリのソースを細かく説明して、(おそらく) 内部で何が起こっているかをよりよく理解できるようにします。
で始まるプログラム
org 100h
section .data
exename db "C:\hello.com",0
exename2 db "C:\nasm\nasm.exe",0
cmdline db 0,0dh
実際にメモリにロードされたときにファイルのオリジンを指定するorg
ディレクティブ - この場合、これは100h
. 3 つのラベルの宣言が続きます。これらは、実行するプログラムのヌル終了パスでありexename
、新しく作成されたプロセスが受け取るコマンド ラインを指定する です。これは単なる通常の文字列ではないことに注意してください。最初のバイトはコマンドラインの文字数、次にコマンドライン自体、およびキャリッジ リターンです。この場合、コマンドライン パラメーターがないため、全体は. params として渡したいとします。その場合、このラベルを次のように宣言する必要があります(先頭の余分なスペースに注意してください!)。先に進む...exename2
cmdline
db 0,0dh
-h -x 3
db 8," -h -x 3",0dh
dummy times 20 db 0
paramblock dw 0
dw cmdline
dw 0 ; cmdline_seg
dw dummy ; fcb1
dw 0 ; fcb1_seg
dw dummy ; fcb2
dw 0 ; fcb2_seg
ラベルdummy
はゼロを含むわずか 20 バイトです。以下は、paramblock
Daniel Roethlisberger によって言及された EXEC 構造の表現であるラベルです。最初の項目はゼロです。これは、新しいプロセスがその親プロセスと同じ環境を持つ必要があることを意味します。コマンドライン、最初の FCB、2 番目の FCB の 3 つのアドレスが続きます。リアル モードのアドレスは、セグメントのアドレスとセグメントへのオフセットの 2 つの部分で構成されることに注意してください。これらのアドレスはどちらも 16 ビット長です。これらは、オフセットが最初になるリトルエンディアン形式でメモリに書き込まれます。したがって、コマンドラインをオフセットとして指定しcmdline
、FCB のアドレスをラベルへのオフセットとして指定します。dummy
FCB自体は使用されませんが、アドレスは有効なメモリ位置を指している必要があるためです。ローダーは COM ファイルがロードされるセグメントを選択するため、実行時にセグメントを埋める必要があります。
section .text
entry:
mov ax, cs
mov [paramblock+4], ax
mov [paramblock+8], ax
mov [paramblock+12],ax
paramblock
プログラムは、構造体にセグメント フィールドを設定することから始めます。COM ファイルの場合、つまりすべてのセグメントが同じであるため、これらの値をレジスタCS = DS = ES = SS
にあるものに設定するだけです。cs
mov ax, 4a00h
mov bx, 50
int 21h
これは実際には、アプリケーションの最もトリッキーなポイントの 1 つです。COM ファイルが DOS によってメモリにロードされると、デフォルトで使用可能なすべてのメモリが割り当てられます (リアル モードであるため、CPU はこれを認識しませんが、DOS 内部はそれを追跡します)。したがって、EXEC syscall を呼び出すと、 で失敗しNo memory available
ます。AH=4Ah
したがって、"RESIZE MEMORY BLOCK"呼び出しを実行して、そのすべてのメモリが実際には必要ないことを DOS に伝える必要があります(Ralf Brown)。のbx
register は、メモリ ブロックの新しいサイズを 16 バイト単位 (「段落」) で持つことになっているため、50 に設定し、プログラムには 800 バイトを使用します。この値がランダムに選択されたことを認めなければなりません。意味のある値 (たとえば、実際のファイル サイズに基づく値) に設定しようとしましたが、どこにも行き着きませんでした。ES
は、「サイズ変更」したいセグメントです。この場合はCS
(または、COM ファイルがロードされたときにすべて同じであるため、その他のセグメントです)。この呼び出しが完了すると、新しいプログラムをメモリにロードして実行する準備が整います。
mov ax, 0100h
int 21h
cmp al, '1'
je .prog1
cmp al, '2'
je .prog2
jmp .end
.prog1:
mov dx, exename
jmp .exec
.prog2:
mov dx, exename2
DX
このコードは一目瞭然で、標準入力に基づいて挿入されたプログラムへのパスを選択します。
.exec:
mov bx, paramblock
mov ax, 4b00h
int 21h
これは、実際のEXEC
syscall ( AH=4Bh
) が呼び出される場所です。AL
これは、プログラムをロードして実行する必要があることを意味します。DS:DX
実行可能ファイルへのパスのアドレス (前のコード部分によって選択された)が含まれ、構造を含むラベルES:BX
のアドレスが含まれます。paramblock
EXEC
.end:
mov ax, 4c00h
int 21h
によって呼び出されたプログラムの実行が終了exec
した後、親プログラムはAH=4Ch
syscall の実行によって終了コード 0 で終了します。
vulture-
Freenode の from ##asm の助けに感謝します。私はこれを DOSBox と MS-DOS 6.22 でテストしました。