В этом тексте я буду собирать мелкие ассемблерные задачи и их красивые решения. Вся оптимизация идёт по размеру. С виду они безобидны и бесполезны, но на самом деле прилагаемые решения иногда сделаны достаточно лихо и не каждому под силу.

Содержание



Суть

Получить все 24 перестановки байтов регистра EAX

Рекурсивная процедура. Кладёт все перестановки в массив.
        mov  	eax,?	; 11223344h
	mov  	edi,offset data

.0:	call    .2
	call    .1
	call    .1
.1:	bswap   eax
.2:	call    .4
	call    .3
.3:	bswap   eax	
	ror     eax,8
.4:	call    .5
	xchg    ah,al
.5:	stosd			; пишем результат из EAX
	ret
data:	dd	24 dup(?)	; буфер для записи результа

Отсортировать байты регистра EAX

Цикл делает 6 проходов и 5 эффективных сравнений байтов.
	mov	eax,?

	mov	cl,D0h
.1:	cmp	ah,al
	jb	.2
	xchg	ah,al
.2:	ror	eax,cl
	add	cl,8
	jnz	.1
	ror	eax,8
	ret

Разворот битов регистра EAX

Разворачивает биты регистра EAX в обратном порядке.
	mov	eax,?

	xor	ebx,ebx
	inc	ebx
.1:	rcr	eax,1        
	rcl	ebx,1
	jnc	.1
	mov	eax,ebx

Обнаружение одинаковых байт в двух регистрах

Исходные данные лежат в регистрах EAX, EDX. Флаг Z на выходе включён, если одинаковых байтов нет. Байты регистров накладываются друг на друга, во всех возможных позициях. После чего делается попытка обнаружить 0 в одном из байтов.
	mov	eax,?
	mov	edx,?

	mov	ecx,4 
	call	.1 
	jnz	.exit
	push	edx,edx 
	mov	edx,eax 
	call	.0 
	pop	edx,eax 
	jnz	.exit
.0:	mov	ecx,2 
.1:	ror	eax,8 
	; обнаружение нулевого байта в eax
	push	eax 
	xor	eax,edx 
	mov	ebx,7EFEFEFFh 
	add	ebx,eax 
	not	eax 
	xor	eax,ebx 
	test	eax,81010100h 
	pop	eax 
	loopz	.1 
.exit:	ret 

Для 64 битного процессора:

        mov     rax,?

        mov     rcx,4
        mov     rdx,rax
.1:     ror     rax,8
        push    rax
        xor     rax,rdx
        mov     rbx,7EFEFEFEFEFEFEFFh
        add     rbx,rax
        not     rax
        xor     rax,rbx
        test    rax,8101010101010100h
        pop     rax
        loopz   .1 
        ret

COUNT(A,B,C,D)==x

В регистрах EAX, EBX, ECX, EDX находятся 4 булевых массива по 32 элемента в каждом. Необходимо арифметически сложить соответствующие элементы (то есть получить 32 суммы которые могут принимать значения от 0 до 4) и в регистр EAX записать в соответствующий разряд 1, если сумма равна 1 и 0 в противном случае (сумму в явном виде получить необязательно, главное проверить что она равна 1). Например:
EAX=00011111h
EBX=00111101h
ECX=1F001100h
EDX=03001001h

Результат:
EAX=1C100010h

COUNT(A,B,C,D)==1

; R = (A^B^C^D)&((A|B)^(C|D))

xor	eax,ebx
or	ebx,eax
xor	eax,ecx
or	ecx,edx
xor	eax,edx
xor	ebx,ecx
and	eax,ebx

COUNT(A,B,C,D)==2

; R = ((A^B)|(B^C))&~(A^B^C^D)

xor	eax,ebx
xor	ebx,ecx
xor	edx,eax
xor	edx,ecx
or	eax,ebx
not	edx
and	eax,edx

COUNT(A,B,C,D)==3

; R = ((A&B)|(C&D))&(A^B^C^D)

mov	esi,eax
xor	esi,ebx
xor	esi,ecx
xor	esi,edx
and	eax,ebx
and	ecx,edx
or	eax,ecx
and	eax,esi

COUNT(A,B,C)==2

При условии что дополнительных регистров спользовать нельзя.

способ 1

xor a,b
xor a,c
or  b,c
not a
and a,b

способ 2

xor a,b
xor a,c
or  b,a
or  b,c
xor a,b

Десятичная запись числа

	mov	eax,?
    
	mov 	edi,offset .buff
	test    eax,eax
	jns     .1
	neg	eax
	mov     byte [edi],'-'
	inc     edi
.1:	push    -'0'
	xor     ecx,ecx
	mov     cl,10
.2:	cdq
	div     ecx
	push	edx
	test	eax,eax
	jnz	.2
.3:	pop	eax
	add	al,'0'
	stosb
	jnz	.3
	ret
	
.buff   db 30 rep(?)