Так уж случилось, что из-за поломки основного ПК всё оказалось на долгой паузе...
Все, кто знаком и работает с архитектурой x86 / IA32, давно в курсе, что она - далека от совершенства…
Но, так как на разработку конкретной архитектуры всегда влиял так или иначе Intel, буду приводить примеры на IA-32 для доходчивости…
- LEA EBX,[EBX+EBX*2+3] ; Знакомый всем трюк продвинутого ADD
- ADD EBX,[EBX+EBX*2+3] ; Здесь получим исключение, так как регистр базы и регистр индекса - один. Что "из ряда вон!" ("маргинал" какой-то…)
Вот такие случаи в Intel никак не предусмотрели и оставили всё
на совести программиста, переложив всю заботу на обработчик исключения.
- MOV EAX,FS:[0x00000018] ; Встречается порою вполне знакомое…
- MOV EAX,CS: DS: ES: FS:[0x00000018] ; Процессор проигнорирует в этой куче три лишних префикса. Но, запись - "из ряда вон!" ("маргинал" какой-то…)
И такие случаи в Intel демократично проигнорировали и оставили
на совести программиста…
Теперь читатели темы достаточно подготовились к нововведению…
МАРГИНАЛЫ
Как уже говорилось многим выше, префиксы кодируются с помощью любого ненулевого BCD.
- код «10» означает указатель на ячейку в памяти по адресу «[D1+0]» (левый ниббл адресует регистр базы, правый ниббл определяет относительное смещение)
- код «10 23» усложняет указатель добавлением к базовому ещё и индексного регистра, что получается «[D1+D2+3]»
- код «10 23 45» добавляет ещё один индексный регистр и теперь получилось «[D1+D2+D4+35]» (относительное смещение ожидаемо множится на 10 и накапливает правые нибблы)
- код «10 23 45 16» получился недопустимым, как «[D1+D2+D4+D1+356]»! Так какой же регистр здесь можно считать базовым, если D1 повторяется вновь?
Как уже можно догадаться, это - та самая ситуация "
из ряда вон!" с условным кодовым именем "
маргинал какой-то"…
Естественно, для решения этой задачи не требуется никакой вычислительной мощности, так как здесь - концептуальная проблема.
На решение этой проблемы у меня ушёл, без малого, год…
Год, в часы досуга, на листке бумаги карандашом расписывались подобные ситуации с поиском решения.
И решение было найдено достаточно изящное и гибкое!
- код «10 23 45 16» следует интерпретировать как «[D1+(D2+D4)*2+356]», где повторяемый регистр базы служит признаком удвоения суммы индексов
- код «10 23 45 16 47» здесь описывает указатель как «[D1+(D2+D4)*2+D4+3567]», что можно упростить до «[D1+2*D2+3*D4+3567]» следуя нехитрым школьным правилам
- код «10 23 45 16 47 48» показывает «[D1+(D2+D4)*2+D4+D4+35678]» повторяя D4 дважды. Интерпретируем как «[D1+((D2+D4)*2+D4)*2+D4+35678]» и получаем «[D1+4*D2+7*D4+35678]»
Совсем нетрудно догадаться, что появились механизмы умножения индексных регистров через последовательно-выборочный двоичный сдвиг. Да, код при этом начинает разбухать прилично, но программист получает возможность описать указатель любой сложности.
Одним словом… Эврика!
Но, оказывается, здесь кроется один крошечный подвох…
- код «10 10» как следует интерпретировать, если индексные регистры ещё не указаны, а умножение - «[D1+(0)*2+0]» - действует?
- код «10 10 10» с таким механизмом это «[D1+((0)*2)*2+0]» - что это такое вообще???
Ещё месяцы ушли на решение этой концептуальной задачи.
В итоге, получилось следующее:
- код «10 10» подавляет (сбрасывает) регистр базы и аккумулируется как «маргинал»: Условно, через шарп - «1#»
- код «10 10 10» образует «маргинал 1#», но указатель снова в деле. Получаем маргинальный указатель - «1#[D1+0]»
- код «80 80 70 70 10» уже интерпретируется как маргинальный указатель «87#[D1+0]»
Тем самым, никакие исключения, в жанре Intel, в моей архитектуре не понадобились, а в сумме - появились и комплексные векторы на ячейку памяти, и «маргинализаторы операций», выводящие код инструкций «вон из процессора» - в сопроцессор…
Естественно, всё это не ограничилось JavaScript-опытами (
ссылка на JavaScript кодировщик инструкции).
Verilog-модель аккумулятор эффективного адреса и детекции
маргиналов:
Спойлер
Код: Выделить всё
module vector_accumulator
(input wire clk // Clock (positive edge)
,input wire vector_en // Enable parse vector (BDC detected)
,input wire vector_clr // Clear vector (after any instruction)
,input wire [7:0] operation // Operation code (BDC only)
,input wire [15:0] vector_input // Pointer input (pair B:C from register file)
,output reg [15:0] vector_output // Full vector output (summ of Base + Index + Offset)
,output reg vector_margin // Marginal flag (incorrect Index of "dry" Vector)
);
reg [3:0] pointer_index; // Index of "base" in operation code
reg [3:0] offset_digit; // Digit of BDC-digit for "offset"
reg is_base; // Flag of "base" (Index is equal of Base)
reg is_shift; // Flag of "shifting" (Index is repeated in current summ)
reg [3:0] base_index; // Index of current "base" (Index of Base)
reg [9:0] pointers_set; // Collection of used pointers (flags of used Indexes in current summ)
reg [9:0] pointer_mask; // Mask of current pointer
reg [15:0] vector_base; // Pointer of "base"
reg [15:0] vector_index; // Summ of used pointers
reg [15:0] vector_offset; // Decimal offset
reg [15:0] offset_shift; // Offset multipled by 10
always @*
begin
pointer_index = operation[7:4];
pointer_mask = 10'b1 << pointer_index;
is_base = &(base_index ^ pointer_index);
is_shift = |(pointers_set & pointer_mask);
offset_digit = operation[3:0];
offset_shift = (((vector_offset << 2) + vector_offset) << 1) + {12'd0, offset_digit};
vector_output = vector_base + vector_index + vector_offset;
vector_margin = vector_en && ~|offset_digit && is_base && ~|vector_offset && ~|pointers_set;
end
always @(posedge clk)
begin
if(vector_clr)
begin
base_index <= 0;
vector_base <= 0;
vector_index <= 0;
vector_offset <= 0;
pointers_set <= 0;
end else
if(vector_margin)
base_index <= 0;
else
if(vector_en)
begin
vector_offset <= offset_shift;
if(~|base_index)
begin
base_index <= ~pointer_index;
vector_base <= vector_input;
end else
if(is_base)
begin
vector_index <= vector_index << 1;
pointers_set <= pointer_mask;
end else
if(is_shift)
begin
vector_index <= (vector_index << 1) + vector_input;
pointers_set <= pointer_mask;
end else begin
vector_index <= vector_index + vector_input;
pointers_set <= pointers_set | pointer_mask;
end
end
end
endmodule
Те же Intel в своей IA-64 не стали разбираться с корректным выполнением инструкций типа «ADD DX,AX» и вообще обнуляют старшие 32 бита RDX в таких случаях.
И здесь по стопам Intel мне совсем ни к чему. Просто на аппаратном уровне такие ситуации также отлавливаются и устанавливаются ещё один флаг.
К тому же, регистр A0 у меня выполняет функцию PSW: Использовать его в вычислениях бессмысленно.
И регистры группы D0-D9 ссылаются либо к портам УВВ (Devices), либо к регистровым парам (как DX - это DH и DL).
Если «MOV BL,BL» работает как «NOP», то мне такие «пустышки» в большом количестве не нужны - такие ситуации также отлавливаются.
Как результат, шесть инструкций «ADD / SUB / AND / OR / EOR / MOV» дешифратором с учётом всех комбинаций операндов синтезируются в три десятка различных инструкций.
Тем самым, к АЛУ подтягивается уже не три бита кода операции, а все 11, из-за чего программно доступно уже большее число разнообразных инструкций всех видов.
Спойлер
Код: Выделить всё
+--------------> Имеется "маргинальный префикс"
|
| +------------> Имеется "векторный префикс"
| |
| | ++---------> Приёмник - либо A0/PSW, либо порт Dj, либо 16-битная регистровая пара Bn:Cn
| | ||
| | || +-------> Приёмник и источник - один РОН или регистровая пара
| | || |
| | || | ++----> Приёмник - либо A0/PSW, либо порт Dj, либо 16-битная регистровая пара Bi:Ci
| | || | ||
| | || | || +--> Признак FOR/MOV - не АЛУ-операции
| | || | || |
M V AD E AD F
X_1_10_1_XX_1: MOV PSW,[V] ; Чтение PSW из ОЗУ
X_1_10_1_XX_0: UNARY [V] ; Унарные INC/DEC/CLR/SET/NOT над ОЗУ
X_0_10_1_XX_1: MOV??? PSW,PSW ; 1 шт. !!!reserved!!!
X_0_10_1_XX_0: UNARY?? PSW ; 5 шт. !!!reserved!!!
X_1_10_X_01_1: SWAP [V],Dn ; XCHG ОЗУ и 16-битной пары
X_1_10_X_01_0: ALU_OP [V],Dn ; АЛУ-ADD/SUB/AND/OR/EOR 16-битного ОЗУ и пары
X_0_10_X_01_1: INF Dj ; MOV PSW,Dj - Попытка ввода из порта (результат - в CF)
X_0_10_X_01_0: UNARY Dn ; 16-битные унарные INC/DEC/CLR/SET/NOT
X_1_10_X_XX_1: SWAP [V],Rn ; XCHG ОЗУ и 8-битного РОН
X_1_10_X_XX_0: ALU_OP [V],Rn ; АЛУ-ADD/SUB/AND/OR/EOR ОЗУ и 8-битного РОН
X_0_10_X_XX_1: MOV PSW,Rn ; Чтение PSW из РОН
X_0_10_X_XX_0: UNARY Rn ; Унарные INC/DEC/CLR/SET/NOT над РОН
X_1_01_X_10_X: ALU/MOV Dn,[V] ; АЛУ-ADD/SUB/AND/OR/EOR и MOV пары с 16-битным ОЗУ
X_0_01_X_10_1: OUF Dj ; MOV Dj,PSW - Попытка вывода в порт (результат - в CF)
X_0_01_X_10_0: UNARY Dn,CF ; Унарные INC/DEC/CLR/SET/NOT над парой при условии CF
X_1_00_X_10_X: ALU/MOV Rn,[V] ; АЛУ-ADD/SUB/AND/OR/EOR и MOV РОН с ОЗУ
X_0_00_X_10_1: MOV Rn,PSW ; Загрузка PSW в РОН
X_0_00_X_10_0: UNARY Rn,CF ; Унарные INC/DEC/CLR/SET/NOT над РОН при условии CF
X_1_01_1_XX_1: LEA Dn,[V] ; Загрузка "эффективного адреса" в регистровую пару
X_1_01_0_01_1: ORD Dn,[V],Di ; Dn = (MAX(Dn, Di) - ОЗУ) >> 1
X_1_01_X_01_0: ALU_OP Dn,[V],Di ; 16-битная ADD/SUB/AND/OR/EOR ОЗУ с Di, результат в Dn
X_0_XX_1_XX_1: MOV??? R,R ; 1 шт. Холостая пересылка (NOP???)
X_0_01_1_XX_0: UNARY?? Dn ; 5 шт. 16-битные унарные INC/DEC/CLR/SET/NOT (повтор!!!)
X_1_01_0_XX_1: LEX Dn,[V],Ri ; 16-битный XLAT: MOV Dn,[V+2*Ri]
X_1_01_X_XX_0: ALU_OP Dn,[V],Ri ; 8-битная ADD/SUB/AND/OR/EOR ОЗУ с РОН, результат в 16-бит Dn
X_0_01_0_00_1: MOV Dj,Ri ; Аналог OUT Dj,Ri - вывод в порт
X_0_01_X_XX_0: ALU_OP Dn,Ri ; 16-битная ADD/SUB/AND/OR/EOR Dn с 8-битным Dn
X_1_0X_X_01_1: MOV??? R,[V],Dj ; 1 шт. !!!СТРАННАЯ ОПЕРАЦИЯ!!!
X_1_0X_X_01_0: ALU_OP R,[V],Dj ; 8-битная ADD/SUB/AND/OR/EOR ОЗУ с портом Dj, результат в РОН
X_0_0X_X_01_X: ALU/MOV R,Dj ; АЛУ-ADD/SUB/AND/OR/EOR или MOV (IN R,Dj) РОН с портом Dj
X_1_0X_1_0X_1: LEX R,[V] ; 8-битный XLAT: MOV R,[V+R]
X_1_0X_0_0X_1: ORD R,[V],R' ; R = (MAX(R, R') - ОЗУ) >> 1
X_1_0X_X_01_X: ALU/MOV R,[V],R' ; ADD/SUB/AND/OR/EOR или MOV ОЗУ с R', результат в РОН
X_0_0X_X_01_X: ALU/MOV R,R' ; ADD/SUB/AND/OR/EOR или MOV ОЗУ с R', результат в РОН
Примеры:__________________________________
AA A0 12 0F MOV PSW,[D1+2] ; На самом деле, полная мнемоника операции - MOV A0,[D1+2],A0
AA A0 12 0E NOT [D1+2] ; На самом деле, полная мнемоника операции - EOR A0,[D1+2],A0
AD A0 12 3F SWAP [D1+2],D3 ; На самом деле, полная мнемоника операции - MOV A0,[D1+2],D3
AD A0 1F INF D1 ; На самом деле, полная мнемоника операции - MOV A0,D1
AD A0 1E NOT D1 ; На самом деле, полная мнемоника операции - EOR A0,D1
AB A0 12 3F SWAP [D1+2],B3 ; На самом деле, полная мнемоника операции - MOV A0,[D1+2],B3
AB A0 12 3E EOR [D1+2],B3 ; На самом деле, полная мнемоника операции - EOR A0,[D1+2],B3
AB A0 1F MOV PSW,B1 ; На самом деле, полная мнемоника операции - MOV A0,B1
AB A0 1E NOT B1 ; На самом деле, полная мнемоника операции - EOR A0,B1
DA D1 0F OUF D1 ; На самом деле, полная мнемоника операции - MOV D1,A0
DA D1 0E NOT D1,CF ; На самом деле, полная мнемоника операции - EOR D1,A0
DD D1 23 1F LEA D1,[D2+3] ; На самом деле, полная мнемоника операции - MOV D1,[D2+3],D1
DD D1 23 4F ORD D1,[D2+3],D4 ; На самом деле, полная мнемоника операции - MOV D1,[D2+3],D4
DC D1 23 4F LEX D1,[D2+3],C4 ; На самом деле, полная мнемоника операции - MOV D1,[D2+3],C4
Например, вот фрагмент логов отладчика дешифратора команд:
- M# / M - последовательность "маргиналов"
- VV / V - наличие корректного вектора
- IC - код инструкции
- E - флаг использования одного регистра за приёмник и за транслятор
- AD - флаги наличия A0/PSW или D со стороны приёмника или транслятора
При прогонке отладочного кода через отладочную модель дешифратора команд выдаётся примерно такой лог:
Спойлер
Код: Выделить всё
VCD info: dumpfile testbench.vcd opened for output.
_IP_:M#[VV]IC M_V_AD_E_AD_F RC<>TR _ASM ..OPERANDS.. _REAL INSTRUCTION_
0000: AA 0_0_x0_x_0x_0:A?!=0? ARG A,A
0001: A0 0_0_x0_x_00_0:A?!=AA REG A0
0002:86#59 A0 1_1_10_0_00_0:A0!=AA MOV86 D1+2D2+3D3+4D4+5D5+67D6+789,A0
0016:03# A0 1_0_10_0_00_0:A0!=AA REG3 A0 ; Marginaled
0019: 0F 0_0_10_1_10_1:A0==A0 --- ??? ; MOV A0,A0
001A: 0E 0_0_10_1_10_0:A0==A0 --- ??? ; EOR A0,A0
001B: 0D 0_0_10_1_10_0:A0==A0 --- ??? ; DIS A0,A0
001C: 0C 0_0_10_1_10_0:A0==A0 --- ??? ; CON A0,A0
001D: 0B 0_0_10_1_10_0:A0==A0 --- ??? ; SUB A0,A0
001E: 0A 0_0_10_1_10_0:A0==A0 --- ??? ; ADD A0,A0
001F: 34 0F 0_1_10_1_10_1:A0==A0 MOV PSW,D3+4 ; MOV A0,D3+4,A0
0021: 34 0E 0_1_10_1_10_0:A0==A0 NOT D3+4 ; EOR A0,D3+4,A0
0023: AD 0_0_10_0_00_0:A0!=AA ARG A,D
0024: 5F 0_0_10_0_01_1:A0!=D5 INF D5 ; MOV A0,D5
0025: 5E 0_0_10_0_01_0:A0!=D5 NOT D5 ; EOR A0,D5
0026: 34 5F 0_1_10_0_01_1:A0!=D5 SWP D3+4,D5 ; MOV A0,D3+4,D5
0028: 34 5E 0_1_10_0_01_0:A0!=D5 EOR D3+4,D5 ; EOR A0,D3+4,D5
002A: AC 0_0_10_0_01_0:A0!=Da ARG A,C
002B: 5F 0_0_10_0_00_1:A0!=C5 MOV PSW,C5 ; MOV A0,C5
002C: 5E 0_0_10_0_00_0:A0!=C5 NOT C5 ; EOR A0,C5
002D: 34 5F 0_1_10_0_00_1:A0!=C5 SWP D3+4,C5 ; MOV A0,D3+4,C5
002F: 34 5E 0_1_10_0_00_0:A0!=C5 EOR D3+4,C5 ; EOR A0,D3+4,C5
0031: DA 0_0_10_0_00_0:A0!=CD ARG D,A
0032: D1 0_0_01_0_00_0:Dx!=AD REG D1
0033: 0F 0_0_01_0_10_1:D1!=A0 OUF D1 ; MOV D1,A0
0034: 0E 0_0_01_0_10_0:D1!=A0 NOT D1,CF ; EOR D1,A0
0035: 34 0F 0_1_01_0_10_1:D1!=A0 MOV D1,D3+4 ; MOV D1,D3+4,A0
0037: 34 0E 0_1_01_0_10_0:D1!=A0 EOR D1,D3+4 ; EOR D1,D3+4,A0
0039: CA 0_0_01_0_00_0:D1!=AC ARG C,A
003A: C2 0_0_00_0_00_0:C?!=AC REG C2
003B: 0F 0_0_00_0_10_1:C2!=A0 MOV C2,PSW ; MOV C2,A0
003C: 0E 0_0_00_0_10_0:C2!=A0 NOT C2,CF ; EOR C2,A0
003D: 34 0F 0_1_00_0_10_1:C2!=A0 MOV C2,D3+4 ; MOV C2,D3+4,A0
003F: 34 0E 0_1_00_0_10_0:C2!=A0 EOR C2,D3+4 ; EOR C2,D3+4,A0
0041: DD 0_0_00_0_00_0:C2!=AD ARG D,D
0042: 1F 0_0_01_1_01_1:D1==D1 ??? D1 ; MOV D1,D1
0043: 1E 0_0_01_1_01_0:D1==D1 NEG D1 ; EOR D1,D1
0044: 1D 0_0_01_1_01_0:D1==D1 STD D1 ; DIS D1,D1
0045: 1C 0_0_01_1_01_0:D1==D1 CLN D1 ; CON D1,D1
0046: 1B 0_0_01_1_01_0:D1==D1 SUB D1 ; SUB D1,D1
0047: 1A 0_0_01_1_01_0:D1==D1 ADD D1 ; ADD D1,D1
0048:06#34 5F 1_1_01_0_01_1:D1!=D5 ORD6 D1,D3+4,D5 ; MOV D1,D3+4,D5
004C: 34 5E 0_1_01_0_01_0:D1!=D5 EOR D1,D3+4,D5 ; EOR D1,D3+4,D5
004E: 34 1F 0_1_01_1_01_1:D1==D1 LEA D1,D3+4 ; MOV D1,D3+4,D1
0050: 34 1E 0_1_01_1_01_0:D1==D1 RRC D1,D3+4,D1 ; EOR D1,D3+4,D1
0052: 34 1D 0_1_01_1_01_0:D1==D1 SAL D1,D3+4,D1 ; DIS D1,D3+4,D1
0054: 34 1C 0_1_01_1_01_0:D1==D1 SAR D1,D3+4,D1 ; CON D1,D3+4,D1
0056: 34 1B 0_1_01_1_01_0:D1==D1 SUB D1,D3+4,D1 ; SUB D1,D3+4,D1
0058: 34 1A 0_1_01_1_01_0:D1==D1 MUL D1,D3+4,D1 ; ADD D1,D3+4,D1
005A: DB 0_0_01_0_01_0:D1!=Dd ARG D,B
005B: 5F 0_0_01_0_00_1:D1!=B5 OUT D1,B5 ; MOV D1,B5
005C: 5E 0_0_01_0_00_0:D1!=B5 EOR D1,B5 ; EOR D1,B5
005D: 34 5F 0_1_01_0_00_1:D1!=B5 LEX D1,D3+4,B5 ; MOV D1,D3+4,B5
005F: 34 5E 0_1_01_0_00_0:D1!=B5 EOR D1,D3+4,B5 ; EOR D1,D3+4,B5
0061: CD 0_0_01_0_00_0:D1!=BC ARG C,D
0062: 5F 0_0_00_0_01_1:C2!=D5 MOV C2,D5 ; MOV C2,D5
0063: 5E 0_0_00_0_01_0:C2!=D5 EOR C2,D5 ; EOR C2,D5
0064: 34 5F 0_1_00_0_01_1:C2!=D5 ??? C2,D3+4,D5 ; MOV C2,D3+4,D5
0066: 34 5E 0_1_00_0_01_0:C2!=D5 EOR C2,D3+4,D5 ; EOR C2,D3+4,D5
0068: CC 0_0_00_0_01_0:C2!=Dc ARG C,C
0069: 2F 0_0_00_1_00_1:C2==C2 MOV C2,C2 ; MOV C2,C2
006A: 2E 0_0_00_1_00_0:C2==C2 NEG C2,C2 ; EOR C2,C2
006B: 2D 0_0_00_1_00_0:C2==C2 STD C2,C2 ; DIS C2,C2
006C: 2C 0_0_00_1_00_0:C2==C2 CLN C2,C2 ; CON C2,C2
006D: 2B 0_0_00_1_00_0:C2==C2 SUB C2,C2 ; SUB C2,C2
006E: 2A 0_0_00_1_00_0:C2==C2 ADD C2,C2 ; ADD C2,C2
006F: 34 2F 0_1_00_1_00_1:C2==C2 LEX C2,D3+4,C2 ; MOV C2,D3+4,C2
0071: 34 2E 0_1_00_1_00_0:C2==C2 RRC C2,D3+4,C2 ; EOR C2,D3+4,C2
0073: 34 2D 0_1_00_1_00_0:C2==C2 SAL C2,D3+4,C2 ; DIS C2,D3+4,C2
0075: 34 2C 0_1_00_1_00_0:C2==C2 SAR C2,D3+4,C2 ; CON C2,D3+4,C2
ТЕ ЕЩЁ МАРГИНАЛЫ
Как сказано выше, указание неверного указателя на аппаратном уровне обрабатываются в особенном виде, из-за чего программист может использовать эти резервные механизмы по своему усмотрению.
Однако, эти самые
маргиналы местами просто
слишком маргинальны.
Например, рассмотрим несколько странных инструкций:
- код «00» - команда останова «HLT»: Переход на адрес 0000 с сохранением точки останова в D0 (регистровой паре B0:C0)
- код «10 00» - команда останова «HLT [D1+0]»: Безусловный переход на адрес [D1] с сохранением точки останова в D0
- код «10 10 00» - команда останова «HLT1» или «HLT#1»: Здесь присутствует маргинал - непонятная операция…
- код «10 10 10 00» - команда останова «HLT 1#D1+0» или «HLT 1#[D1+0]»: Присутствует маргинальный указатель - ещё страннее…
Нетрудно заметить, что на одной только операции Останова с частной комбинацией префиксов уже довольно неясная ситуация. Даже если браться обрабатывать такие комбинации на самом высоком уровне - эмуляцией в JavaScript, не совсем понятно, что именно с этим безобразием делать…
МАРГИНАЛЬНЫЕ КАНАЛЫ
Так как «маргинальные индексы» временно помечались в ассемблере/дизассемблере с «решёткой-шарпом», то это напомнило некоторую аналогию с каналами в Бейсиках (у
ZX-Spectrum также имеются).
Так, есть такой
GW-Basic - оператор OPEN - вполне хороший пример.
Тем самым, вполне можно:
- код «10 10 00» - команду «HLT#1» можно условно обозначать за «CLOSE#1»…
- код «10 10 10 00» - команду «HLT#1 [D1]» можно условно обозначать за «OPEN#1 "/dev/…"»…
Тем самым, у процессора на программном уровне в системе команд появляются две, хоть и побочные, но весьма любопытные операции. Программист получает основные операции работы с файловой системой уже на самом низком уровне.
Сам процессор может продолжать игнорировать такие операции и ничего не делать.
Тогда как внешняя периферия и т.н. сопроцессор может перехватывать все эти «
маргинальные конструкции инструкций» и производить соответствующую настройку.
В таком случае, если в системе будут присутствовать «сетевой сопроцессор», «графический сопроцессор» или «математический сопроцессор», уже на уровне ассемблера предоставляется довольно мощное окружение…
МАРГИНАЛЬНАЯ ЭМУЛЯЦИЯ КАНАЛОВ
Естественно, о поддержке «
маргинального окружения» на аппаратном уровне думать не приходится в принципе!
Тем самым, следует продумать механизмы прерываний, чтобы поддерживать все эти «
маргинальные каналы» на программном уровне драйверов операционной системы.
Ещё во времена DOS и IBM PC-XT существовали библиотеки эмуляции отсутствующего FPU. Здесь - примерно то же самое…
ПРИМЕР
Если мы настроили «
Маргинал #1» указателем на строку, типа "/dev/ega/320x240/" как открытия непосредственной проекции EGA-графики, то код
будет означать «MOV 1#[D8+320D9],A7», что буквально «*(BYTE *)(D8 + 320 * D9) = A7», где D8 - координата X и D9 - координата Y.
Получается одна длинная операция пиксельной записи в графическую плоскость.
P.S: Как можно подметить, термин «
маргинал» в рамках текущих разработок используется как устоявшийся.
Вполне возможно, что вместо привычных «эскейпов» его вполне можно использовать…