JavaScript에서 fantasy CPU를 위한 시뮬레이터
소개
시뮬레이터는 사용자가 다른 시스템에서 완전히 다른 시스템을 실행할 수 있도록 하는 멋진 기술이다.
아날로그는 ARM 장치에서 x86 프로그램을 실행하거나 x86 Windows 데스크톱에서 ARM Android 프로그램을 실행하거나 라즈베리 파이의 아날로그 컴퓨터/콘솔에서 당신이 가장 좋아하는 복고 게임을 실행하는 등 광범위한 응용 프로그램을 가지고 있다.
전체 시스템을 시뮬레이션할 때, 시뮬레이터 소프트웨어는 이 시스템의 모든 하드웨어 장치를 처리해야 한다.이것들은 CPU뿐만 아니라 영상 시스템, 입력 장치 등도 포함할 수 있다. 그러나 시뮬레이터의 핵심 개념은 여전히 시뮬레이션 CPU이다.
본고에서 우리는 CPU가 어떻게 작동하는지, 가상 CPU로 작성된 프로그램을 실행하는 간단한 기계를 통해 소프트웨어에서 모방하는 방법을 탐색할 것이다.
프로그램은 일련의 바이트일 뿐이다
모두가 알다시피 CPU는 기계의 핵심이고 그 주요 임무는 프로그램을 실행하는 것이다.프로그램은 단지 컴퓨터 메모리의 일련의 지령일 뿐이다.
CPU가 JavaScript를 알고 있다고 믿는 경향이 있을 수 있습니다.이것은 매우 매력적인 개념이지만 사실은 그렇지 않다.새 버전의 JavaScript가 나타나면 CPU가 변경됩니다!
사실 CPU는 제한된 명령 집합만 이해할 수 있다.CPU 엔지니어링 팀이 CPU를 설계할 때 이해해야 할 지침입니다.
사용자는 이러한 명령을 직접 사용하거나 고급 언어로 인코딩한 다음 컴파일러/시스템을 사용하여 프로그램을 CPU가 이해할 수 있는 명령 집합으로 번역하여 프로그램을 만들 수 있습니다.
사용자가 프로그램을 어떻게 만드든지 간에 명령은 최종적으로 일련의 바이트 형식으로 메모리에 나타난다. 예를 들어
11,0,10,42,6,255,30,0,11,0,0,11,1,1,11,3,1,60,1,10,2,0,20,
2,1,60,2,10,0,1,10,1,2,11,2,1,20,3,2,31,2,30,2,41,3,2,19,31,0,50
CPU의 역할은 메모리에서 이러한 명령을 가져오고 실행하는 것입니다.이 점에서 위의 숫자 시리즈를 다시 보십시오.이것은 우리의 환상적인 CPU를 위한 실제 프로그램이다.그 역할을 이해하기 위해서 우리는 소프트웨어에서 가상 CPU를 실현해야 한다. 그리고 우리는 그것을 사용하여 프로그램을 실행할 것이다.
Fantasy CPU 고급 사양
우리가 소프트웨어에서 시뮬레이션할 CPU는 환상이다.그것은 현실 세계에는 존재하지 않지만, 실제 CPU의 작업 방식에 매우 가깝다.이 기계의 일부 영감은 본문에서 나온다.
CPU에는 R0, R1, R2, R3 등 4개의 공통 디지털 레지스터가 있습니다.이 레지스터 외에도 CPU는 스택을 조작할 수 있으며, 값을 스택으로 밀어넣거나 꺼낼 수 있다.
CPU는 명령을 통해 작동합니다.일부 지령은 조작수가 없고, 다른 지령은 여러 개의 조작수가 있다. (조작수를 매개 변수로 간주한다.)
일련의 지령들이 하나의 프로그램을 구성한다.명령은 프로그램에서 다음과 같이 인코딩됩니다.
INSTRUCTION1 [OPERAND1] [OPERAND2] [OPERAND3] INSTRUCTION2 [OPERAND1] ...
모든 지령에는 유일한 번호가 있다.간단하게 말하자면, 명령 코드, 조작 수와 짝수 주소는 모두 정규수이다.따라서 바이트나 다른 데이터 형식은 필요하지 않습니다.모든 것이 숫자다!그래서 우리의 프로그램은 일련의 숫자다.각 숫자는 하나의 메모리 단위를 차지한다.예를 들어 3개의 조작 수를 가진 명령은 4개의 프로그램 메모리 단원 (명령 코드 1개, 조작 수 3개) 을 차지한다.
설명서
이제 CPU가 받아들인 명령 집합을 살펴보겠습니다.모든 CPU (우리의 fantasy one 포함) 는 2진법으로 명령을 실행하지만, CPU 엔지니어링 팀은 일반적으로 이름/부호를 CPU 식별 명령과 연결합니다.
Using mnemonics makes the task of writing programs much easier for humans. If one writes programs using the instructions mnemonics it is said that he codes in assembly language. It only takes a simple step to transform these programs written in assembly language into binary instructions (e.g. machine language).
가상 CPU의 명령 및 부호는 매우 간단하고 직관적입니다.우리는 아래의 모든 내용을 묘사하는 데 시간을 들일 것이다.CPU를 소프트웨어에 모방하려면 모든 CPU 명령을 알아야 하기 때문입니다.
참고: 각 명령에는 특정 명령을 인코딩하는 데 사용할 숫자가 지정됩니다.레지스터 R0, R1, R2, R3도 숫자 0, 1, 2, 3으로 인코딩됩니다.
regsrc에서 regdst로 값을 불러옵니다.E、 g.regdst=regsrc
MOVR reg_dst, reg_src
MOVR = 10
레지스터regdst에 수치를 불러옵니다.E, g.regdst=값MOVV reg_dst, value
MOVV = 11
regsrc에서 온 값을regdst에 추가하고 결과를 regudst에 저장합니다ADD reg_dst, reg_src
ADD = 20
regdst의 값에서 regsrc의 값을 빼고 결과를 reg\U dst에 저장합니다SUB reg_dst, reg_src
SUB = 21
스택에서 reg_src 값 밀어넣기PUSH reg_src
PUSH = 30
스택에서 마지막 값을 꺼내서 레지스터 reg\U dst에 불러옵니다.POP reg_dst
POP = 31
주소addr로 이동합니다.GOTO 비슷해!JP addr
JP = 40
reg1의 값이 JL reg_1, reg_2, addr
JL = 41
호출된 명령 주소를 창고에 밀어 넣고 주소addr로 건너뛰기CALL addr
CALL = 42
스택에서 마지막 숫자를 꺼내서 주소라고 가정하고 이 주소로 이동합니다.RET
RET = 50
레지스터reg에 포함된 값을 화면에 인쇄합니다PRINT reg
PRINT = 60
VM을 중지합니다.HALT가 발생하면 가상 CPU에서 명령을 실행하지 않습니다.HALT
HALT = 255
아날로그 CPU
fantasy의 CPU 규격이 생겼습니다. 우리는 이제 소프트웨어에서 CPU를 모의할 수 있습니다. 자바스크립트를 사용합니다.
위에서 말한 바와 같이, 진정한 기계에서는 프로그램이 메모리에 저장된다.우리의 시뮬레이터에서는 간단한 진열 구조의 시뮬레이션 메모리를 사용할 것이다.사실 우리는 메모리에 프로그램 하나만 놓을 뿐이다.
let program = [11,0,10,42,6,255,30,0,11,0,0,11,1,1,11,3,1,60,1,10,2,0,20,
2,1,60,2,10,0,1,10,1,2,11,2,1,20,3,2,31,2,30,2,41,3,2,19,31,0,50];
우리의 가상 CPU는 이 그룹에서 하나씩 지령을 추출하여 실행해야 한다.CPU는 PC(프로그램 카운터)라는 전용 레지스터를 사용하여 필요한 명령을 추적합니다.Fact: PC register is a real register on many physical CPU architectures.
CPU 시뮬레이터의 핵심은 단지 하나의 큰 "스위치"문장일 뿐, 규범에 따라 모든 명령을 처리할 것이다.
let pc = 0;
let halted = false;
function run()
{
while(!halted)
{
runone();
}
}
function runone()
{
if (halted)
return;
let instr = program[pc];
switch(instr)
{
// handle each instruction according to specs
// also advance pc to prepare for the next fetch
// ...
}
}
이게 다야!이것은 우리의 뛰어난 CPU 시뮬레이터의 구조이다.지령을 처리하는 것도 매우 간단한 임무이다.설명의 규격을 자세히 읽고 집행하기만 하면 된다.switch(instr)
{
// movr rdst, rsrc
case 10:
pc++;
var rdst = program[pc++];
var rsrc = program[pc++];
regs[rdst] = regs[rsrc];
break;
// movv rdst, val
case 11:
pc++;
var rdst = program[pc++];
var val = program[pc++];
regs[rdst] = val;
break;
...
}
천천히 규범을 읽고 이 가상 CPU를 완성할 수 있는지 봅시다.실행 프로그램의 결과를 보았을 때, 너는 네가 매우 잘했다는 것을 알게 될 것이다.인코딩을 자세히 하면, 프로그램은 상위 10개의 피보나치 수를 표시해야 한다.
너도 이 운동장에서 우리의 판본을 찾아볼 수 있다.
https://codeguppy.com/code.html?simple_vm/vm0_fibonacci
로드 프로그램
위의 놀이터에서 보듯이 저희 시뮬레이터는 간단한 가상 머신에 포함되어 있습니다. 이 가상 머신은 처음에 바이트 그룹에서 프로그램을 불러오고 가상 CPU에 실행을 요구합니다.
vm.load([11,0,10,42,6,255,30,0,11,0,0,11,1,1,11,3,1,60,1,10,2,0,20,
2,1,60,2,10,0,1,10,1,2,11,2,1,20,3,2,31,2,30,2,41,3,2,19,31,0,50]);
만약 당신의 유일한 목적이 우리가 제공한 프로그램을 불러오는 것이라면, 이렇게 불러오는 것은 가능합니다.그러나 기계 언어로 프로그램을 설계하고 실행하려면 매우 직관적이거나 효율적인 방법이 아닐 수도 있습니다.인성화된 CPU 명령 부호로 프로그램을 불러올 수 있는 작은 기술을 소개해 드리겠습니다.몇 개의 상수만 정의하면 됩니다.
const MOVR = 10;
const MOVV = 11;
const ADD = 20;
const SUB = 21;
const PUSH = 30;
const POP = 31;
const JP = 40;
const JL = 41;
const CALL = 42;
const RET = 50;
const PRINT= 60;
const HALT = 255;
const R0 = 0;
const R1 = 1;
const R2 = 2;
const R3 = 3;
vm.load([
MOVV, R0, 10,
CALL, 6,
HALT,
// PrintFibo: (addr = 6)
PUSH, R0,
MOVV, R0, 0,
MOVV, R1, 1,
MOVV, R3, 1,
PRINT, R1,
// continue: (addr = 19)
MOVR, R2, R0,
ADD, R2, R1,
PRINT, R2,
MOVR, R0, R1,
MOVR, R1, R2,
MOVV, R2, 1,
ADD, R3, R2,
POP, R2,
PUSH, R2,
JL, R3, R2, 19,
POP, R0,
RET
]);
이 기술을 사용하면 JavaScript에서 어셈블리 언어로 프로그래밍하는 것과 같습니다.너는 이 운동장에서 보조 기호가 있는 새로운 프로그램을 찾을 수 있다.
https://codeguppy.com/code.html?simple_vm/vm1_fibonacci
이 점에서 우리는 우리의 문장을 끝낼 수 있다.그러나 우리는 이 기회를 빌려 기계 언어 코드를 처리할 때 자주 사용하는 작은 도구를 신속하게 실현할 것이다.
어셈블리 프로그램
기계 언어 프로그램을 사용할 때 가장 중요한 도구는 아마도 어셈블리 프로그램일 것이다.
이 프로그램은 사용자가 텍스트 파일 형식으로 프로그램을 작성하고 더욱 사용하기 쉬운 어셈블리 언어를 사용할 수 있도록 하며, 어셈블리 프로그램은 어셈블리 원본 코드를 CPU가 이해할 수 있는 2진 데이터로 변환하여 복잡한 작업을 완성할 수 있다.
우리는 기본적인 작업을 완성하기 위해 간소화된 어셈블리 프로그램을 구축하려고 시도할 것이다.작동 방식은 다음과 같습니다.
let code = `
// Loads value 10 in R0
// and calls Fibonacci routine
MOVV R0, 10
CALL 6
HALT
...
`;
let bytes = asm.assemble(code);
코드를 보려면 이 놀이터를 확인하십시오.https://codeguppy.com/code.html?simple_vm/vm3_assembler
어셈블러
기계 언어를 사용할 때 구축할 수 있는 또 다른 유용한 도구는 어셈블리 프로그램이다.
어셈블러는 2진 형식의 프로그램을 입력으로 하고 인간이 읽을 수 있는 방식(어셈블러 언어)으로 목록을 출력합니다.
다음 프로그램에서 실행할 때:
let src = asm.disassemble(
[11,0,10,42,6,255,30,0,11,0,0,11,1,1,11,3,1,60,1,10,2,0,20,
2,1,60,2,10,0,1,10,1,2,11,2,1,20,3,2,31,2,30,2,41,3,2,19,31,0,50]
);
... 우리의 어셈블러는 다음과 같은 내용을 출력해야 한다.0 11 0 10 MOVV R0, 10
3 42 6 CALL 6
5 255 HALT
6 30 0 PUSH R0
8 11 0 0 MOVV R0, 0
11 11 1 1 MOVV R1, 1
14 11 3 1 MOVV R3, 1
17 60 6 PRINT R1
19 10 2 0 MOVR R2, R0
22 20 2 1 ADD R2, R1
25 60 6 PRINT R2
27 10 0 1 MOVR R0, R1
30 10 1 2 MOVR R1, R2
33 11 2 1 MOVV R2, 1
36 20 3 2 ADD R3, R2
39 31 2 2 POP R2
41 30 2 PUSH R2
43 41 3 2 19 JL R3, R2, 19
47 31 0 2 POP R0
49 50 RET
이 목록에는 메모리 주소와 바이너리 명령, 프로그램의 모든 명령에 대한 관련 기호가 포함되어 있습니다.어셈블러의 간단한 구현을 보려면 다음과 같이 하십시오.
https://codeguppy.com/code.html?simple_vm/vm2_disassembler
피보나치 계획에 대해
너는 우리가 어떻게 피보나의 계수를 인쇄하기 위해 어셈블리 프로그램을 생각해 냈는지 알고 싶니?답은 간단하다.먼저 JavaScript를 사용하여 알고리즘을 작성한 다음 어셈블리로 변환합니다.
물론, 일단 당신이 어셈블리에서 글을 쓴 경험이 더 많아지면, 당신은 이 임무를 한 번에 직접 완성할 수 있습니다!이건 연습만 하면 돼!
결론
나는 네가 이 문장을 좋아하길 바란다.설령 전체 시스템을 시뮬레이션하기 전에 해야 할 일이 많다 하더라도, 나는 본고가 당신에게 시뮬레이션 시스템의 핵심(CPU)이 무엇을 해야 하는지를 설명하는 기본적인 개술을 제공할 수 있기를 바랍니다.
빠른 참고를 위해 저는 본고의 모든 프로그램을 함께 놓고 다음 사이트에서 설명을 했습니다.
https://codeguppy.com/code.html?simple_vm/vm
다음으로, 이 가상 CPU를 위한 추가 프로그램을 만들어 주십시오. 필요하면 새 명령으로 CPU 명령 집합을 확장하여 프로그램을 지원하십시오.
Looking forward to your feedback and comments. Please let me know if you want to see more about this subject, perhaps even seeing the implementation of a full emulator. For more recreational coding programs please feel free to browse codeguppy.com
Reference
이 문제에 관하여(JavaScript에서 fantasy CPU를 위한 시뮬레이터), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/codeguppy/implement-an-emulator-for-a-fantasy-cpu-in-javascript-3617텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)