Validação do Dígito Verificador Módulo 11 Febraban

/*
 * Programa para validar o dígito verificador (DV ou DAC) módulo 11.
 *
 * O DAC (Dígito de Auto-Conferência) módulo 11 de um número é calculado
 * multiplicando cada algarismo do número pela seqüência de pesos
 * 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4.... posicionados da direita para a
 * esquerda. A soma dos resultados dessa multiplicação é dividida por 11,
 * obtém-se o resto da divisão, este resto deve ser subtraído de 11,
 * o resultado da subtração é o DAC. Observação: Quando o resto da divisão
 * for igual a 0 ou 1, atribuí-se ao DAC o digito “0”, e quando for 10,
 * atribuí-se ao DAC o digito “1”. (Padrão FEBRABAN)
 *
 * Este programa pode ser utilizado para validar o NIF (Número de
 * Identificação Fiscal) de Portugal.
 *
 * Recebe como argumento o número com o DV na última posição.
 * Retorna 1 (false) se o dígito verificador estiver incorreto.
 * Retorna 0 (true) se o dígito verificador estiver correto.
 *
 * Referência:
 * https://www.bb.com.br/docs/pub/emp/mpe/dwn/PadraoCodigoBarras.pdf
 */

// Raspberry Pi 3 Modelo A+
.cpu	cortex-a53
.fpu	neon-fp-armv8
.syntax	unified 				// Sintaxe moderna

// Equivalências

.equ	TRUE, 0
.equ	FALSE, 1
.equ	SYS_EXIT, 1
.equ	SYS_WRITE, 4
.equ	STDIN, 0
.equ	STDOUT, 1
.equ	STDERR, 2

// Dados

.data

.align 4
txt_erro:	.string	"Erro: Informe o número para calcular o DV.\n"
.equ		TAM_ERRO, (. - txt_erro)	// Tamanho da mensagem

.align 4
txt_ndig:	.string	"Erro: Algum caractere não é um dígito de 0 a 9.\n"
.equ		TAM_NDIG, (. - txt_ndig)	// Tamanho da mensagem

// Código

.text
.global _start

msg_erro:			// Escreve a mensagem de erro na saída padrão
	LDR	R7, =SYS_WRITE	// Número da chamada do sistema em R7 (escrever)
	LDR	R0, =STDERR	// Descritor do arquivo em R0 (saída de erro padrão, 2)
	LDR	R1, =txt_erro	// R1 -> texto da mensagem de erro
	MOV	R2, #TAM_ERRO	// Tamanho da string da mensagem de erro em R2
	SVC	0		// Chamar o sistema operacional
	MOV	R0, #FALSE	// Código de retorno = 1
	MOV	PC, LR		// Retornar: PC (Program Counter) = LR (Link Register)

msg_ndig:			// Escreve a mensagem de erro na saída padrão
	LDR	R7, =SYS_WRITE	// Número da chamada do sistema em R7 (escrever)
	LDR	R0, =STDERR	// Descritor do arquivo em R0 (saída de erro padrão, 2)
	LDR	R1, =txt_ndig	// R1 -> texto da mensagem de erro
	MOV	R2, #TAM_NDIG	// Tamanho da string da mensagem de erro em R2
	SVC	0		// Chamar o sistema operacional
	MOV	R0, #FALSE	// Código de retorno = 1
	B	end		// Terminar a execução do programa

strsize:			// Calcula o tamanho da string e retorna em R2
	MOV	R4, R0		// R4 aponta para o início da string apontada por R0
	MOV	R2, #0		// Inicializa o tamanho da string com 0
inc:	LDRB	R3,[R4], #1	// Carrega o byte apontado por R4 em R3; R4 += 1
	CMP	R3, #0		// Compara o valor em R3 com 0 (fim da string)
	BEQ	endsiz		// Se for igual sai do loop e retorna
	ADD	R2, #1		// Senão incrementa o tamanho da string em R2
	B	inc		// e continua o loop
