あなたの仕事は、最大32バイトのコードを使用し、ゼロ化されたレジスターから始めて、可能な限り多くの命令を実行して停止するx86マシン言語(任意のバージョン)でプログラムを書くことです。
x86マシンで使用可能な形式である限り、ランダムアクセスメモリについては何でも想定できます。
あなたの仕事は、最大32バイトのコードを使用し、ゼロ化されたレジスターから始めて、可能な限り多くの命令を実行して停止するx86マシン言語(任意のバージョン)でプログラムを書くことです。
x86マシンで使用可能な形式である限り、ランダムアクセスメモリについては何でも想定できます。
回答:
私のプログラム:
_start:
mov bl, _end
stc
iloop: adc [bx], al
inc bx
jnz iloop
jnc _start
a32
loop _start
hlt
_end:
(テクニカルノート:これはNASMのために書かれている。a32
代替アドレスサイズプレフィックスバイト用のnasmの構文ですMASMのために、あなたが代わる。a32
とdefb 0x67
。)
わかりやすくするために、リストの出力を次に示します。
1 _start:
2 0000 B310 mov bl, _end
3 0002 F9 stc
4 0003 1007 iloop: adc [bx], al
5 0005 43 inc bx
6 0006 75F9 jnz iloop
7 0008 73F4 jnc _start
8 000A 67 a32
9 000B E2F1 loop _start
10 000D F4 hlt
11 _end:
プログラムは、プロセッサがリアルモードであり、プログラムがメモリの64kセグメントの下部にあることを前提としています。メモリセグメントは、すべてのビットがゼロに初期化されます。その設計は単純です。メモリを単一の巨大な符号なし整数として扱い、すべてのゼロにロールバックされるまで、考えられるすべての値をインクリメントします。この2を32回繰り返します。その後、停止します。
最も内側のループ(4〜6行目)は、巨大な整数をインクリメントします。前のバイトのキャリーがあったかどうかに応じて、各反復で1バイトに0または1が追加されます。このループは、常に2反復するように巨大な整数ですべてのバイトは、それが変化したか否か、アクセスされることに注意してください16 14回- 。
ちなみに、ご参考までに、このコードは、x86 inc
/ dec
命令がキャリーフラグに影響を与えない理由を示しています。このようなマルチバイトのキャリーパターンを単純化するためです。(このパターンは、元の8080命令セットが定義された8ビットマイクロプロセッサの時代に、より頻繁に登場しました。)
行7では、最後のバイトから1が実行されるまでインクリメントプロセスが繰り返され、巨大な整数がすべてのビットがゼロにリセットされたことを示します。これには時間がかかります。
行8から9は、最も外側のループを示し、レジスターがゼロになるまで、このプロセスを2から32回繰り返しecx
ます。これは、巨大な整数に32ビットを追加することと実質的に同じです。
別の外側のループを追加して(言う)を使用して、もう一度この操作を行うことが可能であろうedx
レジスタが、その後、多分使用esi
し、edi
さらに多くの繰り返しのために。ただし、実行する価値はありません。インクリメントおよびループする命令には4バイトが必要です。これらの4バイトは、巨大な整数から取り除かれます。したがって、レジスタを介して32ビットを追加するために、RAMカウンターで32ビットを失います。唯一の理由ecx
は例外で、loop
3バイトにしか収まらない特殊な命令があるためです。したがって、このプログラムは24ビットを32ビットと交換しますが、これはわずかですが8ビットの正のゲインです。
プログラムが停止する前に実行する命令の数を直接計算することはそれほど難しくありません。ただし、この数を見積もるはるかに簡単な方法があります。プログラムは、プログラムを含む14バイトを除くすべてのメモリと、bx
およびecx
レジスタを変更します。これは、2加算16 524224ビットの合計、14 + 2 + 4 = 65528バイト- 。ショートカットには、実行中に、524224ビットのすべての可能なパターンが1回だけ現れることを認識することが含まれます。RAMとecx
レジスタの場合、プログラムはすべての値をインクリメントするため、これは簡単に確認できます。ためにbx
メモリ内の値が更新されると同時に変更されるため、これは少し明白ではありません。ただし、プログラムの構造を考えると、完全なビットパターンが実際に2回出現する場合、プログラムは無限ループにある必要があることを示すことができます。これはそうではないので、各ビットパターンは最終的に一度だけ訪問されなければなりません。(当然、完全な証明は演習として読者に残されます。)
すべての可能なビットパターンがプログラムの過程で現れるため、プログラムは少なくとも2つの524224命令を実行する必要があります。これは、およそ1.4×10 157807に等しいです。(ジャンプ命令のため、実際の数値はわずかに高くなりますが、この大きさの違いは無視できます。)
明らかに、これは64k以上のRAMを使用することで大幅に改善できます。アクセスできるRAMの量が正確にわかるまで、次のバージョンのコードを待ちます。
(at&t構文)
start:
movq $0x1a,%rax
stc
loop:
adcb %cl,(%rax)
incq %rax
jnz loop
jnc start
hlt
逆アセンブル、26バイト:
start:
0000000000000000 movq $0x0000001a,%rax
0000000000000007 stc
loop:
0000000000000008 adcb %cl,(%rax)
000000000000000a incq %rax
000000000000000d jne loop
0000000000000013 jae start
0000000000000019 hlt
ブレッドボックスのソリューションに似ていますが、16ビットのアドレスで停止する理由はありません。私のコードはx86-64を使用し、2 ^ 64バイトのメモリがあることを想定しており、コードを保持するメモリ以外のすべてを巨大なカウンターとして使用します。