시프트와 회전 예제 사다리 문서의 문제 4 풀이 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""시프트와 회전 예제 사다리의 답 확인. 실행: python3 40_shift-rotate-ladder_p4.py
MASM이 없어 시프트·회전 명령을 파이썬 함수로 흉내 낸다(한 칸씩 n번 민다).
문제 1~4의 모든 중간 값과 CF를 단언한다."""
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__":
# 문제 1: mov al,0D4h / shl al,1 / sar al,2 / rol al,3
al, cf = shl(0xD4, 1); assert (al, cf) == (0xA8, 1)
al, cf = sar(al, 2); assert (al, cf) == (0xEA, 0)
al, cf = rol(al, 3); assert (al, cf) == (0x57, 1)
# 문제 2: stc / mov bl,4Eh / rcl bl,2 / rcr bl,1
bl, cf = rcl(0x4E, 1, cf=1); assert (bl, cf) == (0x9D, 0)
bl, cf = rcl(bl, 1, cf=cf); assert (bl, cf) == (0x3A, 1)
bl, cf = rcr(bl, 1, cf=cf); assert (bl, cf) == (0x9D, 0)
# 문제 3: mov ax,0B3C5h / mov dx,2D4Fh / shld dx,ax,4 / shrd ax,dx,8 / ror ax,4
dx, cf = shld(0x2D4F, 0xB3C5, 4); assert (dx, cf) == (0xD4FB, 0)
ax, cf = shrd(0xB3C5, dx, 8); assert (ax, cf) == (0xFBB3, 1)
ax, cf = ror(ax, 4, 16); assert (ax, cf) == (0x3FBB, 0)
# 문제 4: DX = 266Ah에서 달(5~8번 비트) 꺼내기
dx = 0x266A
ax, _ = shr(dx, 5, 16)
assert ax == 0x0133 and ax & 0b00001111 == 3
assert dx >> 9 == 19 and dx & 0b11111 == 10 # 해 19, 날 10
# 모든 (해, 달, 날) 조합에서 같은 코드가 달을 꺼낸다
for y in range(128):
for m in range(1, 13):
for d in (1, 15, 31):
v = (y << 9) | (m << 5) | d
assert shr(v, 5, 16)[0] & 0x0F == m
assert shr(v, 9, 16)[0] == y
# 맨 위 비트가 1인 값(A66Ah)에서 해를 꺼낼 때: SHR은 53h, SAR은 FFD3h
assert shr(0xA66A, 9, 16)[0] == 0x0053 and sar(0xA66A, 9, 16)[0] == 0xFFD3
print("ALL CHECKS PASSED")