Answers:
push $10
push $34
push $s
push $34
push $37
push $37
push $s
call printf
mov $0,%ebx
mov $1,%eax
int $128
s:.ascii "push $10
push $34
push $s
push $34
push $37
push $37
push $s
call printf
mov $0,%cebx
mov $1,%ceax
int $128
s:.ascii %c%s%c%c"
(我这个编译它:gcc -nostartfiles -lc quine.S -o quine
)
.class public Q
.super java/io/File
.method public static main([Ljava/lang/String;)V
.limit stack 9
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc ".class public Q%n.super java/io/File%n.method public static main([Ljava/lang/String;)V%n.limit stack 9%ngetstatic java/lang/System/out Ljava/io/PrintStream;%nldc %c%s%c%nldc 3%nanewarray java/lang/Object%ndup%ndup%nldc 0%nldc 34%ninvokestatic java/lang/Integer/valueOf(I)Ljava/lang/Integer;%ndup_x2%naastore%nldc 2%nswap%naastore%ndup2%nswap%nldc 1%nswap%naastore%ninvokevirtual java/io/PrintStream/printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;%npop%nreturn%n.end method"
ldc 3
anewarray java/lang/Object
dup
dup
ldc 0
ldc 34
invokestatic java/lang/Integer/valueOf(I)Ljava/lang/Integer;
dup_x2
aastore
ldc 2
swap
aastore
dup2
swap
ldc 1
swap
aastore
invokevirtual java/io/PrintStream/printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
pop
return
.end method
令人遗憾的是,Jasmin不允许使用微软ilasm
允许的许多精美技巧。但是JVM总共有6条不同的dup
指令,可以执行各种有趣的事情。.NET似乎不支持对堆栈中的项目进行重新排序。
无论如何,我想我的两个条目都不是最短代码的重要竞争者,但我想很难使它们更短。因此,出于完整性考虑:-)
评论版本,其中包含有关堆栈内容的信息:
.class public Q
.super java/io/File
.method public static main([Ljava/lang/String;)V
.limit stack 9
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc ".class public Q%n.super java/io/File%n.method public static main([Ljava/lang/String;)V%n.limit stack 9%ngetstatic java/lang/System/out Ljava/io/PrintStream;%nldc %c%s%c%nldc 3%nanewarray java/lang/Object%ndup%ndup%nldc 0%nldc 34%ninvokestatic java/lang/Integer/valueOf(I)Ljava/lang/Integer;%ndup_x2%naastore%nldc 2%nswap%naastore%ndup2%nswap%nldc 1%nswap%naastore%ninvokevirtual java/io/PrintStream/printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;%npop%nreturn%n.end method"
ldc 3 ; stack; System.out, string, 3
anewarray java/lang/Object ; stack: System.out, string, Object[3]
dup
dup ; stack: System.out, string, array, array, array
ldc 0 ; stack: System.out, string, array, array, array, 0
ldc 34 ; stack: System.out, string, array, array, array, 0, 34
invokestatic java/lang/Integer/valueOf(I)Ljava/lang/Integer;
dup_x2 ; stack: System.out, string, array, array, 34, array, 0, 34
aastore ; stack: System.out, string, array, array, 34
ldc 2 ; stack: System.out, string, array, array, 34, 2
swap ; stack: System.out, string, array, array, 2, 34
aastore ; stack: System.out, string, array
dup2 ; stack: System.out, string, array, string, array
swap ; stack: System.out, string, array, array, string
ldc 1 ; stack: System.out, string, array, array, string, 1
swap ; stack: System.out, string, array, array, 1, string
aastore ; stack: System.out, string, array
invokevirtual java/io/PrintStream/printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
pop
return
.end method
历史:
ldc
比bipush
或短iconst_*
。使用A86汇编为51个字节。除了DOS Int21 AH = 9函数外,不需要任何外部库(将字符串写入stdout)。
db 185
db 51
db 0
db 190
db 0
db 1
db 191
db 47
db 1
db 172
db 178
db 10
db 199
db 6
db 45
db 1
db 32
db 32
db 180
db 0
db 246
db 242
db 128
db 196
db 48
db 136
db 37
db 79
db 10
db 192
db 117
db 242
db 180
db 9
db 186
db 42
db 1
db 205
db 33
db 226
db 221
db 195
db 100
db 98
db 32
db 32
db 51
db 49
db 10
db 13
db 36
.assembly H{}.method void M(){.entrypoint.locals init(string)ldstr".assembly H{0}{1}.method void M(){0}.entrypoint.locals init(string)ldstr{2}{3}{2}stloc 0ldloc 0ldc.i4 4newarr object dup dup dup dup ldc.i4 0ldstr{2}{0}{2}stelem.ref ldc.i4 1ldstr{2}{1}{2}stelem.ref ldc.i4 2ldc.i4 34box char stelem.ref ldc.i4 3ldloc 0stelem.ref call void[mscorlib]System.Console::Write(string,object[])ret{1}"stloc 0ldloc 0ldc.i4 4newarr object dup dup dup dup ldc.i4 0ldstr"{"stelem.ref ldc.i4 1ldstr"}"stelem.ref ldc.i4 2ldc.i4 34box char stelem.ref ldc.i4 3ldloc 0stelem.ref call void[mscorlib]System.Console::Write(string,object[])ret}
一行,末尾没有换行。
格式化并注释了第一版(即使它不再是奎因了)–我不太可能偏离一般概念:
.assembly H{}
.method void M() {
.entrypoint
.locals init (
string,
object[]
)
// the string
ldstr".assembly H{0}{1}.method void M(){0}.entrypoint.locals init(string,object[])ldstr{2}{3}{2}stloc.0 ldloc.0 ldc.i4.4 newarr object stloc.1 ldloc.1 ldc.i4.0 ldstr{2}{0}{2} stelem.ref ldloc.1 ldc.i4.1 ldstr{2}{1}{2} stelem.ref ldloc.1 ldc.i4.2 ldc.i4 34 box char stelem.ref ldloc.1 ldc.i4.3 ldloc.0 stelem.ref ldloc.1 call void[mscorlib]System.Console::Write(string,object[])ret{1}"
stloc.0 // store in first local var
ldloc.0 // load again. Going to be the first argument to Console::Write
ldc.i4.4 newarr object stloc.1 // create new array and store in local var
ldloc.1 ldc.i4.0 ldstr"{" stelem.ref // we need a literal brace
ldloc.1 ldc.i4.1 ldstr"}" stelem.ref // closing, too
ldloc.1 ldc.i4.2 ldc.i4 34 box char stelem.ref // double quote
ldloc.1 ldc.i4.3 ldloc.0 stelem.ref // our format string from before
ldloc.1 // load array
call void[mscorlib]System.Console::Write(string,object[]) // output
ret
}
历史:
dup
比ldloc.1
每次写作都要短。ldloc.1
可以编写类似的东西ldloc 1
以使最后一个标记成为文字。产生的字节码可能更大,但这是关于汇编程序代码的,所以我不在乎:-)object[]
用作局部变量;我可以直接在堆栈上完成所有操作。真好.globl main
main:movw $34,B+87
push $B
call printf
call printf
pop B
ret
.data
B:.ascii".globl main
main:movw $34,B+87
push $B
call printf
call printf
pop B
ret
.data
B:.ascii"
用构建gcc -m32 -o a.out quine.S
。(-m32
如果您的操作系统已经是32位,则为可选。)
编辑补充:如果我们修改规则,允许puts
被调用,而不是printf
那么它可以做182个 174字节:
.globl main
main:movw $34,B+86
push $B+1
call puts
call puts
pop B
ret
.data
B:.ascii"
.globl main
main:movw $34,B+86
push $B+1
call puts
call puts
pop B
ret
.data
B:.ascii"
(请注意,与前一个不同,该命令有一个换行符终止。)
printf
在堆栈上破坏其args。call
再次使用它并期望使用相同的args在技术上并不安全,但是它在实践中可行,因为gcc / clang从未将args插槽用作暂存空间,即AFAIK。
printf
从_start
(例如,在静态二进制文件中)进行调用,因此这对于编写a main
而不是a 是一个很好的论据_start
。 此答案说明了从静态或动态二进制文件链接libc的各种方法。(在Linux动态二进制文件中,动态链接程序将运行glibc的初始化函数,因此您可以printf
从_start
入口点使用它,但是cygwin IIRC并非如此。)
[bits 16]
mov ax,07C0h
mov ds,ax
mov ah,0
mov al,03h
int 10h
mov si,code
call p
jmp $
p:mov ah,0Eh
r:lodsb
cmp al,0
je d
cmp bx,0x42
jne s
c:int 10h
jmp r
s: cmp al,94
je re
cmp al,63
je q
jmp c
q:mov al,34
jmp c
re:push si
mov bx,0x42
mov si,code
call p
mov bx,0
pop si
jmp p
d:ret
code:db "[bits 16]\mov ax,07C0h\mov ds,ax\mov ah,0\mov al,03h\int 10h\mov si,code\call p\jmp $\p:mov ah,0Eh\r:lodsb\cmp al,0\je d\cmp bx,0x42\jne s\c:int 10h\jmp r\s:cmp al,94\je re\cmp al,63\je q\jmp c\q:mov al,34\jmp c\re:push si\mov bx,0x42\mov si,code\call p\mov bx,0\pop si\jmp p\\d:ret\\code:db ?^?\times 510-($-$$) db 0\dw 0xAA55"
times 510-($-$$) db 0
dw 0xAA55
.att_syntax noprefix
.globl main
main:
pushq rbp
movq rsp, rbp
mov $.Cs, rdi
mov $0xa, rsi
mov $0x22, edx
mov $.Cs, ecx
mov $0x22, r8d
mov $0xa, r9d
xor eax, eax
call printf
xor eax, eax
leave
ret
.Cs: .string ".att_syntax noprefix
.globl main
main:
pushq rbp
movq rsp, rbp
mov $.Cs, rdi
mov $0xa, rsi
mov $0x22, edx
mov $.Cs, ecx
mov $0x22, r8d
mov $0xa, r9d
xor eax, eax
call printf
xor eax, eax
leave
ret%c.Cs: .string %c%s%c%c"
xor eax,eax
如果您不在乎程序的退出状态,则根本不需要。即使它以非零状态退出,它仍然会自行打印。您也可以使用push
代替pushq
。实际上,您为什么还要制作一个堆栈框架?删除push rbp
/ mov rsp, rbp
和leave
。您也可以使用较短的标签名称。 .Cs
如果1可以,则为3个字符。
.att_syntax noprefix
可能不再为自己付出代价。 .intel_syntax noprefix
也会让您删除这六个$
前缀。但可能仍然不值得。(您可以使用它lea ecx,.Cs
代替intel语法mov ecx,offset .Cs
)
MODEL TINY
.CODE
.STARTUP
DB 177
DB 076
DB 186
DB 044
DB 001
DB 172
DB 180
DB 036
DB 179
DB 004
DB 191
DB 080
DB 001
DB 079
DB 136
DB 037
DB 212
DB 010
DB 004
DB 048
DB 134
DB 196
DB 075
DB 117
DB 244
DB 180
DB 009
DB 205
DB 033
DB 178
DB 071
DB 226
DB 228
DB 178
DB 038
DB 205
DB 033
DB 195
DB 013
DB 010
DB 069
DB 078
DB 068
DB 036
DB 077
DB 079
DB 068
DB 069
DB 076
DB 032
DB 084
DB 073
DB 078
DB 089
DB 013
DB 010
DB 046
DB 067
DB 079
DB 068
DB 069
DB 013
DB 010
DB 046
DB 083
DB 084
DB 065
DB 082
DB 084
DB 085
DB 080
DB 013
DB 010
DB 068
DB 066
DB 032
END