SHL moves every bit of a byte or word one place to the left, as many times as the count says. A 0 enters at bit 0, and the bit pushed out of the top goes into CF; after several steps CF holds the last bit that left. SAL (shift arithmetic left) is another name for exactly the same instruction.

CF <- b7 b6 b5 b4 b3 b2 b1 b0 <- 0
0000 0110 6
SHL 1 0000 1100 12, CF = 0

Forms

The operand is a register or memory (SHL WORD PTR [BX], 1). On the 8086 the count is either the constant 1 or the CL register. SHL AL, 3 does not exist; write MOV CL, 3 then SHL AL, CL. If CL is 0, nothing is shifted and the flags are left alone.

Flags

CF gets the last bit shifted out. SF, ZF and PF describe the result. OF is defined only for a count of 1: it is set when the top bit changed, which means the value changed sign when read as signed (CF and the new top bit differ). For larger counts OF is undefined, and AF is always undefined.

Multiplying

Shifting left by n multiplies an unsigned or signed number by 2 to the power n, as long as the result fits. With single-bit shifts, CF=1 tells you an unsigned value overflowed. Shifts and adds combine into other factors, for example times 10:

SHL AX, 1 ; x * 2
MOV BX, AX
SHL AX, 1 ; x * 4
SHL AX, 1 ; x * 8
ADD AX, BX ; x * 8 + x * 2 = x * 10

Pitfalls and later CPUs

The 8086 uses all 8 bits of CL as the count, so a word shifted by 16 or more simply becomes 0. The 80186 and later CPUs keep only the low 5 bits of the count (0..31). The 186 also added an immediate count, as in SHL AX, 4, which MASM accepts only after .186. To shift a 32-bit value in DX:AX, use SHL AX, 1 then RCL DX, 1.

Prints 00000011 00000110 11000000 CF=0 10000000 CF=1
MOV AL, 3 ; 0000 0011
CALL SHOWBIN
SHL AL, 1 ; 6
CALL SHOWBIN
MOV CL, 5
SHL AL, CL ; 6 * 32 = 192
CALL SHOWBIN
CALL SHOWCF
SHL AL, 1 ; 384 does not fit: CF = 1
CALL SHOWBIN
CALL SHOWCF
SHOWBIN PROC NEAR ; print AL as 8 binary digits and a space
PUSHF ; keep the caller's flags
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV BL, AL
MOV CX, 8
NEXTBIT:
SHL BL, 1 ; the top bit falls into CF
MOV DL, "0"
ADC DL, 0 ; "0" + CF = "0" or "1"
MOV AH, 02h
INT 21h
LOOP NEXTBIT
MOV DL, " "
MOV AH, 02h
INT 21h
POP DX
POP CX
POP BX
POP AX
POPF
RET
SHOWBIN ENDP
SHOWCF PROC NEAR ; print "CF=0" or "CF=1" and a space
PUSHF
PUSH AX
PUSH DX
MOV DL, "0"
ADC DL, 0 ; "0" + CF, before anything changes CF
PUSH DX
MOV AH, 09h
MOV DX, OFFSET CFTXT
INT 21h
POP DX
MOV AH, 02h
INT 21h
MOV DL, " "
MOV AH, 02h
INT 21h
POP DX
POP AX
POPF
RET
SHOWCF ENDP
CFTXT DB "CF=$"