R a s p b e r e d

Usando o Raspberry Pi

Dígito Verificador Módulo 11

Programa para validar o dígito verificador (DV ou DAC) módulo 11, conforme especificado pela Febraban

Este programa pode ser utilizado para validar o NIF (Número de Identificação Fiscal) de Portugal.

/**
 * @file    dv11chk.s
 * @author  Halley Pacheco de Oliveira
 * @version 1.0
 * 
 * @section DESCRIPTION
 *
 * 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. (https://www.nif.pt)
 *
 * @param  Número com o DV na última posição.
 * @return 0 (true) se o dígito verificador estiver correto;
 *         1 (false) se o dígito verificador estiver incorreto.
 *
 * @see: 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 (UAL=Unified Assembler Language)

// Definições

.set    TRUE, 0
.set    FALSE, 1
.set    SYS_EXIT, 1
.set    SYS_WRITE, 4
.set    STDIN, 0
.set    STDOUT, 1
.set    STDERR, 2
.set    ZERO, 0x30      // Caractere "0"
.set    NOVE, 0x39      // Caractere "9"

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

// Código

.text
.global _start

msg_erro:                       // Escreve a mensagem de erro na saída de erro 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, 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 de erro 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, 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     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         // 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, ZERO        // Comparar o caractere com "0"
        BLT     msg_ndig        // Se for menor mostrar mensagem de erro e terminar
        CMP     R3, NOVE        // Comparar o caractere com "9"
        BGT     msg_ndig        // Se for maior mostrar mensagem de erro e terminar
        SUB     R3, R3, ZERO    // R3 = Valor numérico do dígito (subtrair de "0")
        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, ZERO    // R7 = DV convertido em caractere (somar com "0")
        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:~/raspbered/assembly/dv11chk $ make clean
rm dv11chk.o dv11chk

pi@raspberrypi:~/raspbered/assembly/dv11chk $ make
as -o dv11chk.o dv11chk.s
ld -o dv11chk dv11chk.o

pi@raspberrypi:~/raspbered/assembly/dv11chk $ ./dv11chk ; test $? -eq 0 && echo true || echo false
Erro: Informe o número para calcular o DV.
false

pi@raspberrypi:~/raspbered/assembly/dv11chk $ ./dv11chk 123.456 ; test $? -eq 0 && echo true || echo false
Erro: Algum caractere não é um dígito de 0 a 9.
false

pi@raspberrypi:~/raspbered/assembly/dv11chk $ ./dv11chk 012300678960 ; test $? -eq 0 && echo true || echo false
true

pi@raspberrypi:~/raspbered/assembly/dv11chk $ ./dv11chk 012300678961 ; test $? -eq 0 && echo true || echo false
false

Script para validação:

#!/bin/bash
# @summary:: Aprovação do programa dvd11chk
# @since: 2019-07-10
# @author: Halley Pacheco de Oliveira
#
# Todas as validações devem produzir como resultado true
# para o programa ser aprovado.
#
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
Última atualização em 2019-07-28
Publicado em 2019-07-28