diff --git a/Assignments/6_Assembler/assembler.py b/Assignments/6_Assembler/assembler.py new file mode 100644 index 0000000..2bed8aa --- /dev/null +++ b/Assignments/6_Assembler/assembler.py @@ -0,0 +1,118 @@ +import sys +import os +import re + +if (len(sys.argv) > 2): + raise ValueError("Too many parameters") +if (len(sys.argv) == 1): + raise ValueError("Excepted .asm filename") +if (not re.match(r".*\.asm", sys.argv[1])): + raise ValueError(f"{sys.argv[1]} is not valid filename") +if (not os.path.exists(sys.argv[1])): + raise ValueError(f"{sys.argv[1]} not found") + +with open(sys.argv[1], "r") as asm_file: + asm_with_labels = list( + filter(lambda line: len(line) > 0, + map(lambda line: line.strip().replace(" ", "").split("//")[0], + asm_file.readlines()))) + +labels: dict[str, int] = { + "SP": 0x0000, + "LCL": 0x0001, + "ARG": 0x0002, + "THIS": 0x0003, + "THAT": 0x0004, + **{f"R{i}": i for i in range(16)}, + "SCREEN": 0x4000, + "KBD": 0x6000 +} +variables: dict[str, int] = {} + +for i, command in enumerate(asm_with_labels): + if (bool(re.match(r"^\(.+\)$", command))): + labels[command[1:-1]] = -1 + else: + for k in labels.keys(): + if (labels[k] == -1): + labels[k] = i-len(labels.keys())+7+16 + +asm = list(filter(lambda c: not bool( + re.match(r"^\(.+\)$", command)), asm_with_labels)) + +jumps: dict[str, int] = { + "JGT": 0b001, + "JEQ": 0b010, + "JGE": 0b011, + "JLT": 0b100, + "JNE": 0b101, + "JLE": 0b110, + "JMP": 0b111, +} +expressions: dict[str, int] = { + "0": 0b101010, + "1": 0b111111, + "-1": 0b111010, + "D": 0b001100, + "A": 0b110000, + "!D": 0b001101, + "!A": 0b110001, + "-D": 0b001111, + "-A": 0b110011, + "D+1": 0b011111, + "A+1": 0b110111, + "D-1": 0b001110, + "A-1": 0b110010, + "D+A": 0b000010, + "D-A": 0b010011, + "A-D": 0b000111, + "D&A": 0b000000, + "D|A": 0b010101, +} + + +hack: list[str] = [] + +for command in asm: + if (command[0] == "@"): + if (command[1:].isnumeric()): + hack.append(bin(int(command[1:]))[2:].zfill(16)) + else: + if (command[1:] in labels.keys()): + hack.append(bin(labels[command[1:]])[2:].zfill(16)) + else: + if (not (command[1:] in variables.keys())): + variables[command[1:]] = \ + max(list(filter(lambda address: address >= 15 and address < 0x4000, + [15, *variables.values()])))+1 + hack.append(bin(variables[command[1:]])[2:].zfill(16)) + else: + memory, compute, destination, jump = 0, 0, 0, 0 + if (";" in command): + jump = jumps[command[command.find(";")+1:]] + command = command[:command.find(";")] + if ("=" in command): + destinations = command[:command.find("=")] + if ("M" in destinations): + destination += 0b001 + if ("D" in destinations): + destination += 0b010 + if ("A" in destinations): + destination += 0b100 + command = command[command.find("=")+1:] + if ("M" in command): + memory = 1 + command = command.replace("M", "A") + compute = expressions[command] + bin_code = 0b111_0_000000_000_000 + bin_code |= memory << 12 + bin_code |= compute << 6 + bin_code |= destination << 3 + bin_code |= jump + hack.append(bin(bin_code)[2:].zfill(16)) + +with open(sys.argv[1].replace(".asm", ".hack"), "w") as hack_file: + for i, command in enumerate(hack): + hack_file.write(f"{command}") + if (i != len(hack)-1): + hack_file.write("\n") diff --git a/README.md b/README.md index 4eb17fd..1e3b7ae 100644 --- a/README.md +++ b/README.md @@ -6,19 +6,20 @@ В этом репозитории я сохраняю свои работы в ходе прохождения курса [Nand2tetris](https://www.nand2tetris.org) ### Программа курса с моим описанием содержания каждой главы: -- #### Hardware - - [Project 1: Boolean Logic](./Assignments/1_Boolean_Logic) +- #### **Hardware** + - **[Project 1: Boolean Logic](./Assignments/1_Boolean_Logic)** > Реализация [NAND-логики](https://en.wikipedia.org/wiki/NAND_logic), то есть создание основных логических блоков (AND, OR, NOT, XOR, MUX, DMUX, а так же их версий для работы с 16-битной шиной) с помощью операции NAND ([И-НЕ или Штрих Шеффера](https://ru.wikipedia.org/wiki/Штрих_Шеффера)) - - [Project 2: Boolean Arithmetic](./Assignments/2_Boolean_Arithmetic/) + - **[Project 2: Boolean Arithmetic](./Assignments/2_Boolean_Arithmetic/)** > Создание [простого арифметико-логического устройства (ALU)](./Assignments/2_Boolean_Arithmetic/ALU.hdl) с помощью логических блоков из первого проекта, способного складывать и вычитать 16-битные числа - - [Project 3: Memory](./Assignments/3_Sequential_Logic/) + - **[Project 3: Memory](./Assignments/3_Sequential_Logic/)** > Вводим единицу времени - такт, за счёт чего появляется текущее и следующее состояние, которое можно запоминать и изменять. Создаём простейшую память. На основе DFF компонента создаём [однобитный регистр](./Assignments/3_Sequential_Logic/Bit.hdl), затем [16-битный регистр](./Assignments/3_Sequential_Logic/Register.hdl), из них собираем блоки оперативной памяти ([RAM8](./Assignments/3_Sequential_Logic/RAM8.hdl), [RAM64](./Assignments/3_Sequential_Logic/RAM64.hdl), [RAM512](./Assignments/3_Sequential_Logic/RAM512.hdl), [RAM4K](./Assignments/3_Sequential_Logic/RAM4K.hdl), [RAM16K](./Assignments/3_Sequential_Logic/RAM16K.hdl)), а так же создаём простой [счётчик](./Assignments/3_Sequential_Logic/PC.hdl), который может использоваться для хранения текущей выполняемой инструкции и перехода к новой инструкции - - [Project 4: Machine Language](./Assignments/4_Machine_Language/) + - **[Project 4: Machine Language](./Assignments/4_Machine_Language/)** > Разбираемся с тем, что такое машинный код и как компьютер выполняет комманды записанные с его помощью. Вводим понятие ассемблера, и изучаем язык ассемблера для создаваемой платформы. [Пишем пару простых программ](./Assignments/4_Machine_Language/), в том числе [реализуем простое чтение данных с клавиатуры и вывод картинки на эмулятор экрана](./Assignments/4_Machine_Language/Fill.asm) - - [Project 5: Computer Architecture](./Assignments/5_Computer_Architecture/) + - **[Project 5: Computer Architecture](./Assignments/5_Computer_Architecture/)** > Завершаем работу над аппаратной составляющей компьютера. [Собираем модуль памяти](./Assignments/5_Computer_Architecture/Memory.hdl), позволяющий, в том числе, взаимодействовать с клавиатурой и экраном. [Собираем ЦПУ](./Assignments/5_Computer_Architecture/CPU.hdl) из ранее созданных ALU, счётчика и регистров. Из памяти, ЦПУ и чипа ROM с набором инструкций [собираем компьютер Hack](./Assignments/5_Computer_Architecture/Computer.hdl) -- #### Software - - Project 6: Assembler +- #### **Software** + - **[Project 6: Assembler](./Assignments/6_Assembler/)** + > На практике изучаем как язык ассемблера компилируется в двоичный код, [создавая ассемблер для компьютера Hack](./Assignments/6_Assembler/assembler.py) - Project 7: VM I: Stack Arithmetic - Project 8: VM II: Program Control - Project 9: High-Level Language