Skip to main content
12 of 19
add explanation for register state
640KB
  • 11.9k
  • 2
  • 35
  • 57

#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.

For all official IBM/MS versions of DOS, AX and BX are initialized to 0000 on startup (ref, ref) and DX to CS, which is a 12-bit value so guaranteed to be non-zero and positive . This is how we can assume BX is 0 and can flip/flop DX to determine odd/even digits places.

Example output:

enter image description here

Download LUHN.COM IBM PC DOS test program.

640KB
  • 11.9k
  • 2
  • 35
  • 57