Z80Golf , 53 36 34 바이트
@Lynn 덕분에 -16 바이트 @@ eil 덕분에
-2 바이트
이것은 단지 Z80 머신 코드이므로, 이것에는 많은 인쇄 할 수없는 것들이 있으므로, xxd -r
뒤집을 수있는 16 진 덤프가 있습니다 :
00000000: ddb6 2120 10dd b615 280c 003e 62ff 3e65 ..! ....(..>b.>e
00000010: ffff 3e70 ff76 003e 62ff 3e65 ffff 3e70 ..>p.v.>b.>e..>p
00000020: ff76 .v
온라인으로 사용해보십시오! (파이썬의 철저한 테스터)
설명
z80golf는 무정부 골프의 가상 Z80 기계,이다 call $8000
하는 putchar입니다 call $8003
getchar가있다, halt
당신의 프로그램이 배치되고, 인터프리터 출구를 만들어 $0000
, 다른 모든 메모리를 제로로 채워진다. 어셈블리에서 프로그램을 방사선으로 방지하는 것은 매우 어렵지만 일반적으로 유용한 기술은 1 바이트 dem 등원 명령어를 사용하는 것입니다. 예를 들어
or c ; b1 ; a = a | c
는 1 바이트 a | c | c == a | c
에 불과하므로 명령어를 반복하여 방사선을 차단할 수 있습니다. Z80에서 8 비트 즉시로드는 2 바이트 (즉, 즉시가 두 번째 바이트에 있음)이므로 일부 를로드 할 수 있습니다. 값을 레지스터에 안정적으로 있습니다. 이것이 내가 프로그램의 시작 부분에서 원래했던 일이므로 답변 맨 아래에 보관 된 더 긴 변형을 분석 할 수 있지만 더 간단한 방법이 있음을 깨달았습니다.
이 프로그램은 두 개의 독립적 인 페이로드로 구성되며 그 중 하나는 방사선으로 인해 손상되었을 수 있습니다. 바이트가 제거되었는지 여부와 일부 절대 메모리 주소의 값을 확인하여 제거 된 바이트가 페이로드의 두 번째 사본 앞에 있는지 여부를 확인합니다.
먼저 방사선이 관찰되지 않으면 종료해야합니다.
or a, (ix+endbyte) ; dd b6 21 ; a |= memory[ix+0x0021]
jr nz, midbyte ; 20 10 ; jump to a halt instruction if not zero
바이트가 제거되면 모든 바이트가 이동하고 $0020
마지막을 포함 76
하므로 $0021
0이됩니다. 중복성이 거의 없지만 프로그램 시작을 감당할 수 있습니다.
- 점프 오프셋
$10
이 제거되면 방사선이 올바르게 감지되고 점프가 수행되지 않으며 오프셋이 중요하지 않습니다. 다음 명령어의 첫 번째 바이트가 사용되지만 바이트 제거에 견딜 수 있도록 설계되었으므로 이는 중요하지 않습니다.
- 점프 opcode
$20
가 제거되면, 점프 오프셋 $10
은 djnz $ffe4
다음 명령어 바이트를 오프셋으로 디코딩 하여 루프 명령어 인 B를 감소시키고 결과가 0이 아닌 경우 점프합니다. ffe4-ffff
0으로 채워 지기 때문에 (nop
들), 프로그램 카운터 주위에 감싸고,이 프로그램의 256 회를 시작을 실행 한 다음 마지막으로 나갈 것입니다. 이 작품이 놀랍습니다.
- 를 제거하면
$dd
나머지 스 니펫이로 디코딩되고 or (hl) / ld ($1020), hl
프로그램의 다음 부분으로 밉니다. 는 or
중요한 레지스터를 변경하지 않으며이 시점에서 HL이 0이므로 쓰기도 취소됩니다.
- 를 제거하면
$b6
나머지는 그대로 디코딩 ld ($1020), ix
되고 위와 같이 진행됩니다.
- 를 제거하면
$21
디코더가를 먹고 동작을 $20
트리거합니다 djnz
.
통합 검사 0 덕분에를 사용 or a, (ix+*)
하면 2 바이트 를 절약 ld a, (**) / and a / and a
할 수 있습니다.
이제 두 개의 페이로드 사본 중 실행할 사본을 결정해야합니다.
or (ix+midbyte) ; dd b6 15
jr z, otherimpl ; 28 0c
nop ; 00
; first payload
ld a, 'b' ; 3e 62
rst $0038 ; ff
ld a, 'e' ; 3e 65
rst $0038 ; ff
rst $0038 ; ff
ld a, 'p' ; 3e 70
rst $0038 ; ff
midbyte:
halt ; 76
otherimpl:
nop ; 00
ld a, 'b' ; 3e 62
; ... ; ...
rst $0038 ; ff
endbyte:
halt ; 76
두 개의 사본은 nop로 구분됩니다. 상대 점프를 사용하여 선택하기 때문에 복사는 대상 다음에 첫 번째 바이트를 건너 뛰는 방식으로 프로그램을 이동시킬 수 있습니다. 또한 nop는 0으로 인코딩되므로 시프트 된 바이트를 쉽게 감지 할 수 있습니다. 스위치 자체가 손상된 경우 두 페이로드가 모두 안전하기 때문에 어떤 페이로드가 선택되는지는 중요하지 않습니다. 그래도 초기화되지 않은 메모리로 점프하지 않도록하십시오.
- 삭제
$dd
하면 다음 두 바이트가 다음과 같이 디코딩됩니다.or (hl) / dec d
. Clobbers D. 별거 아닙니다.
- 삭제
$b6
하면에 대한 문서화되지 않은 더 긴 인코딩이 생성됩니다 dec d
. 같은 상기와.
- 삭제
$15
하면 $28
대신 오프셋 이 읽히고 $0c
아래 에서와 같이 실행이 진행 됩니다.
- 이
$28
사라지면 $0c
가로 디코딩됩니다 inc c
. 페이로드는 신경 쓰지 않습니다 c
.
- 삭제
$0c
-이것이 nop의 목적입니다. 그렇지 않으면 페이로드의 첫 번째 바이트가 점프 오프셋으로 읽히고 프로그램이 초기화되지 않은 메모리로 점프합니다.
페이로드 자체는 매우 간단합니다. 문자열의 작은 크기는이 접근법을 루프보다 작게 만들고 위치 독립적 인 방식을 만드는 것이 더 쉽다고 생각합니다. 은 e
에 beep
반복, 그래서 하나를 면도 할 수 있습니다 ld a
. 사이의 모든 메모리 있기 때문에, $0038
과가 $8000
제로, 내가 그것을 통해 가을과 짧은 사용할 수 rst
의 변형 call
에 대해서만 작동 지시, $0
, $8
, $10
까지, 등등과$38
.
이전 접근법
64 바이트
00000000: 2e3f 3f2e 3f3f 7e7e a7a7 201f 1e2b 2b1e .??.??~~.. ..++.
00000010: 2b2b 6b00 7ea7 2814 003e 62cd 0080 3e65 ++k.~.(..>b...>e
00000020: cd00 80cd 0080 3e70 cd00 8076 003e 62cd ......>p...v.>b.
00000030: 0080 3e65 cd00 80cd 0080 3e70 cd00 8076 ..>e......>p...v
58 바이트
00000000: 2e39 392e 3939 7e7e a7a7 2019 3a25 00a7 .99.99~~.. .:%..
00000010: 2814 003e 62cd 0080 3e65 cd00 80cd 0080 (..>b...>e......
00000020: 3e70 cd00 8076 003e 62cd 0080 3e65 cd00 >p...v.>b...>e..
00000030: 80cd 0080 3e70 cd00 8076 ....>p...v
53 바이트
편집 히스토리에 대한 설명이 있지만 그다지 다르지 않습니다.
00000000: 3a34 00a7 a720 193a 2000 a728 1400 3e62 :4... .: ..(..>b
00000010: cd00 803e 65cd 0080 cd00 803e 70cd 0080 ...>e......>p...
00000020: 7600 3e62 cd00 803e 65cd 0080 cd00 803e v.>b...>e......>
00000030: 70cd 0080 76 p...v
만일의 경우 : 비어 있지 않은 결과물이 삐 소리 대신 괜찮습니다.
1 바이트
v
halt
s the program normally, but if radiation removes it, then the memory will be full of zeroes, making $8000
execute an infinite number of times, printing a lot of null bytes.