endsiz:	MOV	PC, LR		// Retornar: PC (Program Counter) = LR (Link Register)

_start:

	LDR	R0, [SP]	// Número de argumentos (argc)
	CMP	R0, #1		// Número de argumentos = 1 ? (só o nome do programa?)
	BGT	argv1		// Se for maior continuar a execução do programa
	BL	msg_erro	// Se não for maior mostrar a mensagem de erro
	B	end		// e terminar o programa

argv1:
	LDR	R0, [SP, #8]	// argv[1] -> string contendo o número
	BL	strsize		// R2 = Tamanho do número
	ADD	R1, R0, R2	// R1 -> Final da string contendo o número
	SUB	R1, #1		// R1 -> DV (último caractere da string sem o 0)
	LDRB	R10,[R1]	// R10 = Dígito Verificador
	SUB	R1, #1		// R1 -> Penúltimo caractere da string

calcular_dv:			// Calcular o dígito verificador módulo 11
	MOV	R4, #2		// R4 = Peso inicial (2,3,4,5,6,...)
	MOV	R6, #0		// R6 = Somatório
somatorio:			// Somatório do dígito x peso
	CMP	R0, R1		// Comparar posição atual com início da string
	BGT	fim_somatorio	// Se início da string > posição atual terminar
	LDRB	R3,[R1]		// Carregar o caractere apontado por R1 em R3
	CMP	R3, #48		// Comparar o caractere com "0"
	BLT	msg_ndig	// Se for menor mostrar mensagem de erro e terminar
	CMP	R3, #57		// Comparar o caractere com "9"
	BGT	msg_ndig	// Se for maior mostrar mensagem de erro e terminar
	SUB	R3, R3, #48	// R3 = Valor numérico do dígito
	MUL	R5, R4, R3	// R5 = Dígito x Peso
	ADD	R6, R6, R5	// R6 += Dígito x Peso
				// Próximo peso (2,3,4,5,6,7,8,9,2,3,4,...)
	ADD	R4, #1		// Somar 1 ao peso
	CMP	R4, #9		// Comparar o peso com o valor limite (9)
	BLE	prox_digito	// Se não for maior que nove manter
	MOV	R4, #2		// Se for maior que nove volta a ser dois
prox_digito:			// Próximo dígito do número
	SUB	R1, R1, #1	// Posição atual mais próxima do início da string
	B	somatorio	// Continuar a calcular o somatório
fim_somatorio:			// Calcular o dígito verificador a partir do somatório
	MOV	R7, #11		// R7 = D = Denominador = 11
	UDIV	R8, R6, R7	// R8 = Q = Quociente = Soma / Denominador
	MUL	R9, R8, R7	// R9 = Denominador x Quociente (D x Q)
	SUB	R9, R6, R9	// R9 = Resto = R = Soma - (D × Q)
	SUB	R9, R7, R9	// R9 = DV = 11 - Resto
	CMP	R9, #10		// Comparar Resto com 10
	BLT	fim_calcular_dv	// Se Resto for menor que 10 então DV = Resto
	MOV	R9, #0		// Senão DV = 0
fim_calcular_dv:		// Mostrar o dígito verificador
	MOV	R0, #TRUE	// Código de retorno = TRUE
	ADD	R7, R9, #48	// R7 = DV convertido em caractere
	CMP	R7, R10		// DV calculado = DV informado ?
	BEQ	end		// Terminar a execução
	MOV	R0, #FALSE	// Código de retorno = FALSE

end:

	MOV	R7, #SYS_EXIT	// Número da chamada do sistema em R7 (terminar)
	SVC	0		// Chamar o sistema operacional

Makefile:

dv11chk: dv11chk.o
	ld -o dv11chk dv11chk.o
dv11chk.o: dv11chk.s
	as -o dv11chk.o dv11chk.s
clean:
	rm dv11chk.o dv11chk

Construção e execução:

pi@raspberrypi:~/assembly/dv11chk $ make clean
rm dv11chk.o dv11chk
pi@raspberrypi:~/assembly/dv11chk $ make
as -o dv11chk.o dv11chk.s
ld -o dv11chk dv11chk.o
pi@raspberrypi:~/assembly/dv11chk $ ./dv11chk 012300678960 ; test $? -eq 0 && echo True || echo false
True
pi@raspberrypi:~/assembly/dv11chk $ ./teste.sh 
*** NIF Portugal ***
(da página https://www.nif.pt/)
true
true
...
true
true
*** Febraban Brasil ***
(da página https://www.bb.com.br/docs/pub/emp/mpe/dwn/PadraoCodigoBarras.pdf)
true
true
pi@raspberrypi:~/assembly/dv11chk $ true ; echo $?
0
pi@raspberrypi:~/assembly/dv11chk $ false ; echo $?
1

Programa de teste:

#!/bin/bash
# @summary:: Teste do programa dvd11chk
# @since: 2019-07-10
# @author: Halley Pacheco de Oliveira
#
# Todos os testes devem produzir como resultado true
#
echo "*** NIF Portugal ***"
echo "(da página https://www.nif.pt/)"
./dv11chk 505208113 ; test $? -eq 0 && echo true || echo false
./dv11chk 508570654 ; test $? -eq 0 && echo true || echo false
./dv11chk 505781034 ; test $? -eq 0 && echo true || echo false
./dv11chk 504550632 ; test $? -eq 0 && echo true || echo false
./dv11chk 505674840 ; test $? -eq 0 && echo true || echo false
./dv11chk 509414125 ; test $? -eq 0 && echo true || echo false
./dv11chk 502687827 ; test $? -eq 0 && echo true || echo false
./dv11chk 502334967 ; test $? -eq 0 && echo true || echo false
./dv11chk 507355075 ; test $? -eq 0 && echo true || echo false
./dv11chk 501974610 ; test $? -eq 0 && echo true || echo false
./dv11chk 508442915 ; test $? -eq 0 && echo true || echo false
./dv11chk 502833424 ; test $? -eq 0 && echo true || echo false
./dv11chk 505583887 ; test $? -eq 0 && echo true || echo false
./dv11chk 507442423 ; test $? -eq 0 && echo true || echo false
./dv11chk 507086082 ; test $? -eq 0 && echo true || echo false
./dv11chk 509387152 ; test $? -eq 0 && echo true || echo false
./dv11chk 509372708 ; test $? -eq 0 && echo true || echo false
./dv11chk 507200110 ; test $? -eq 0 && echo true || echo false
./dv11chk 504214381 ; test $? -eq 0 && echo true || echo false
./dv11chk 504018876 ; test $? -eq 0 && echo true || echo false
./dv11chk 506183157 ; test $? -eq 0 && echo true || echo false
./dv11chk 502683899 ; test $? -eq 0 && echo true || echo false
./dv11chk 504276514 ; test $? -eq 0 && echo true || echo false
./dv11chk 504902156 ; test $? -eq 0 && echo true || echo false
./dv11chk 508572894 ; test $? -eq 0 && echo true || echo false
./dv11chk 504822713 ; test $? -eq 0 && echo true || echo false
./dv11chk 505441632 ; test $? -eq 0 && echo true || echo false
./dv11chk 507826981 ; test $? -eq 0 && echo true || echo false
./dv11chk 504598015 ; test $? -eq 0 && echo true || echo false
echo "*** Febraban Brasil ***"
echo "(da página https://www.bb.com.br/docs/pub/emp/mpe/dwn/PadraoCodigoBarras.pdf)"
./dv11chk 012300678960 ; test $? -eq 0 && echo true || echo false
./dv11chk 82200002150482009741232201540982901086059400 ; test $? -eq 0 && echo true || echo false

↑ Acima