배경
오래된 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-제로 플래그는 일부 작업 결과로 설정된 후0N-일부 연산 결과가 음수로 표시된 후 음수 플래그가 설정 됨 (결과의 비트 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절대 주소 지정을 사용 한다는 것 입니다. 방금이 디자인을 따랐습니다. 실제로 그렇게 논리적으로 들리는 것은 아닙니다.)