Assembly Language Quine

21

Écrivez le quine le plus court possible en langage assembleur .

Utilisez l'ISA de votre choix, sauf s'il contient une print-quineinstruction ou un équivalent. Les exemples incluent x86, MIPS, SPARC, MMIX, IBM BAL, MIX, VAX, JVM, ARM, etc.

Vous pouvez établir un lien avec la _printffonction de la bibliothèque standard C (ou l'équivalent Java pour le bytecode JVM) pour les E / S.

La longueur sera jugée à la fois en fonction du nombre d'instructions et de la taille du segment de données. Les solutions doivent contenir au moins deux instructions.

Le quine doit imprimer le code d' assemblage , pas le code machine assemblé.

Hoa Long Tam
la source
3
Oh wow, cela ressemble à un robuste
lâche anonyme

Réponses:

20

x86 Linux, syntaxe AT&T: 244

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"

(Je l' ai compilé avec ceci: gcc -nostartfiles -lc quine.S -o quine)

JB
la source
C'est déprimant, maintenant :-(
Joey
1
Je dirais généralement «le bon outil pour le travail», mais là encore, cela ne me semble pas correct: D
JB
Il semble que ce soit plus juste que le mien ;-)
Joey
5

Assemblage de bytecode JVM (via Jasmin ) - 952 960 990

.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

Malheureusement, Jasmin n'autorise pas autant de belles astuces que Microsoft le ilasmpermet. Mais la JVM a un total de sixdup instructions différentes qui font toutes sortes de choses amusantes. Réorganiser les éléments de la pile est quelque chose que .NET ne semble pas prendre en charge.

Dans tous les cas, je suppose qu'aucune de mes deux entrées n'est sérieuse pour le code le plus court, mais je suppose qu'il est difficile de les rendre beaucoup plus courts. Par conséquent, juste pour être complet :-)

Version commentée avec des informations sur le contenu de la pile:

.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

Histoire:

  • 2011-02-07 02:09 (990) - Première version de travail.
  • 2011-02-07 02:11 (960) - ldcest plus court que bipushou iconst_*.
  • 2011-02-07 02:30 (952) - Qui a dit que je dois hériter de java.lang.Object? Les autres noms de classe sont tellement plus courts :-)
Joey
la source
4

gas pour x86 Linux (89 octets, sept instructions)

Techniquement, c'est de la triche.

mov $4,%al
mov $1,%bl
mov $b,%ecx
mov $89,%dl
int $128
mov %bl,%al
int $128
b:.incbin"a"

Enregistrez dans un fichier nommé aet assemblez avec les commandes suivantes pour créer l'exécutable nommé a.out.

as -o a.o ; ld a.o

La directive .incbininclut un fichier textuellement à l'emplacement actuel. Si vous l'utilisez pour inclure le code source lui-même, vous obtenez un joli quine.

FUZxxl
la source
3

Format Windows .COM: 307 caractères

Assemble, en utilisant A86, à 51 octets. Ne nécessite aucune bibliothèque externe autre que la fonction DOS Int21 AH = 9 (écrire la chaîne dans 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
Skizz
la source
J'ai peur de compter 357 octets. (et votre programme affiche en fait 408) Belle mise en œuvre, cependant. Vous souhaiterez peut-être inclure une source d'assemblage non-db'd afin que les autres visualiseurs aient un aperçu direct.
JB
@JB: Je n'ai pas inclus CR \ NL. En le regardant maintenant, j'aurais vraiment dû mettre les données sur une seule ligne de base de données. Cela le rendrait plus petit.
Skizz
3

NASM, 223 octets

%define a "%define "
%define b "db "
%define c "%deftok "
%define d "a, 97, 32, 34, a, 34, 10, a, 98, 32, 34, b, 34, 10, a, 99, 32, 34, c, 34, 10, a, 100, 32, 34, d, 34, 10, c, 101, 32, 100, 10, b, 101, 10"
%deftok e d
db e

Battre la réponse acceptée!

MD XF
la source
2

.NET CIL - 623 669 691 723 727

.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}

Une seule ligne, pas de saut de ligne à la fin.

Première version formatée et commentée (même si ce n'est plus une quine) - il est peu probable que je dévie beaucoup du concept général:

.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
}

Histoire :

  • 2011-02-06 16:48 (727) - Première version de travail.
  • 2011-02-06 17:14 (723) - Je n'ai pas besoin d'espace après un littéral de chaîne.
  • 2011-02-06 17:21 (691) - dupest plus court que d'écrire à ldloc.1chaque fois.
  • 2011-02-06 17:24 (669) - Je n'ai pas besoin d'espaces après un littéral et des choses comme ça ldloc.1peuvent être écrites ldloc 1pour faire du dernier jeton un littéral. Le bytecode résultant est probablement plus grand, mais il s'agit du code assembleur, donc je m'en fiche :-)
  • 2011-02-06 17:34 (623) - Je n'ai pas besoin de la object[]comme variable locale; Je peux faire tout cela directement sur la pile. Agréable.
