이중 시프트 SHLD·SHRD 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""이중 시프트(SHLD, SHRD) 검증. 실행: python3 40_double-shift_verify.py
MASM이 없어 시프트·회전 명령을 파이썬 함수로 흉내 낸다(한 칸씩 n번 민다).
문서의 값과 이어서 실행하기의 답을 단언하고, 이어 붙인 수의 시프트와 같은지 검사한다."""
def mask(bits):
return (1 << bits) - 1
def signed(x, bits=8):
"""비트열 x를 부호 있는 수(2의 보수)로 읽는다."""
return x - (1 << bits) if x >> (bits - 1) else x
def shl(x, n, bits=8, cf=0):
"""왼쪽 시프트(SHL = SAL). (결과, CF)를 돌려준다. 한 칸씩 n번 민다."""
for _ in range(n):
cf = (x >> (bits - 1)) & 1 # 밀려 나가는 맨 위 비트가 CF로 간다
x = (x << 1) & mask(bits) # 맨 아래 빈칸은 0
return x, cf
sal = shl # SAL은 SHL의 다른 이름이다
def shr(x, n, bits=8, cf=0):
"""논리 오른쪽 시프트. 맨 위 빈칸을 0으로 채운다."""
for _ in range(n):
cf = x & 1
x >>= 1
return x, cf
def sar(x, n, bits=8, cf=0):
"""산술 오른쪽 시프트. 맨 위 빈칸을 원래 부호 비트로 채운다."""
for _ in range(n):
cf = x & 1
x = (x >> 1) | (x & (1 << (bits - 1)))
return x, cf
def rol(x, n, bits=8, cf=0):
"""왼쪽 회전. 맨 위 비트가 CF와 맨 아래 비트 두 곳으로 간다."""
for _ in range(n):
cf = (x >> (bits - 1)) & 1
x = ((x << 1) & mask(bits)) | cf
return x, cf
def ror(x, n, bits=8, cf=0):
"""오른쪽 회전. 맨 아래 비트가 CF와 맨 위 비트 두 곳으로 간다."""
for _ in range(n):
cf = x & 1
x = (x >> 1) | (cf << (bits - 1))
return x, cf
def rcl(x, n, bits=8, cf=0):
"""CF를 낀 왼쪽 회전. CF가 맨 아래로 들어오고 맨 위 비트가 CF로 나간다."""
for _ in range(n):
out = (x >> (bits - 1)) & 1
x = ((x << 1) & mask(bits)) | cf
cf = out
return x, cf
def rcr(x, n, bits=8, cf=0):
"""CF를 낀 오른쪽 회전. CF가 맨 위로 들어오고 맨 아래 비트가 CF로 나간다."""
for _ in range(n):
out = x & 1
x = (x >> 1) | (cf << (bits - 1))
cf = out
return x, cf
def shld(dest, src, n, bits=16):
"""dest를 왼쪽으로 n칸 밀고, 빈 아래 칸을 src의 위쪽 n비트로 채운다. src는 그대로."""
cf = 0
for i in range(n):
cf = (dest >> (bits - 1)) & 1
incoming = (src >> (bits - 1 - i)) & 1
dest = ((dest << 1) & mask(bits)) | incoming
return dest, cf
def shrd(dest, src, n, bits=16):
"""dest를 오른쪽으로 n칸 밀고, 빈 위 칸을 src의 아래쪽 n비트로 채운다. src는 그대로."""
cf = 0
for i in range(n):
cf = dest & 1
incoming = (src >> i) & 1
dest = (dest >> 1) | (incoming << (bits - 1))
return dest, cf
if __name__ == "__main__":
# --- SHLD 그림 (8비트로 그린 그림) ---
assert shld(0b11100000, 0b10011101, 1, bits=8) == (0b11000001, 1)
# --- wval = 9BA6h, AX = AC36h, shld wval,ax,4 ---
assert shld(0x9BA6, 0xAC36, 4)[0] == 0xBA6A
# --- SHRD 그림 (8비트) ---
assert shrd(0b11000001, 0b00011101, 1, bits=8) == (0b11100000, 1)
# --- AX = 234Bh, DX = 7654h, shrd ax,dx,4 ---
assert shrd(0x234B, 0x7654, 4)[0] == 0x4234
# --- 이어서 실행하기: AX = 7C36h, DX = 9FA6h ---
dx, cf = shld(0x9FA6, 0x7C36, 4); assert dx == 0xFA67
dx, cf = shrd(dx, 0x7C36, 8); assert dx == 0x36FA
# 둘째 줄을 처음 DX에 바로 하면 다른 값이 나온다 (앞줄 결과에 이어서 해야 한다)
assert shrd(0x9FA6, 0x7C36, 8)[0] == 0x369F
# --- 16비트 표본 검사: 두 값을 이어 붙인 32비트 수로 본 식과 같다 ---
import random
random.seed(40)
for _ in range(20000):
d, s, n = random.randrange(65536), random.randrange(65536), random.randrange(1, 16)
assert shld(d, s, n)[0] == (((d << 16 | s) << n) >> 16) & 0xFFFF
assert shrd(d, s, n)[0] == ((s << 16 | d) >> n) & 0xFFFF
assert shld(d, s, n)[1] == (d >> (16 - n)) & 1 # CF = dest에서 마지막으로 나간 비트
assert shrd(d, s, n)[1] == (d >> (n - 1)) & 1
# src 대신 0을 넣으면 보통 시프트와 같다
assert shld(d, 0, n)[0] == shl(d, n, 16)[0]
assert shrd(d, 0, n)[0] == shr(d, n, 16)[0]
# --- 8비트 전수 검사 (count = 1..7) ---
for d in range(256):
for s in range(256):
for n in (1, 3, 7):
assert shld(d, s, n, 8)[0] == (((d << 8 | s) << n) >> 8) & 0xFF
assert shrd(d, s, n, 8)[0] == ((s << 8 | d) >> n) & 0xFF
# --- 쓰임: 64비트 수 EDX:EAX를 왼쪽으로 4칸 (shld edx,eax,4 뒤 shl eax,4) ---
for _ in range(5000):
v = random.getrandbits(64)
edx, eax = v >> 32, v & 0xFFFFFFFF
edx, _ = shld(edx, eax, 4, 32)
eax, _ = shl(eax, 4, 32)
assert (edx << 32 | eax) == (v << 4) & (2 ** 64 - 1)
# 문서의 예: 12345678h:9ABCDEF0h << 4
edx, _ = shld(0x12345678, 0x9ABCDEF0, 4, 32); eax, _ = shl(0x9ABCDEF0, 4, 32)
assert (edx, eax) == (0x23456789, 0xABCDEF00)
# 순서를 바꿔 EAX를 먼저 밀면 다른(틀린) 값이 된다
eax2, _ = shl(0x9ABCDEF0, 4, 32); edx2, _ = shld(0x12345678, eax2, 4, 32)
assert edx2 == 0x2345678A != 0x23456789
# 카드
assert shld(0x1234, 0xABCD, 8)[0] == 0x34AB
assert shrd(0x1234, 0xABCD, 8)[0] == 0xCD12
assert shld(0x00FF, 0x8000, 1) == (0x01FF, 0)
print("ALL CHECKS PASSED")