Skip to main content
5 of 19
deleted 6 characters in body
640KB
  • 11.9k
  • 2
  • 35
  • 57

#8086 Assembly, MASM, 29 bytes

; Perform LUHN check
; Input: SI = card num string, CX = length
; Output: ZF=1 if valid, ZF=0 if not valid
    LUHN    MACRO
            LOCAL DIGIT_LOOP, EVEN
33 D2       XOR  DX, DX         ; clear digit sum in DX 
03 F1       ADD  SI, CX         ; start at end of string 
FD          STD                 ; set LODSB direction to decrement 
    DIGIT_LOOP:
AC          LODSB               ; load next digit into AL, decrement SI
2C 30       SUB  AL, '0'        ; convert ASCII char to binary value 
F6 C3 01    TEST BL, 1          ; test if digit position is even or odd
74 06       JZ   EVEN           ; if even index, do not double or sum digits 
D0 E0       SHL  AL, 1          ; double the value 
D4 0A       AAM                 ; BCD convert to split digits (ex: 18 = 12H --> 0108H) 
02 C4       ADD  AL, AH         ; add AL = AL + AH 
    EVEN:
32 E4       XOR  AH, AH         ; clear AH so AX can be added to DX 
03 D0       ADD  DX, AX         ; add to running sum in DX 
43          INC  BX             ; increment even/odd counter 
E2 EB       LOOP DIGIT_LOOP 
D4 0A       AAM                 ; BCD convert AX (set ZF=1 if low digit is 0)
    ENDM

Implemented as a MACRO (basically a function) above. Got to use (abuse) the x86's BCD-to-binary instructions to handle the individual digit splitting and mod 10 check. It turns out that the AAM and AAD instructions do BCD/binary conversion of 2 digit values (which can be very handy) despite them not being documented or described as performing that function generically.

Example output:

enter image description here

Download LUHN.COM IBM PC DOS test program.

640KB
  • 11.9k
  • 2
  • 35
  • 57