Joey
la source
On dirait que vous avez supprimé l'objet [] de la version non formatée, mais pas celui formaté ...
Aurel Bílý
@Aurel: En effet, comme indiqué, le formaté est la toute première version. L'idée est toujours la même, donc je ne la mettrai pas à jour.
Joey
2

gaz pour x86 Linux, 184 176 octets

.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"

Construisez avec gcc -m32 -o a.out quine.S. (Le -m32est facultatif si votre système d'exploitation est déjà 32 bits.)

Modifié pour ajouter: Si nous modifions les règles pour permettre putsd'être appelé à la place, printfcela peut être fait en 182 174 octets:

.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"

(Notez que celui-ci, contrairement au précédent, a une nouvelle ligne de fin.)

boite à pain
la source
J'apprécie la brièveté. Mais je me sens trompé par le fait qu'en plus de printf / put, vous dépendez en fait du prologue / épilogue C standard, qui n'est pas explicitement autorisé. Et à mon humble avis n'était pas censé être; mais j'ai la meilleure réponse: évidemment je suis biaisé :-)
JB
Eh bien, on pourrait soutenir que l'utilisation du prologue / épilogue C est implicitement autorisée, en raison de la mention de l'utilisation de printf (). Les fonctions libc ne se comportent pas toujours de manière fiable si vous contournez le prologue / épilogue C. En fait sur mon système, votre version ne fonctionne pas si je redirige la sortie vers un fichier, car stdout n'est vidé que dans le code C epilog. (Si nous avions plutôt utilisé write (), qui n'est qu'un wrapper autour d'un syscall, cela aurait fonctionné de toute façon.)
breadbox
Cela fait assez longtemps maintenant, mais je me souviens que l'autorisation des fonctions C était une surprise pour moi à l'époque: cela rendait le problème un peu impur. OP n'existe pas non plus depuis longtemps; il va être difficile de demander des éclaircissements maintenant.
JB
Notez que l'ABI permet printfde clobber ses arguments sur la pile. Techniquement, il n'est pas sûr de callle refaire et d'attendre les mêmes arguments, mais cela fonctionne dans la pratique car gcc / clang n'utilise jamais les emplacements d'arg comme espace de travail, AFAIK.
Peter Cordes
De plus, en général, il n'est pas sûr d'appeler printfdepuis _start(par exemple dans un binaire statique), c'est donc un bon argument pour écrire un mainau lieu d'un _start. Cette réponse explique les différentes manières de lier libc à partir de binaires statiques ou dynamiques. (Dans un binaire dynamique Linux, l'éditeur de liens dynamique exécutera les fonctions d'initialisation de la glibc, vous pouvez donc les utiliser printfdepuis le _startpoint d'entrée, mais ce n'est pas le cas sur cygwin IIRC.)
Peter Cordes
1

ASM amorçable, 660 octets

[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

À l'origine par jdiez17 , vraiment joué par le vôtre.

MD XF
la source
0

x86-64, System V AMD64 ABI, GASM: 432

.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"
lxgr
la source
1
Vous n'avez pas besoin d'espace après la virgule entre les opérandes. Et vous n'avez pas besoin xor eax,eaxdu tout si vous ne vous souciez pas du statut de sortie de votre programme. Il s'imprime toujours, même s'il se termine avec un état différent de zéro. Vous pouvez également utiliser pushau lieu de pushq. En fait, pourquoi fabriquez-vous même un cadre de pile? Déposez le push rbp/ mov rsp, rbpet leave. Vous pouvez également utiliser des noms d'étiquette plus courts. .Csest 3 caractères quand 1 serait bien.
Peter Cordes
Après cela, .att_syntax noprefixne paie probablement plus pour lui-même. .intel_syntax noprefixvous permettrait également de supprimer ces six $préfixes. mais cela n'en vaut probablement pas la peine. (Vous pouvez utiliser à la lea ecx,.Csplace de la syntaxe Intel mov ecx,offset .Cs)
Peter Cordes
0

TAL

push puts
push \100
push {push puts
push \100
push {@}
dup
strmap
invokeStk 2}
dup
strmap
invokeStk 2

Pour l'exécuter, appelez ::tcl::unsuppoted::assembleavec le code comme argument.
Tcl 8.6 uniquement.

Johannes Kuhn
la source
3
Vous devez inclure le nombre d'octets.
MD XF
0

80x86 TASM, 561 octets

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
MD XF
la source