배경
오래된 8 비트 6502 칩이 마음에 듭니다. 6502 기계 코드에서 PPCG의 몇 가지 문제를 해결하는 것도 재미 있습니다. 그러나 데이터를 읽거나 표준 출력으로 출력하는 것과 같이 단순해야하는 일부 작업은 기계 코드에서 불필요하게 번거 롭습니다. 6502에서 영감을 얻었지만 문제에보다 유용하게 사용할 수 있도록 디자인 된 8 비트 가상 머신을 발명하십시오. 무언가를 구현하기 시작하면서 VM의 디자인이 최소한으로 줄어들면 이것이 큰 도전이 될 수 있음을 깨달았습니다. :)
태스크
다음 사양을 준수하는 8 비트 가상 머신을 구현하십시오. 이것은 code-golf 이므로 바이트 수가 가장 적은 구현이 승리합니다.
입력
구현에는 다음 입력이 필요합니다.
단일 부호없는 바이트
pc
, 이것은 초기 프로그램 카운터 (VM이 실행을 시작하는 메모리의 주소0
)최대 길이의
256
항목이 있는 바이트 목록으로 , 가상 머신의 RAM입니다 (초기 내용 포함).
이 입력은 적절한 형식으로 가져갈 수 있습니다.
산출
VM이 종료 된 후 RAM의 최종 내용 인 바이트 목록 (아래 참조). 결국 종료로 이어지는 입력 만받는다고 가정 할 수 있습니다. 모든 합리적인 형식이 허용됩니다.
가상 CPU
가상 CPU는
- 8 비트 프로그램 카운터
- 8 비트 누산기 레지스터 호출
A
및 - 8 비트 지수가 호출 레지스터
X
.
세 가지 상태 플래그가 있습니다.
Z
-제로 플래그는 일부 작업 결과로 설정된 후0
N
-일부 연산 결과가 음수로 표시된 후 음수 플래그가 설정 됨 (결과의 비트 7이 설정 됨)C
-캐리 플래그는 결과의 "누락"비트에 대한 추가 및 시프트로 설정됩니다.
시작되면, 플래그는 모든 프로그램 카운터는 주어진 값의 내용으로 설정, 클리어 A
하고 X
불확정이다.
8 비트 값은 다음 중 하나를 나타냅니다.
- 부호 범위의 정수
[0..255]
- 범위 내의 부호있는 정수, 2의 보수
[-128..127]
상황에 따라. 작업이 오버플로 또는 언더 플로 인 경우 값이 줄 바꿈됩니다 (추가의 경우 캐리 플래그가 영향을받습니다).
종료
가상 머신이 종료되면
HLT
명령에 도달- 존재하지 않는 메모리 주소에 액세스
- 프로그램 카운터는 메모리 외부에서 실행됩니다 (VM에 256 바이트의 전체 메모리가 제공 되어도 랩되지 않습니다)
어드레싱 모드
- 암시 적 -명령어에는 인수가 없으며 피연산자가 암시됩니다.
- 즉치 -피연산자는 명령어 바로 다음의 바이트입니다.
- relative- (분기 전용) 명령어가 서명 된 후 바이트 (2의 보수)이며 분기가 수행 될 경우 프로그램 카운터에 추가 할 오프셋을 결정합니다.
0
다음 명령의 위치입니다 - absolute- 명령어 뒤의 바이트는 피연산자의 주소입니다.
- indexed- 명령어 뒤의 바이트 더하기
X
(레지스터)는 피연산자의 주소입니다.
명령
각 명령어는 opcode (1 바이트)로 구성되며 주소 지정 모드에서 즉치 , 상대 , 절대 및 두 번째 인수 바이트를 인덱싱 합니다. 가상 CPU는 명령을 실행할 때 그에 따라 프로그램 카운터를 증가시킵니다 ( 1
또는 2
).
여기에 표시된 모든 opcode는 16 진입니다.
LDA
-피연산자를로드A
- 오피 코드 : 즉시 :
00
, 절대 :02
, 색인 :04
- 플래그 :
Z
,N
- 오피 코드 : 즉시 :
STA
-A
피연산자에 저장- 오피 코드 : 즉시 :
08
, 절대 :0a
, 색인 :0c
- 오피 코드 : 즉시 :
LDX
-피연산자를로드X
- 오피 코드 : 즉시 :
10
, 절대 :12
, 색인 :14
- 플래그 :
Z
,N
- 오피 코드 : 즉시 :
STX
-X
피연산자에 저장- 오피 코드 : 즉시 :
18
, 절대 :1a
, 색인 :1c
- 오피 코드 : 즉시 :
AND
- 비트 와 의A
과에 피연산자A
- 오피 코드 : 즉시 :
30
, 절대 :32
, 색인 :34
- 플래그 :
Z
,N
- 오피 코드 : 즉시 :
ORA
- 비트 또는 의A
및에 피연산자A
- 오피 코드 : 즉시 :
38
, 절대 :3a
, 색인 :3c
- 플래그 :
Z
,N
- 오피 코드 : 즉시 :
EOR
-비트 xor (제외 또는)A
및 피연산자A
- 오피 코드 : 즉시 :
40
, 절대 :42
, 색인 :44
- 플래그 :
Z
,N
- 오피 코드 : 즉시 :
LSR
-논리 시프트 오른쪽, 피연산자의 모든 비트를 오른쪽으로 한 자리 이동, 비트 0은 수행- 오피 코드 : 즉시 :
48
, 절대 :4a
, 색인 :4c
- 플래그 :
Z
,N
,C
- 오피 코드 : 즉시 :
ASL
-산술 시프트 왼쪽, 피연산자의 모든 비트를 왼쪽으로 한 자리 이동, 비트 7은 수행- 오피 코드 : 즉시 :
50
, 절대 :52
, 색인 :54
- 플래그 :
Z
,N
,C
- 오피 코드 : 즉시 :
ROR
-오른쪽으로 회전, 피연산자의 모든 비트를 오른쪽으로 한 자리 이동, 캐리는 비트 7로 이동, 비트 0은 캐리로 이동- 오피 코드 : 즉시 :
58
, 절대 :5a
, 색인 :5c
- 플래그 :
Z
,N
,C
- 오피 코드 : 즉시 :
ROL
-왼쪽으로 회전, 피연산자의 모든 비트를 왼쪽으로 한 자리 이동, 캐리는 비트 0으로 이동, 비트 7은 캐리로 이동- 오피 코드 : 즉시 :
60
, 절대 :62
, 색인 :64
- 플래그 :
Z
,N
,C
- 오피 코드 : 즉시 :
ADC
-캐리 추가, 피연산자 + 캐리 추가A
, 캐리 오버플로 설정- 오피 코드 : 즉시 :
68
, 절대 :6a
, 색인 :6c
- 플래그 :
Z
,N
,C
- 오피 코드 : 즉시 :
INC
-피연산자를 1 씩 증가- 오피 코드 : 즉시 :
78
, 절대 :7a
, 색인 :7c
- 플래그 :
Z
,N
- 오피 코드 : 즉시 :
DEC
-피연산자를 1 씩 감소- 오피 코드 : 즉시 :
80
, 절대 :82
, 색인 :84
- 플래그 :
Z
,N
- 오피 코드 : 즉시 :
CMP
-A
에서 피연산자를 빼서 피연산자와 비교 하고A
결과를 잊습니다. 언더 플로우시 캐리가 지워지고, 다르게 설정- 오피 코드 : 즉시 :
88
, 절대 :8a
, 색인 :8c
- 플래그 :
Z
,N
,C
- 오피 코드 : 즉시 :
CPX
- 비교X
- 동일CMP
에 대한X
- 오피 코드 : 즉시 :
90
, 절대 :92
, 색인 :94
- 플래그 :
Z
,N
,C
- 오피 코드 : 즉시 :
HLT
-종료- 오피 코드 : 암시 적 :
c0
- 오피 코드 : 암시 적 :
INX
- 증가X
하나- 오피 코드 : 암시 적 :
c8
- 플래그 :
Z
,N
- 오피 코드 : 암시 적 :
DEX
- 감소X
하나- 오피 코드 : 암시 적 :
c9
- 플래그 :
Z
,N
- 오피 코드 : 암시 적 :
SEC
-캐리 플래그 설정- 오피 코드 : 암시 적 :
d0
- 플래그 :
C
- 오피 코드 : 암시 적 :
CLC
-명확한 운반 깃발- 오피 코드 : 암시 적 :
d1
- 플래그 :
C
- 오피 코드 : 암시 적 :
BRA
-항상 지점- 오피 코드 : 상대 :
f2
- 오피 코드 : 상대 :
BNE
-Z
플래그가 지워 지면 분기- 오피 코드 : 상대 :
f4
- 오피 코드 : 상대 :
BEQ
-Z
플래그가 설정된 경우 분기- 오피 코드 : 상대 :
f6
- 오피 코드 : 상대 :
BPL
-N
플래그가 지워 지면 분기- 오피 코드 : 상대 :
f8
- 오피 코드 : 상대 :
BMI
-N
플래그가 설정된 경우 분기- 오피 코드 : 상대 :
fa
- 오피 코드 : 상대 :
BCC
-C
플래그가 지워 지면 분기- 오피 코드 : 상대 :
fc
- 오피 코드 : 상대 :
BCS
-C
플래그가 설정된 경우 분기- 오피 코드 : 상대 :
fe
- 오피 코드 : 상대 :
오피 코드
위 목록에서 유효한 명령어로 매핑되지 않은 opcode가 발견되면 VM의 동작이 정의되지 않습니다.
당 조나단 앨런의 요청 , 당신은 할 수 있습니다 대신에 표시된 옵 코드의 옵 코드의 자신의 세트를 선택 지침 섹션을 참조하십시오. 그렇게하면 위에서 답변에 사용 된 opcode에 전체 매핑을 추가 해야합니다 .
매핑은 쌍이있는 16 진 파일이어야합니다 ( <official opcode> <your opcode>
예 : 두 개의 opcode를 교체 한 경우).
f4 f5
10 11
개행은 여기서 중요하지 않습니다.
테스트 사례 (공식 opcode)
// some increments and decrements
pc: 0
ram: 10 10 7a 01 c9 f4 fb
output: 10 20 7a 01 c9 f4 fb
// a 16bit addition
pc: 4
ram: e0 08 2a 02 02 00 6a 02 0a 00 02 01 6a 03 0a 01
output: 0a 0b 2a 02 02 00 6a 02 0a 00 02 01 6a 03 0a 01
// a 16bit multiplication
pc: 4
ram: 5e 01 28 00 10 10 4a 01 5a 00 fc 0d 02 02 d1 6a 21 0a 21 02 03 6a 22 0a 22 52
02 62 03 c9 f8 e6 c0 00 00
output: 00 00 00 00 10 10 4a 01 5a 00 fc 0d 02 02 d1 6a 21 0a 21 02 03 6a 22 0a 22 52
02 62 03 c9 f8 e6 c0 b0 36
나중에 더 많은 테스트 케이스를 추가 할 수 있습니다.
참조 및 테스트
자체 실험에 도움을주기 위해 여기에 (완전히 골프화되지 않은) 참조 구현 이 있습니다. 추적 정보 (분해 된 명령 포함)를 출력 stderr
하고 실행 중에 opcode를 변환 할 수 있습니다.
소스를 얻는 권장 방법 :
git clone https://github.com/zirias/gvm --branch challenge --single-branch --recurse-submodules
또는 체크 아웃 브랜치 challenge
및 git submodule update --init --recursive
복제 후 사용자 정의 빌드 시스템을 얻으십시오.
GNU make를 사용하여 도구를 빌드하십시오 (type make
또는 gmake
시스템에서 기본 make가 GNU make가 아닌 경우).
사용법 :gvm [-s startpc] [-h] [-t] [-c convfile] [-d] [-x] <initial_ram
-s startpc
-초기 프로그램 카운터, 기본값은0
-h
-입력이 16 진입니다 (그렇지 않으면 이진).-t
-추적 실행stderr
-c convfile
-주어진 매핑에 따라 opcode를 변환convfile
-d
-결과 메모리를 이진 데이터로 덤프-x
-결과 메모리를 16 진수로 덤프initial_ram
-초기 RAM 내용 (16 진 또는 2 진)
참고 실행 수정 옵 코드 동안 프로그램에 실패 변환 기능을.
면책 조항 : 위의 규칙과 사양은이 도구가 아닌 도전에 대한 권한입니다. 이것은 특히 opcode 변환 기능에 적용됩니다. 여기에 제시된 도구에 사양에 대한 버그가 있다고 생각되면 의견을 보내주십시오. :)
BRA
( "항상 지점") 제어 흐름에 지점을 소개하지 않습니다, 그것은 호출 할 수 없습니다 JMP
?
BRA
은 65C02 및 MC 68000과 같은 최신 칩 설계에 존재합니다 (6502에는 이러한 명령어가 없음) JMP
. 차이점은 BRA
상대 주소 지정과 JMP
절대 주소 지정을 사용 한다는 것 입니다. 방금이 디자인을 따랐습니다. 실제로 그렇게 논리적으로 들리는 것은 아닙니다.)