8086マシンコード、27バイト
00000000 bb 00 00 85 c0 74 13 01 d8 be 00 01 89 e7 47 47 |.....t........GG|
00000010 57 b9 1b 00 f3 a4 5b 89 47 01 c3 |W.....[.G..|
0000001b
このマシンコードはアドレス0x100にある必要があり、小さなコードモデル(cs = ds = es = ss)を想定しています。ただし、余分なバイトを消費せずに関数の場所を変更できます。オフセットに配置0
すると、(xor si,si
ではなくmov si, 0x100
)バイトが節約されます
必要な呼び出し規約
これは、呼び出し元がスタック上に少なくとも27バイトを事前に割り当てていることを前提としています。で数値を受け取り、でax
関数ポインタを返しますbx
。でこのポインターを呼び出すとax=0
、チェーンが終了し、で合計が返されbx
ます。
したがって、最初の呼び出しでは:
mov bp, sp
sub sp, 28
mov ax, number_to_add
call function
; new function pointer in bx
次に、後続の各呼び出しに対して:
sub sp, 28
mov ax, number_to_add
call bx
; new function pointer in bx
終了します:
mov ax, 0
call bx
; result in bx
mov sp, bp
Ungolfed(マシンコードのコメント付き分解):
00000000 BB0000 mov bx,0x0 ; 0 is replaced after copying
00000003 85C0 test ax,ax
00000005 7413 jz 0x1a ; if(ax==0) ret (with value in bx)
00000007 01D8 add ax,bx ; arg += total
00000009 BE0001 mov si,0x100 ; address of the original: ds:0x100
0000000C 89E7 mov di,sp
0000000E 47 inc di
0000000F 47 inc di ; dst = sp+2 = above return address
00000010 57 push di
00000011 B91B00 mov cx,0x1b
00000014 F3A4 rep movsb ; copy the function code.
00000016 5B pop bx ; bx = start of copy destination
00000017 894701 mov [bx+0x1],ax ; update total in the copied code
0000001A C3 ret ; with bx = function pointer
ゼロ以外のAXでこれを呼び出した後、bx = sp
からのマシンコードの変更されたコピーがバッファに書き込まれfunction
ます 最初の命令の16ビット即値が合計を保持します。(の前の最後の命令で書かれていret
ます。)
push di
/ pop bx
をmov bx, di
(の前にrep movsb
)に置き換えると、よりシンプルになりますが、節約にはなりません。
呼び出し側がdstバッファーへのポインターを渡すように要求すると、di
4バイトを節約しsp
ます。
関数の開始アドレスを関数のサイズと同じにすると、バイトが節約されます(mov cx, si
)。
f(4)
は新しい関数を返すことを意味します。その新しい関数が引数なしで呼び出され4
た場合、を返しますが、別の引数で呼び出された場合は、同じセマンティクスで新しい引数が追加されたなどの新しい関数を再び返し4
ます。