2.2.2 Dígito Verificador Módulo 10
Programa para calcular o dígito verificador (DV ou DAC) módulo 10, conforme especificado pela Febraban
/**
* @file dv10dig.s
* @author Reficio
* @version 1.0
*
* @section DESCRIPTION
*
* 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)
*
* @param Número para o qual será calculado o DV.
* @output Valor do dígito verificador (DV) calculado.
* @return 0 se não encontrar erro, ou 1 se encontrar algum erro.
*
* @see: https://www.bb.com.br/docs/pub/emp/mpe/dwn/PadraoCodigoBarras.pdf
*/
// Nova sintaxe unificada
.syntax unified
// Definições
.set SYS_EXIT, 1
.set SYS_WRITE, 4
.set STDIN, 0
.set STDOUT, 1
.set STDERR, 2
// Dados
.data
.align
txt_erro: .string "Erro: Informe o número para calcular o DV.\n"
.set TAM_ERRO, (. - txt_erro) // Tamanho da mensagem
.align
txt_ndig: .string "Erro: Algum caractere não é um dígito de 0 a 9.\n"
.set TAM_NDIG, (. - txt_ndig) // Tamanho da mensagem
.align
txt_dv: .string "DV: X\n"
.set TAM_DV, (. - txt_dv) // Tamanho da mensagem
// Código
.text
.global _start
msg_erro: // Escrever a mensagem de erro na saída padrão
MOV R7, SYS_WRITE // Número da chamada do sistema em R7 (escrever)
MOV 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, 1 // Código de retorno = 1
MOV PC, LR // Retornar: PC (Program Counter) = LR (Link Register)
msg_ndig: // Escrever a mensagem de erro na saída padrão
MOV R7, SYS_WRITE // Número da chamada do sistema em R7 (escrever)
MOV 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, 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
MOV R7, SYS_WRITE // Número da chamada do sistema em R7 (escrever)
MOV R0, STDOUT // Descritor do arquivo 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: // Calcular o tamanho da string e retornar 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 found // 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
found: 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
// 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:~/raspbered/assembly/dv10dig $ make clean
rm dv10dig.o dv10dig
pi@raspberrypi:~/raspbered/assembly/dv10dig $ make
as -o dv10dig.o dv10dig.s
ld -o dv10dig dv10dig.o
pi@raspberrypi:~/raspbered/assembly/dv10dig $ ./dv10dig ; echo $?
Erro: Informe o número para calcular o DV.
1
pi@raspberrypi:~/raspbered/assembly/dv10dig $ ./dv10dig 000a111 ; echo $?
Erro: Algum caractere não é um dígito de 0 a 9.
1
pi@raspberrypi:~/raspbered/assembly/dv10dig $ ./dv10dig 01230067896 ; echo $?
DV: 3
0
pi@raspberrypi:~/raspbered/assembly/dv10dig $ ./dv10dig 8220000215048200974123220154098290108605940 ; echo $?
DV: 1
0