Cálculo do Dígito Verificador Módulo 10 Febraban

/*
 * Programa para calcular o dígito verificador (DV ou DAC) módulo 10.
 *
 * O DAC (Dígito de Auto-Conferência) módulo 10 de um número, é calculado
 * multiplicando cada algarismo pela seqüência de multiplicadores
 * 2, 1, 2, 1, ... posicionados da direita para a esquerda.
 *
 * A soma dos algarismos do produto é dividida por 10 e o DAC será a
 * diferença entre o divisor (10) e o resto da divisão:
 * DAC = 10 - (resto da divisão).
 * Quando o resto da divisão for 0 (zero), o DAC calculado é 0 (zero).
 * (Padrão FEBRABAN)
 *
 * Recebe como argumento o número para o qual será calculado o DV.
 * Mostra o valor do dígito verificador (DV) calculado.
 * Retorna 0 se não encontrar erros e 1 se encontrar algum erro.
 *
 * 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

// Equates

.equ	SYS_EXIT, 1
.equ	SYS_WRITE, 4
.equ	STDIN, 0
.equ	STDOUT, 1

// 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

.align 4
txt_dv:		.string	"DV: X\n"
.equ		TAM_DV, (. - txt_dv)		// 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, =STDOUT	// Valor do destino em R0 (saída padrão, 1)
	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, #1		// 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, =STDOUT	// Valor do destino em R0 (saída padrão, 1)
	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, #1		// Código de retorno = 1
	B	end		// Terminar a execução do programa

msg_dv:				// Escrever o dígito verificador na saída padrão
	LDR	R7, =SYS_WRITE	// Número da chamada do sistema em R7 (escrever)
	LDR	R0, =STDOUT	// Valor do destino em R0 (saída padrão, 1)
	LDR	R1, =txt_dv	// R1 -> texto da mensagem do DV
	MOV	R2, #TAM_DV	// Tamanho da string da mensagem do DV em R2
	SVC	0		// Chamar o sistema operacional
	MOV	R0, #1		// Código de retorno = 1
	MOV	PC, LR		// Retornar: PC (Program Counter) = LR (Link Register)

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		// Armazenar o tamanho do número em R2
	ADD	R1, R0, R2	// R1 -> Final da string contendo o número
	SUB	R1, #1		// R1 -> Último caractere da string (sem o 0)

	MOV	R4, #2		// R4 = Multiplicador inicial (2,1,2,1,2,1,...)
	MOV	R6, #0		// R6 = Somatório dos dígitos da multiplicação
calcdv:
	CMP	R0, R1		// Comparar posição atual com início da string
	BGT	fim_soma	// 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 = N = Numerador = Dígito x Multiplicador
				// Calcular Quociente e Resto (N = D × Q + R)
	MOV	R7, #10		// R7 = D = Denominador = 10
	UDIV	R8, R5, R7	// R8 = Q = Quociente = Numerador / Denominador
	MUL	R9, R8, R7	// R9 = Denominador x Quociente (D x Q)
	SUB	R9, R5, R9	// R9 = Resto = R = N - (D × Q)
	ADD	R6, R6, R8	// R6 += Q
	ADD	R6, R6, R9	// R6 += R
multiplicador:			// Próximo multiplicador (2,1,2,1....)
	SUBS	R4, #1		// Subtrai 1 do Multiplicador
	BNE	notz		// Se não for zero manter
	MOV	R4, #2		// Se for zero volta a ser dois
notz:	SUB	R1, R1, #1	// Posição atual mais próxima do início da string
	B	calcdv		// Continuar com o próximo dígito
fim_soma:			// Calcular o dígito verificador a partir da soma
	MOV	R7, #10		// R7 = D = Denominador = 10
	UDIV	R8, R6, R7	// R8 = Q = Quociente = Numerador / Denominador
	MUL	R9, R8, R7	// R9 = Denominador x Quociente (D x Q)
	SUBS	R9, R6, R9	// R9 = Resto = R = N - (D × Q)
	BEQ	fim_calcdv	// Se resto eq 0 então DV = 0
	SUB	R9, R7, R9	// Senão R9 = DV = 10 - Resto
fim_calcdv:
	ADD	R7, R9, #48	// R7 = DV convertido em caractere
	LDR	R6, =txt_dv	// R6 -> Início da mensagem de DV
	ADD	R6, R6, #TAM_DV	// R6 -> Primeiro byte após a mensagem
	SUB	R6, R6, #3	// R6 -> Penúltimo caractere da mensagem (X)
	STRB	R7, [R6, #0]	// Armazenar o DV no fim da mensagem
	BL	msg_dv		// Mostrar o DV
	MOV	R0, #0		// Código de retorno = 0

end:

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

Makefile:

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

Construção e execução:

pi@raspberrypi:~/assembly/dv10dig $ make clean
rm dv10dig.o dv10dig
pi@raspberrypi:~/assembly/dv10dig $ make
as -o dv10dig.o dv10dig.s
ld -o dv10dig dv10dig.o
pi@raspberrypi:~/assembly/dv10dig $ ./dv10dig  ; echo $?
Erro: Informe o número para calcular o DV.
1
pi@raspberrypi:~/assembly/dv10dig $ ./dv10dig  0000a111 ; echo $?
Erro: Algum caractere não é um dígito de 0 a 9.
1
pi@raspberrypi:~/assembly/dv10dig $ ./dv10dig 01230067896 ; echo $?
DV: 3
0
pi@raspberrypi:~/assembly/dv10dig $ ./dv10dig 8220000215048200974123220154098290108605940  ; echo $?
DV: 1
0

↑ Acima