x86-64 기계 코드, 8 바이트
Bruce Forte의 솔루션 에서 영감을 얻었 지만 약간 미달했습니다. :-)
8D 07 lea eax, [rdi] ; put copy of input parameter in EAX
D1 EF shr edi, 1 ; shift LSB into CF
InfiniteLoop:
F3 73 FD rep jnc InfiniteLoop ; test CF; infinite loop back here if input was even
C3 ret ; return with original input in EAX if it was odd
EDI
System V AMD64 호출 규칙 에 따라 레지스터 에서 단일 정수 매개 변수가 사용됩니다 .
이 값의 사본은 처음에 만들어지며, EAX
필요한 경우 반환 될 수 있습니다. ( 홀수 바이트의 명령어가 필요하기 때문에 LEA
보통 대신 사용됩니다 MOV
.)
그런 다음 in 값 EDI
이 1 씩 오른쪽으로 시프트되어 시프트 된 비트가 캐리 플래그 (CF)에 배치됩니다. 이 비트는 짝수이면 0이되고 홀수이면 1이됩니다.
그런 다음 JNC
명령을 사용하여 CF를 테스트합니다.이 명령은 CF가 0 인 경우에만 분기됩니다 (예 : 짝수). 이것은 우리가 짝수 값을 위해 무한 루프로 갈 것임을 의미합니다. 홀수 값의 경우 원래 값 (in EAX
)이 반환됩니다.
JNC
하지만 명령 에는 약간의 트릭 이 있습니다. REP
접두사가 있습니다! 일반적으로 REP
접두사는 문자열 명령에만 사용되지만 인텔 및 AMD 설명서는 모두 관련이 없거나 불필요한 / 중복 REP
접두사가 무시 된다는 점에 동의하므로 여기서는 분기 명령에 1을 던져 3 바이트 길이로 만듭니다. 그렇게하면 점프 명령으로 인코딩 된 상대 오프셋도 홀수입니다. (물론 REP
홀수 바이트 접두사입니다.)
고맙게도 RET
홀수 바이트를 사용하여 인코딩됩니다!
온라인으로 사용해보십시오!
챌린지의 "출력"요구 사항을 만족 시키거나 반환하지 않기 위해 값이 홀수이거나 무한 루프에 들어가면 값을 반환하지 않는다고 생각하거나 더 흥미로운 것을 원한다면 여기에 함수가 있습니다. 직렬 포트에 값을 출력합니다 (물론 홀수 일 경우에만).
x86-64 머신 코드 (직렬 포트로 출력), 17 바이트
8D 07 lea eax, [rdi] ; put copy of input parameter (EDI) in EAX
B1 F7 mov cl, 0xf7 ; put 0xF7 into low-order bits of CX
B5 03 mov ch, 0x03 ; put 0x03 into high-order bits of CX
FE C1 inc cl ; increment low-order bits of CX to 0xF8 (so all together it's now 0x3F8)
0F B7 D1 movzx edx, cx ; move CX to DX ("MOV DX, CX" would have a 16-bit prefix of 0x66)
D1 EF shr edi, 1 ; shift LSB of input parameter into CF
73 01 jnc IsEven ; test CF: branch if 0 (even), fall through if 1 (odd)
EF out dx, eax ; output EAX (original input) to I/O port 0x3F8 (in DX)
IsEven:
C3 ret ; return
이것을 좀 더 흥미롭게 만드는 것은 코드 가 더 많은 일을한다는 것인데, 이는 홀수 바이트만을 사용하여 인코딩 된 명령어를 사용하여 모든 작업을 수행하는 것이 더 어려웠다는 것을 의미합니다. 물론 이것은 코드 골프에서 실패한다는 것을 의미합니다. 그래서 그것은 일종의 트레이드 오프입니다. 흥미롭고 도전적인 것을 원하십니까, 아니면 짧기를 원하십니까?
어쨌든 이것은 x86 OUT
명령어 를 사용하여 PC 의 표준 COM1 직렬 포트 인 I / O 포트 0x3F8에 씁니다 . 물론 재미있는 부분은 모든 표준 I / O 포트 (직렬 및 병렬)가 짝수의 주소를 가지므로 OUT
명령에 대한 즉각적인 것으로 단순히 인코딩 되거나 레지스터로 직접 이동할 수 없다는 것 입니다. 실제 값보다 하나 작은 값으로 초기화 한 다음 레지스터의 값 을 증가 시켜야합니다 . 피연산자로 사용될 때 명령어에서 홀수 바이트를 사용하여 인코딩 된 레지스터가 필요하므로 조작에 특정 레지스터를 사용하는 것으로 제한됩니다.
또한 값이 홀수 인 경우에만 명령이 홀수 오프셋을 갖도록 루프 상단에서 레지스터 DX
를 통해 레지스터 를 초기화 CX
해야했습니다 JNC
. 그러나 우리가 건너 뛰는 것은 OUT
명령 이므로이 코드는 폐기물주기와 클로버 스크래치 레지스터 만 수행합니다. 실제로 아무것도 출력 하지 않으므로 규칙을 위반하지 않습니다.
마지막으로,이 함수는 입력 값을로 남겨두고 (직렬 포트로 출력을 수행하거나 수행하지 않은 후) 리턴 EAX
합니다. 그러나 이것이 실제로 어떤 규칙도 위반하지는 않습니다. 어셈블리 언어의 모든 함수는 값과 함께 반환됩니다. EAX
문제는 중요한 값이거나 가비지 값입니다. 그것은 함수의 문서에 의해 결정되며 (실제로 값을 반환하거나 반환합니까 void
)이 경우 값을 반환하지 않는 것으로 문서화하고 있습니다. :-)
직렬 포트로의 출력을 구현하지 않기 때문에 TIO 링크가 없습니다. 당신은 진짜 철이나 상상력이 필요합니다.