Оглавление
§ Модификация кода
В предыдущей главе я более-менее подробно рассказал о том, как подключить, запустить процессор так, чтобы он сохранял опкод, в этой же главе я расскажу и напишу код выполнения некоторых простых инструкции из первого, базового набора команд. На рассмотрении сегодня будут инструкции LXI, STAX, LDAX, INX и DCX.В первую очередь, в целях больше отладки, чем какого-то реального значения, я выведу особый сигнал
m0
в пины модуля:1module KR580VM80ALite 2( 3 input clock, 4 input reset_n, 5 input ce, 6 output m0, // Новый пин -- начало инструкции 7 output [15:0] address, 8 input [ 7:0] in, 9 output reg [ 7:0] out, 10 output reg we 11); 12 13assign m0 = (t == 0);Он будет равен 1, если это — нулевой, стартовый такт инструкции. Соответственно, можно будет поменять запись опкода, просто поставив
m0
в уже существующем ранее коде:1if (m0) begin opcode <= in; pc <= pcn; endРазницы никакой, но выглядит чуть более прилично. Для функционирования записи данных в регистры, также будут добавлены несколько регистров:
1reg b; // =1 Запись d в 8-битный регистр n 2reg w; // =1 Запись d в 16-битный регистр n 3reg [15:0] d; // 16-битное значение 4reg [ 2:0] n; // Номер регистраСоответственно, в блоке always тоже будет некоторая модификация:
1... 2else if (ce) begin 3 4 t <= t + 1; // Счетчик микрооперации 5 b <= 0; // Выключить запись в регистр (по умолчанию) 6 w <= 0; 7 we <= 0; // Аналогично, выключить запись в память (по умолчанию)Запись в регистр происходит так. Сначала выставляется номер регистра
n
, где 0-B, 1-C, 2-D, 3-E, 4-H, 5-L и 7-A.- При наличии сигнала
b=1
, будет записана младшая часть регистраd
в 8-битный регистр - При наличии сигнала
w=1
, будет записано значениеd
в 16-битный регистр
§ Запись регистров
Каждый раз, когда я делаю 80-совместимые процессоры, я делаю немного по-разному каждый из них. Не отходя от своей традиции, сделал видоизменение тоже в том, что вместо того, чтобы формировать данные на позитивном фронте, а записывать на негативном, я формирую также на позитивном, но в то же время запись регистров тоже проходит на позитивном, но на следующем такте.Это значит, что если на такте 0 будет установлен сигнал
b=1
(8 бит) или w=1
(16 бит) , то на такте 1 будет произведена запись в регистр номер n
данных d
.1always @(posedge clock) 2if (reset_n && ce) begin 3 4 // 8-bit 5 if (b) 6 case (n) 7 0: bc[15:8] <= d[7:0]; // B 8 1: bc[ 7:0] <= d[7:0]; // C 9 2: de[15:8] <= d[7:0]; // D 10 3: de[ 7:0] <= d[7:0]; // E 11 4: hl[15:8] <= d[7:0]; // H 12 5: hl[ 7:0] <= d[7:0]; // L 13 7: a <= d[7:0]; // A 14 endcase 15 16 // 16-bit 17 if (w) 18 case (n) 19 2'b00: bc <= d; 20 2'b01: de <= d; 21 2'b10: hl <= d; 22 2'b11: sp <= d; 23 endcase 24 25endТакже немаловажной деталью этого действия является тот факт, что запись в регистр будет произведена только тогда, когда процессор не находится в состоянии сброса, а также активирован. Как я и говорил ранее, активация с помощью сигнала
CE
позволит на время останавливать исполнение процессором микрокода без каких-либо негативных последствий. Для Радио86 это довольно важная деталь, поскольку видеоконтроллер постоянно останавливает процессор, пока читает из видеопамяти.§ Инструкция LXI
Эта инструкция записывает 16-битное непосредственное значение, которое идет сразу за опкодом, в регистр BC, DE, HL или SP и выполняется за 10 тактов. На самом деле, эта инструкция выполняется за 3 реальных такта, но остальные 7 это чтение из памяти, отправка сигнала регенерации. Поскольку я не реализовываю до самых мелочей, то сигнал регенерации формироваться не будет, как и не производиться счетчик регистра R. Зачем его считать, если к нему никакого доступа нет.Ниже приведен код выполнения инструкции. В зависимости от того, какой t, будет взят либо сохраненный ранее
opcode
, либо же данные из памяти in
.1casex (t ? opcode : in) 28'b00xx_0001: case (t) 3 4 1: begin pc <= pcn; d[ 7:0] <= in; n <= opcode[5:4]; end 5 2: begin pc <= pcn; d[15:8] <= in; w <= 1; end 6 9: begin t <= 0; end 7 8endcaseАлгоритм работы:
- На 0-м такте происходит только защелкивание опкода, поэтому каких-то реальных действий и не выполняется
- На 1-й такте на входе in находится младший байт непосредственного значения, выбирает номер регистра (BC, DE, HL или SP)
- На 2-м такте ставится на запись уже старший байт и записывает в регистр на следующем такте
- Такты с 3 по 8 пропускаются (то есть, ничего не происходит)
- И на 9-м такте переходит к следующей инструкции
§ Инструкция STAX
Значение из регистра A записывается либо в память по адресу BC, либо по адресу DE. Эта инструкция занимает 7Т у процессора, но на деле она у меня выполняется всего лишь за один такт, за первый.18'b000x_0010: case (t) 2 3 0: begin we <= 1; out <= a; cp <= in[4] ? de : bc; sw <= 1; end 4 6: begin sw <= 0; t <= 0; end 5 6endcaseУстанавливается
sw=1
и we=1
, чтобы на следующем такте записалось значение из out=a
в память по адресу bc
, если это STAX B
, либо же de
, если это STAX D
. И всё. На следующих тактах от 1 до 5 ничего не происходит, кроме как на последнем, который обозначен как 6-й, но на самом деле он 7-й, ибо от 0 до 6 включительно — это 7 тактов. Там возвращается sw=0
, что устанавливает указать адреса опять на PC и переходит к новой инструкции.§ Инструкция LDAX
Загрузка из памяти по адресу BC или DE в регистр A. Выполняется за 7Т.18'b000x_1010: case (t) 2 3 0: begin cp <= in[4] ? de : bc; sw <= 1; end 4 1: begin b <= 1; n <= 7; d <= in; end 5 6: begin sw <= 0; t <= 0; end 6 7endcase
- На 0-м такте выбирается источник, откуда будут читаться данные
- На 1-м такте из памяти читается в промежуточный регистр d для последующей записи в A
- Соответственно, на 2-м такте происходит запись и на 6-м — переключение обратно на PC и переход к исполнению следующей инструкции
§ Инструкция INX и DCX
Прибавляется +1 если это INX или убавляется -1, если DCX к 16-битной регистровой паре.18'b00xx_x011: case (t) 2 3 0: begin 4 5 case (in[5:4]) 6 2'b00: begin d <= in[3] ? bc - 1 : bc + 1; end 7 2'b01: begin d <= in[3] ? de - 1 : de + 1; end 8 2'b10: begin d <= in[3] ? hl - 1 : hl + 1; end 9 2'b11: begin d <= in[3] ? sp - 1 : sp + 1; end 10 endcase 11 12 n <= in[5:4]; 13 w <= 1; 14 15 end 16 4: begin t <= 0; end 17 18endcase
- На 0-м такте в промежуточный регистр
d
записывается инкремент или декремент 16-битного регистра. Честно сказать, использование такой конструкции тратит много логических элементов, но ПЛИС у меня большая, так что заботиться не стоит о таких мелочах. - Выбирается номер регистра для записи и записывается на следующему такте полученное значение обратно.
- На 4-м такте — окончание работы этой инструкции и переход к следующей
Приложение. Исходный код по материалам текущей главы