§ Изменение концепции
Ранее я говорил о том, что делаю процессор 8088, основываясь на 8-битной шине данных. Так вот, посмотрел на то, сколько тратится тактов на каждую инструкцию и сильно, очень сильно разочаровался в таком подходе. Мне бы хотелось сделать процессор следующим образом:- Шина 32-х битная
- Инструкции могут быть не только 8086, но и поддерживать 80186
- Инструкция будет загружаться в "очередь инструкции" каждый раз, считывая до 6 байт
- Исполнение инструкции не обязательно будет находится в posedge clock, но часть его вынесена в always @*-секцию
- Некоторые простые инструкции теперь могут выполняться за 1Т
- Байт modrm будет декодироваться полностью за 1Т
§ Определение длины инструкции
У каждой инструкции есть своя длина. Чтобы определить длину инструкции, достаточно лишь знать ее опкод и, если есть, байт modrm. В таблице ниже я приведу карту базового набора инструкции.00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
00 | 1 | 2 | 1 | 2 | ||||||||||||
10 | 1 | 2 | 1 | 2 | ||||||||||||
20 | 1 | 2 | 1 | 2 | ||||||||||||
30 | 1 | 2 | 1 | 2 | ||||||||||||
40 | ||||||||||||||||
50 | ||||||||||||||||
60 | 2 | 2 | 1 | 1 | ||||||||||||
70 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
80 | 1 | 2 | 1 | 1 | ||||||||||||
90 | 4 | |||||||||||||||
A0 | 2 | 2 | 2 | 2 | 1 | 2 | ||||||||||
B0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
C0 | 1 | 1 | 2 | 1 | 2 | 3 | 2 | 1 | ||||||||
D0 | 1 | 1 | ||||||||||||||
E0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 4 | 1 | ||||
F0 | 1 | 2 |
Там, где опкод помечен зеленым, это значит, что за ним идет байт modrm, а значит, что инструкция как минимум содержит в себе два байта, но может и больше, потому что все определяется байтом modrm - там может быть считывание 1-байтного или 2-байтного смещения. В ячейках есть цифры 1 или 2, которые означают размер непосредственного значения (immediate), идущего за опкодом и байтом modrm, если есть. В случае когда нет ни байта modrm, ни immediate, то такой опкод является одиночным и занимает 1 байт.
Для этой цели я создам модуль в файле cpu_isize.v:
// verilator lint_off WIDTH // verilator lint_off CASEX // verilator lint_on CASEINCOMPLETE module cpu_isize ( input [15:0] op_queue, output reg op_modrm_byte, output reg op_modrm_mem, output reg [1:0] op_modrm_disp, output reg [2:0] op_imm_size ); endmoduleСверху закомментированы указания верилятору о том, что не надо предлагать casez и не проверять несоответствия ширины разрядности.
В качестве входного "аргумента" предлагается
op_queue
(48 бит или 6 байт), состоящего из опкода и возможного байта modrm, то есть, два байта. Опкод декодируется однозначно, а байт modrm просматривается только тогда, когда он в опкоде есть.На выходе бит
op_modrm_byte
сигнализирует о том, что опкод имеет байт modrm, и потому надо будет проверять то, что на выходе op_modrm_disp
, там может быть значение от 0 до 2. Независимо от modrm-байта, для каждого опкода также генерируется значение op_imm_size от 0 до 4 — количество байт непосредственного значения. Непосредственным значением также может быть 4-байтный переход по длинному адресу инструкции jmp far segment:offset
или call far segment:offset
.Выход op_modrm_mem если 1, то значит, что в качестве операнда используется чтение из памяти.
Код, который расшифровывает наличие байта modrm, достаточно простой:
// Определить наличие байта modrm always @* begin // Наличие байта ModRM casex (op_queue[7:0]) 8'b00xx_x0xx, // ALU mrm 8'b0110_001x, // BOUND, ARPL 8'b0110_10x1, // IMUL 8'b1000_xxxx, // GrpArith,TEST,MOV,LEA,POP 8'b1100_000x, // GrpShift 8'b1100_01xx, // GrpShift 8'b1101_00xx, // LES,LDS,MOV 8'b1101_1xxx, // ESC, GrpMisc 8'b1111_x11x: op_modrm_byte = 1'b1; default: op_modrm_byte = 1'b0; endcase endВ самом деле, это весь набор кодов для того, чтобы расшифровать, есть ли байт modrm или нет у опкода, но, опкод должен быть только из базового набора, без 0F-префикса.
Как ранее и говорилось, если у опкода есть байт modrm, то надо проверить, есть ли у него смещения при получении адреса памяти, где будет располагаться операнд.
2'b00; op_modrm_mem = op_modrm_byte && op_queue[15:8] != 3'b11; // Размер disp [0..2] if (op_modrm_byte) begin casex (op_queue[15:8]) 8'b00_xxx_110, 8'b10_xxx_xxx: op_modrm_disp = 2'h2; 8'b01_xxx_xxx: op_modrm_disp = 2'h1; default: op_modrm_disp = 2'b0; endcase endop_modrm_disp = Сканирование дополнительных 2-х байт может быть только в случае если 1) используется offset16, 2) есть 16-битное слагаемое в вычислении эффективного адреса. Для считывания однобайтного знакового смещения есть mod=01, ну и для всех остальных будет 0, нет дополнительного смещения.
А теперь будет приведен код для самой значительной части, расчет количества байт, необходимых для непосредственного операнда:
casex (op_queue[7:0]) // Однобайтные 8'b00xx_x100, 8'b0110_101x, 8'b0111_xxxx, 8'b1000_00x0, 8'b1000_0011, 8'b1010_1000, 8'b1011_0xxx, 8'b1100_000x, 8'b1100_0110, 8'b1100_1101, 8'b1101_010x, 8'b1110_0xxx, 8'b1110_1011: op_imm_size = 2'h1; // Двухбайтные 8'b00xx_x101, 8'b0110_100x, 8'b1000_0001, 8'b1010_00xx, 8'b1010_1001, 8'b1011_1xxx, 8'b1100_0010, 8'b1100_0111, 8'b1100_1010, 8'b1110_100x: op_imm_size = 2'h2; // ENTER i16,i8 8'b1100_1000: op_imm_size = 2'h3; // JMP|CALL far 8'b1001_1010, 8'b1110_1010: op_imm_size = 3'h4; // TEST rm, i8/i16 8'b1111_011x: if (op_queue[13:12] == 2'b00) op_imm_size = op_queue[0] + 1'b1; default: op_imm_size = 1'b0; endcaseВначале здесь выбираются такие маски опкодов, которые отвечают за 1,2,3,4-байтные случаи, но в конце видно, что есть специальные опкоды F6,F7, у которых имеется номер функции в байте modrm. Если этот номер функции 000 или 001, то это — инструкция TEST с непосредственным операндом. Для F6 это будет один байт, для F7 - два байта. Во всех остальных случаях, кроме описанных здесь, непосредственное значение не используется.
В данный момент материал находится в стадии разработки, поэтому пока что на этом все.
Я заканчиваю эту часть и начинаю следующую. У меня ранее был разработан процессор, который работает, используя 32-х битные операнды, так что теперь я просто буду объяснять то, как он работает.
[<< Предыдущая] [Оглавление]