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
脱开(注释机器码的拆卸):
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
(before rep movsb
)代替,使其更简单,但没有节省。
要求调用者将指针传递到dst缓冲区di
将节省4个字节(相对于相对于计算而言)sp
。
使函数起始地址与函数大小相同将节省一个字节(mov cx, si
)。
f(4)
返回一个新函数。如果调用该新函数时不带参数,则返回4
,但如果调用另一个参数时,它将再次返回具有相同语义但将新参数添加到的新函数4
,依此类推。