Спецификация DCPU-16 Copyright 2012 Mojang Перевод на русский осуществлен IskanderUA и Defend Версия 1.1 (Проверьте 0x10c.com на наличие обновлений) * 16-битные беззнаковые слова (unsigned word) * 0x10000 слов (128 КБ - прим. пер.) в оперативной памяти * 8 регистров (A, B, C, X, Y, Z, I, J) * счётчик команд (program counter) (PC) * указатель стека (stack pointer) (SP) * переполнение (overflow) (O) В этом документе все, что употребляется без [скобок] это условное обозначение для "значения в оперативной памяти по адресу, который указан в скобках". Например, SP - это указатель стека, а [SP] - это значение в оперативной памяти, на которое указывает на указатель стека (stack pointer). Когда процессору нужно прочесть слово, он считывает [PC], и затем увеличивает PC на 1. Условное обозначение для этого действия - [PC++]. В некоторых случаях, процессор может модифицировать значение перед считыванием. Условным обозначением для этого действия является [++PC]. Длина инструкций - 1-3 слова и их действия полностью определяются первым словом. В базовой инструкции "младшие" 4 бита первого слова являются кодом операции (opcode), а оставшееся 12 бит - разделены между 6-битными значения, назваными a и b. a всегда обрабатывается процессором раньше b, и это младшие 6 битов. В битах (где младшый бит является последним) базовая инструкция имеет формат: bbbbbbaaaaaaoooo. Значения: (6 битов) 0x00-0x07: регистр (A, B, C, X, Y, Z, I или J, именно в этом порядке) 0x08-0x0f: [регистр] 0x10-0x17: [следующее слово + регистр] 0x18: Взять, извлечь данные (из стека - прим. перев.) / [SP++] 0x19: Считать данные (из стека - прим. перев.) / [SP] 0x1a: Поместить данные (в стек - прим. перев.) / [--SP] 0x1b: SP 0x1c: PC 0x1d: O 0x1e: Следующее слово 0x1f: Следующее слово (литерал) 0x20-0x3f: Значение литералов 0x00-0x1f (литерал) * "следующее слово" означает "[PC++]". То есть выполняет переход к следующему слову. * Если команда пытается присвоить что-то литералу, то присвоение не происходит. Кроме этого, команда ведет себя как обычно. * Все значения, которые считывают слово (0x10-0x17, 0x1e, и 0x1f) делают 1 цикл для поиска. А остальные - 0 циклов. * При использовании 0x18, 0x19, 0x1a как POP, PEEK и PUSH, в стеке будет задействован обратный порядок памяти (начало по адресу памяти 0xffff). Например: "SET PUSH, 10", "SET X, POP" Базовые коды операций: (4 бита) 0x0: небазовая инструкция - смотри ниже 0x1: SET a, b - устанавливает значение b в a 0x2: ADD a, b - устанавливает значение a+b в a, устанавливает в O значение 0x0001 если случилось переполнение, 0x0 в ином случае 0x3: SUB a, b - устанавливает значение a-b в a, устанавливает в O значение 0xffff если случился выход за нижнюю границу, 0x0 в ином случае 0x4: MUL a, b - устанавливает значение a*b в a, устанавливает в O значение ((a*b)>>16)&0xffff 0x5: DIV a, b - устанавливает значение a/b в a, устанавливает в O значение ((a<<16)/b)&0xffff. если b==0, взамен устанавливает 0 в a и O. 0x6: MOD a, b - устанавливает значение a%b в a. Если b==0, то устанавливать 0 в a. 0x7: SHL a, b - устанавливает значение a<>16)&0xffff 0x8: SHR a, b - устанавливает значение a>>b в a, устанавливает в O значение ((a<<16)>>b)&0xffff 0x9: AND a, b - устанавливает значение a&b в a 0xa: BOR a, b - устанавливает значение a|b в a 0xb: XOR a, b - устанавливает значение a^b в a 0xc: IFE a, b - выполняет следующую операцию только если a==b 0xd: IFN a, b - выполняет следующую операцию только если a!=b 0xe: IFG a, b - выполняет следующую операцию только если a>b 0xf: IFB a, b - выполняет следующую операцию только если (a&b)!=0 * SET, AND, BOR и XOR делают 1 цикл, плюс расход памяти на a и b * ADD, SUB, MUL, SHR, и SHL делают 2 цикла, плюс расход памяти на a и b * DIV и MOD делают 3 цикла, плюс расход памяти на a и b * IFE, IFN, IFG, IFB делают 2 цикла, плюс расход памяти на a и b, плюс 1 цикл если тест не пройден Небазовые команды всегда имеют пустые 4 "младшие" бита, одно значение и 6-битный код операции. В двоичном виде они имеют формат: aaaaaaoooooo0000 Значение (a) - в том же 6-битном формате как и определенное ранее. Небазовые коды операций: (6 бит) 0x00: зарезервировано для будущих расширений 0x01: JSR a - помещает адрес следующей инструкции в стек, и устанавливает значение a в PC 0x02-0x3f: зарезервировано * JSR делает 2 цикла, плюс расход памяти на a. FAQ: В: Почему нет JMP или RET? О: Они не нужны! "SET PC, <цель>" - это одноименная инструкция JMP. Для относительно малых скачков в одно слово, можно даже сделать "ADD PC, <дистанция>" или "SUB PC, <дистанция>". А для RET, просто сделайте "SET PC, POP" В: Как работает переполнение (O)? О: Переполнение (O) устанавливают определенные инструкции (см. выше), но не считывают автоматически. Однако, вы можете использовать его значение из инструкции. Для примера, чтобы сделать 32 бита добавленных 0x12345678 и 0xaabbccdd, сделаем следующее: SET [0x1000], 0x5678 ; "младшее" слово SET [0x1001], 0x1234 ; "старшее" слово ADD [0x1000], 0xccdd ; добавить "младшие" слова, устанавливает в O значения 0 или 1 (в нашем случае 1) ADD [0x1001], O ; добавить 'О' к "старшему"слову ADD [0x1001], 0xaabb ; добавим "старшие" слова, снова установим значение в О (в данном случае 0, так как 0xaabb+0x1235 меньше чем 0x10000) В: Как мне сделать 32-битное или 64-битное деление используя 'O'? О: Это остается в качестве упражнения для читателя. Q: Как насчет легкого примера A: Конечно! Вот пример простого ассемблера и дамп памяти скомпилированного кода: ; Попробуем самые простые вещи SET A, 0x30 ; 7c01 0030 SET [0x1000], 0x20 ; 7de1 1000 0020 SUB A, [0x1000] ; 7803 1000 IFN A, 0x10 ; c00d SET PC, crash ; 7dc1 001a [*] ; Сделаем цикл SET I, 10 ; a861 SET A, 0x2000 ; 7c01 2000 :loop SET [0x2000+I], [A] ; 2161 2000 SUB I, 1 ; 8463 IFN I, 0 ; 806d SET PC, loop ; 7dc1 000d [*] ; Вызов подпрограммы SET X, 0x4 ; 9031 JSR testsub ; 7c10 0018 [*] SET PC, crash ; 7dc1 001a [*] :testsub SHL X, 4 ; 9037 SET PC, POP ; 61c1 ; Зависнет навсегда. X должен быть 0x40 если все прошло правильно. :crash SET PC, crash ; 7dc1 001a [*] ; [*]: Обратите внимание, что код может быть короче и на один цикл быстрее, если использовать короткую форму литералов (0x00-0x1F), ; Но мой ассемблер пока еще не поддерживает короткую форму меток. Полный дамп памяти: 0000: 7c01 0030 7de1 1000 0020 7803 1000 c00d 0008: 7dc1 001a a861 7c01 2000 2161 2000 8463 0010: 806d 7dc1 000d 9031 7c10 0018 7dc1 001a 0018: 9037 61c1 7dc1 001a 0000 0000 0000 0000