R a s p b e r e d

Usando o Raspberry Pi

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  Halley Pacheco de Oliveira
 * @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
 */

// Raspberry Pi 3 Modelo A+
.cpu    cortex-a53
.fpu    neon-fp-armv8
.syntax unified         // Sintaxe moderna (UAL=Unified Assembler Language)

// 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
Última atualização em 2019-07-28
Publicado em 2019-07-28