Лисья Нора

Оглавление


§ Инструкции MOV

Пожалуй, самой простой инструкцией, которую я могу считать после NOP, это MOV. Ее сделать легко, поскольку потребуется лишь скопировать с одного в другой источник данные. К тому же, достаточно простым кодом можно сразу же охватить все 64 MOV. Ниже представлен этот код.
8'b01xx_xxxx: case (t)
 
0: begin cp <= hl; sw <= 1; if (m53 && m20) pc <= pc; end
1: begin
 
n <= opc[5:3];
b <= !m53;
we <= m53;
d <= op20;
out <= op20;
 
end
4: begin sw <= 0; t <= (m53 || m20) ? 5 : 0; end
6: begin t <= 0; end
 
endcase
Этот код охватывает даже HALT, останов процессора. На проводе m53 появляется 1 тогда, когда в качестве операнда приемника (слева) указана память. Аналогично с m20, но указана память из операнда источника (справа). Если указаны и операнд приемник, и источник как память, то процессор останавливает свое выполнение в том плане, что не двигается никуда PC и он постоянно читает из HL байт и пишет обратно байт в HL.
На такте #1 классическая схема записи в регистр или память – все зависит от того, какой будет приемник. Если m53=1, то есть, приемник это память, то we=1, b=0, и op2 (через out) записывается в память. Если же там регистр, то он выбирается в n и туда записывается d (8 бит).
На такте #4 выбирается количество тактом. В случае если либо в операнде приемнике, либо в источнике указана память, то такая инструкция будет 7Т исполняться, иначе 5Т.

§ Арифметико-логическое устройство

Это ядро, можно даже сказать, сердце процессора. Именно обращение к инструкциям перемещения данных и работы с ними, обработки, занимают большую часть всей программы. Обычно именно по этой причине модули АЛУ греются в процессорах больше, так как количество переходов состояния из 0 в 1 и наоборот здесь наиболее частое.
Всего 8 инструкции АЛУ:
Там где ANA, XRA и ORA в качестве операнда используется регистр или память, а там где ANI, XRI, ORI – то используется непосредственный операнд, который идет за опкодом.
Отличие инструкции SUB от CMP в том, что результат не записывается для CMP, но флаги пишутся для обоих. Составим небольшой код, по сути, являющийся мультиплексором на 8 входов.
wire [8:0] alur =
opc[5:3] == ADD ? a + op20 : // ADD
opc[5:3] == ADC ? a + op20 + psw[CF] : // ADC
opc[5:3] == SBB ? a - op20 - psw[CF] : // SBB
opc[5:3] == AND ? a & op20 : // ANA
opc[5:3] == XOR ? a ^ op20 : // XRA
opc[5:3] == OR ? a | op20 : // ORA
a - op20; // SUB|CMP
В зависимости от выбранного opc[5:3] (ALU MODE), будет записана определенная операция в alur. Основное вычисление производится именно здесь. Теперь необходимо рассчитать флаги.
wire sf = alur[7];
wire zf = alur[7:0] == 0;
wire hf = a[4] ^ op20[4] ^ alur[4];
wire af = (a[4] | op20[4]) & (opc[5:3] == AND);
wire pf = ~^alur[7:0];
wire cf = alur[8];
Объяснение флагов.
Как видно, я здесь добавил некоторые константы для удобства чтения кода.
localparam
CF = 0, PF = 2, HF = 4, ZF = 6, SF = 7;
 
localparam
ADD = 0, ADC = 1, SUB = 2, SBB = 3,
AND = 4, XOR = 5, OR = 6, CMP = 7;
И последний штрих, это вычисление флагов по результату.
wire [7:0] aluf =
opc[5:3] == AND || opc[5:3] == XOR || opc[5:3] == OR ?
{sf, zf, 1'b0, af, 1'b0, pf, 1'b1, 1'b0} : // AND, XOR, OR
{sf, zf, 1'b0, hf, 1'b0, pf, 1'b1, cf}; // ADD, ADC, SUB, SBB, CMP
На самом деле, тут только 2 случая. Если это логические операции (AND, XOR, OR), то вместо HF пишется AF, и в CF=0. Для арифметических операции (ADD, ADC, SUB, SBB, CMP), все флаги затронуты. Больше тут ничего не требуется.
Теперь перейдем к основной части, это к исполнению инструкции. На удивление, она довольно проста.
8'b10xx_xxxx: case (t)
 
0: begin cp <= hl; sw <= 1; end
1: begin d <= alur; b <= (opc[5:3] != CMP); n <= 7; psw <= aluf; end
4: begin sw <= 0; t <= m20 ? 5 : 0; end
6: begin t <= 0; end
 
endcase
Алгоритм.
И вот, 64 инструкции АЛУ были успешно запрограммированы в верилоге.