これは新しいことではありません。何年も前に、小規模なコード最適化コンテストを開催していました。たとえば、次のコードは、C 関数strcpy
、strncpy
、およびstpcpy
をわずか 42 バイトで実装しています。これは 1993 年に書きました。16 ビット 8086 アセンブリ言語、C 呼び出し可能、パラメーターがスタックに渡され、呼び出し元がスタックをクリーンアップします。
のエントリ ポイントがstrcpy
命令の最初のバイトであり、次の 2 バイトが AX レジスタにロードされることに注意してください。そして、db 3Ch
は別の命令の最初のバイトであり、次のバイト ( or al
)を消費し、 によって実行される命令STC
の 2 番目のバイトであるor al,0F9h
命令を実行しstrncpy
ます。
リスト ファイルを作成してオペコードを取得し、3 つのエントリ ポイントのそれぞれで何が起こっているかを追跡することは有益です。
この種のトリックは、既存のコードにパッチを適用するときに役立ちました。場合によっては、重要な部分のアドレスを変更せずに、.COM ファイルにバイナリ パッチを作成できます。これは、16 バイト (またはそれ以上) に揃える必要がある場合に重要であり、別の命令を追加できるようにするためだけに 15 バイトのデッド スペースを浪費するという打撃を受けたくありませんでした。ああ、64K バイトしかないときにプレイするゲームです。
Ideal
Model Small,c
CodeSeg
Public strcpy,strncpy,stpcpy
;
; 42 bytes
;
; char * strcpy (char *dest, char *src);
;
strcpy:
db 0B8h ;mov ax,immed
;
; char * stpcpy (char *dest, char *src);
;
stpcpy:
mov al,0Ch ;0Ch is the opcode for OR AL,immediate
mov cx,0ffffh ;make max count
db 3Ch ;cmp al,immediate
;stpcpy - CF set, ZF set
;strcpy - CF set, ZF clear
;
; char * strncpy (char *dest, char *src, unsigned len);
;
strncpy:
or al,0F9h ;strncpy - CF clear, ZF clear
;0F9h is the opcode for STC,
;which is executed by strcpy and stpcpy
pop dx ;return address in DX
pop bx ;dest string in BX
pop ax ;source string in AX
jc l0 ;if strncpy
pop cx ;then get length in CX
push cx ;and fixup stack
l0:
push ax ;more stack fixup
push bx ;save return value
push si ;gotta save SI
xchg si,ax ;SI points to source string
lahf ;save flags for exit processing
l1:
lodsb ;get character
l2:
jcxz Done ;done if count = 0
mov [bx],al ;store character
inc bx ;bump dest
or al,al ;if character is 0 or
loopnz l1 ;if at end of count, then done
sahf ;restore flags
ja l2 ;for strncpy(), must loop until count reached
Done:
pop si ;restore SI
pop ax ;return value in AX
jnz AllDone ;done if not stpcpy
xchg ax,bx ;otherwise return pointer to
dec ax ;end of string
AllDone:
call dx ;return to caller
End
これらのコンテストでいつも私を打ち負かしていた私の友人を困らせるためだけに、そのアイデアを思いつくのに何時間も費やしたことを覚えています. 彼は数分間それを見て、それから別のバイトを剃りました.