#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:
Download LUHN.COM IBM PC DOS test program.