📌 문제
디지털하드웨어설계 과목의 최종 프로젝트는 16-bit CPU를 설계하고 Verilog 언어로 구현하는 것이다. 본인이 구현한 CPU가 제대로 동작하는지 테스트하기 위해서는 기계어 코드를 입력으로 주어야 한다. 하지만 대부분의 사람은 0과 1로만 이루어진 기계어 코드를 이해하기 힘들어서 C++, Java와 같은 프로그래밍 언어로 코드를 작성하고 컴파일러를 통해 기계어 코드로 번역하는 과정을 거친다.
여러 가지 프로그래밍 언어 중에서 어셈블리어는 사람이 이해하기 쉬우면서 기계어와 가장 유사한 언어이다. 어셈블리어 코드는 어셈블러를 통해 기계어 코드로 번역된다. 그리고 어셈블리어는 기계어와 일대일로 대응하는 특징이 있다. 예를 들면, 두 수의 합을 구하는 연산의 어셈블리어 코드가 ADD이고, 기계어 코드가 00000이면 어셈블러는 ADD를 읽어서 그대로 00000로 바꾸어주는 것이다.
아래의 그림은 민호가 설계한 CPU가 처리할 수 있는 16-bit 단위 명령어들의 구조를 모아놓은 표이다.
입력과 출력은 항상 명령어 단위이며, 어셈블리어 코드는 "opcode rD rA rB" 또는 "opcode rD rA #C"의 형태이다. 기본적으로 레지스터 rA와 rB에 있는 두 수 또는 레지스터 rA에 있는 수와 상수 #C를 opcode에 해당하는 연산을 수행하고, 그 결괏값을 레지스터 rD에 저장하는 명령어이다. rA는 opcode에 따라 사용하지 않을 수도 있다. 어셈블러는 opcode, rD, rA, rB, #C를 각 bit의 자리에 맞게 2진수 0과 1로 이루어진 16-bit 기계어 코드로 변역한다. bit마다 자리의 의미는 아래와 같다.
- 0~4 : CPU가 수행해야 할 연산을 나타내는 opcode이다. 만약 4번 bit가 0일 경우 레지스터 rB를, 1일 경우 상수 #C를 사용한다.
- 5 : 사용하지 않는 bit이며, 항상 0이다.
- 6~8 : 결괏값을 저장하는 레지스터 rD의 번호이다.
- 9~11 : 연산에 사용되는 레지스터 rA의 번호이다. 사용하지 않을 경우에는 000이다.
- 12~15 : 만약 4번 bit가 0일 경우 12~14번 bit는 연산에 사용되는 레지스터 rB의 번호이며, 15번 bit는 항상 0이다. 만약 4번 bit가 1일 경우 12~15번 bit는 상수 #C이다.
디지털하드웨어설계 과목을 듣는 민호는 Verilog로 16-bit CPU 구현을 일찍 끝내 놓은 상태이다. 이 16-bit CPU를 테스트하기 위해서는 기계어를 매번 입력으로 줘야 하는데, 너무나 귀찮은 민호는 이에 맞는 어셈블러를 구현하려고 한다. 민호가 직접 설계한 16-bit CPU의 명령어 구조 표를 보고, 어셈블리어 코드가 주어졌을 때 이를 기계어 코드로 번역하는 어셈블러를 만들어보자.
📌 입력
첫 번째 줄에는 명령어의 개수를 의미하는 정수 N (1 ≤ N ≤ 500)이 주어진다.
다음 N개의 각 줄에는 명령어가 어셈블리어 코드로 "opcode rD rA rB" 또는 "opcode rD rA #C"의 형태로 주어진다. 문자열 opcode는 항상 대문자이다. 정수 rD, rA, rB (0 ≤ rD, rA, rB ≤ 7)는 레지스터 번호를 의미한다. 사용하는 레지스터 번호는 1부터 7까지이며, 사용하지 않을 경우에만 0이 주어진다. 정수 #C (0 ≤ #C ≤ 15)는 상수를 의미한다.
기계어 코드로 번역될 때 어긋나는 입력은 주어지지 않는다.
📌 출력
N개의 각 줄에 어셈블리어 코드를 기계어 코드로 번역하여 출력한다.
📌 예제 입출력
📌 풀이
알아야 하는 것
- toString(n): 10진수를 2진수로 변환한다. (2진수를 10진수로 변환할 시에는 parseInt(n, 10)으로 쓴다.)
- str.padStart(목표 문자열의 길이, 채울 문자): 1번째 파라미터 만큼 현재 문자열의 앞을 2번째 파라미터로 채운다.
- str.padEnd는 반대로 문자열의 뒤부터 채운다.
구할 것
- 문제 이해하기
- opcode + 4번(0 or 1) + 5번 (0) + 6~8번 (rD) + 9~11번 (rA or 000) + 12~15번 (4번이 0이라면 rB + 0, 1이라면 #C)
- rD, rA, rB, rC 모두 2진수로 표현해야 한다.
- 먼저 4번까지 opcode 문자를 키로, 코드를 밸류로 잡고, rD와 rA를 2진수로 변환한 후 padStart로 3자리를 맞춰준다.
- 12~15번이 rB + 0일지 #C일지는 4번 코드에 따라 달라지므로 opcode + 4번의 마지막 인덱스 (4번)가 무엇인지에 따라 나눠지게 삼항연산자를 사용한다.
- 만약 0이라면 총 4자리에서 rB가 3자리, 나머지 0이 1자리를 차지해야 하므로 padStart 3으로 작성
- 만약 1이라면 #C를 4자리로 사용할 수 있으므로 padStart 4으로 작성
- 구조 분해 할당으로 모두 동일한 인덱스를 갖고 있으므로 한 번 순회할 때마다 그 값을 빈 배열 arr에 추가한다.
const fs = require("fs");
const input = fs.readFileSync("./dev_stdin.txt").toString().trim().split("\n");
const code = input.splice(1).map((v) => v.split(" "));
const opcodes = {
ADD: "00000",
ADDC: "00001",
SUB: "00010",
SUBC: "00011",
MOV: "00100",
MOVC: "00101",
AND: "00110",
ANDC: "00111",
OR: "01000",
ORC: "01001",
NOT: "01010",
MULT: "01100",
MULTC: "01101",
LSFTL: "01110",
LSFTLC: "01111",
LSFTR: "10000",
LSFTRC: "10001",
ASFTR: "10010",
ASFTRC: "10011",
RL: "10100",
RLC: "10101",
RR: "10110",
RRC: "10111",
};
let arr = [];
for (const [opcode, rD, rA, rBorC] of code) {
const opcodeStr = opcodes[opcode];
const rDnum = Number(rD).toString(2).padStart(3, "0");
const rAnum = Number(rA).toString(2).padStart(3, "0");
const rBorCnum =
opcodeStr[opcodeStr.length - 1] === "0"
? Number(rBorC).toString(2).padStart(3, "0") + "0"
: Number(rBorC).toString(2).padStart(4, "0");
arr.push(opcodeStr + "0" + rDnum + rAnum + rBorCnum);
}
console.log(arr.join("\n"));
opcodes 부분이 노답이라 바꾸고 싶다. C가 붙느냐 안 붙느냐로 나눠지는 것 같았는데 NOT이 있고, C 있는 코드 / C 없는 코드로 나눠도 똑같이 써야 해서 고민중. 아니면 C를 include 하고 있느냐에 따라 1을 더하는게 나은가 싶기도 한데...
아래는 리팩토링 하기 전 코드. 비슷한데 반복문이 2번 들어 있어서 좀 더 복잡한듯.
구조분해 할당을 쓰긴 했는데 좀 효율적으로 쓰지 못해서 아래 부분들을 묶어 위 코드가 되었다.
let opcodeStr = [];
let rDnum = [];
let rAnum = [];
let rBorCnum = [];
for (let i = 0; i < code.length; i++) {
const [opcode, rD, rA, rBorC] = code[i];
opcodeStr.push(opcodes[opcode]);
rDnum.push(Number(rD).toString(2).padStart(3, "0"));
rAnum.push(Number(rA).toString(2).padStart(3, "0"));
if (opcodeStr[i][opcodeStr[i].length - 1] === "0") {
rBorCnum.push(Number(rBorC).toString(2).padStart(3, "0") + "0");
} else {
rBorCnum.push(Number(rBorC).toString(2).padStart(4, "0"));
}
}
let answer = [];
for (let i = 0; i < opcodeStr.length; i++) {
answer.push(opcodeStr[i] + "0" + rDnum[i] + rAnum[i] + rBorCnum[i]);
}
console.log(answer.join("\n"));
'⚙️ 코딩테스트' 카테고리의 다른 글
[JavaScript] 백준 코딩테스트 10709. 실버 5 : 기상 캐스터 (0) | 2023.07.21 |
---|---|
[JavaScript] 백준 코딩테스트 1417. 실버 5 : 국회의원 선거 (0) | 2023.07.20 |
[JavaScript] 백준 코딩테스트 10810. 브론즈 3 : 공 넣기 (0) | 2023.07.18 |
[JavaScript] 백준 코딩테스트 3568. 실버 3 : iSharp (0) | 2023.07.17 |
[JavaScript] 백준 코딩테스트 1303. 실버 1 : 전쟁 - 전투 (0) | 2023.07.13 |