#8086 Assembly, IBM PC DOS, 29 28 25 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
03 F1 ADD SI, CX ; start at end of input 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
F7 DA NEG DX ; flip DX to alternate odd/even index
78 06 JS 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 the two digits: AL = AL + AH
EVEN:
32 E4 XOR AH, AH ; clear AH so AX can be added to DX
03 D8 ADD BX, AX ; add to running sum in BX
E2 ED LOOP DIGIT_LOOP
93 XCHG BX, AX ; sum is in BX, move to AX for conversion
D4 0A AAM ; BCD convert AX (set ZF=1 if low digit is 0)
ENDM
Got to use (abuse) the x86's BCD-to-binary instructions to handle the individual digit splitting and modulo 10
check. It turns out that the AAM
and AAD
instructions do BCD/binary conversion of byte 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.