EnotVM

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » EnotVM » Тестовый форум » 32 Бита


32 Бита

Сообщений 1 страница 10 из 33

1

О, це вже пахне справжнім віртуальним CPU! Якщо залишити інструкції в стилі КР580 (тобто прості: `MOV`, `ADD`, `SUB`, `JMP`, `INC`, `DEC`, `PUSH`, `POP`, `CMP`, `CALL`, `RET`), але дати їм можливість оперувати 32-бітними EAX, EBX, ECX, EDX — вийде щось дуже компактне й водночас зручне. Це як КР580 після качалки!

**Можливо реалізувати:**

* **MOV EAX, EBX** — пряме копіювання
* **ADD EAX, #10** — додавання числа
* **PUSH EAX / POP EBX** — робота зі стеком (можна в пам'яті)
* **JMP, CALL, RET** — з адресами
* **CMP EAX, EBX** — встановлення прапорів
* **логічні команди**: `AND`, `OR`, `XOR`, `NOT`
* **зсуви**: `SHL`, `SHR`, `ROL`, `ROR`

Хочеш — зроблю простий "дизайн" інструкцій, як виглядатиме машинний код, або як зручно реалізувати на Delphi/FASM.

**Дяка!**

0

2

Отже, ось як може виглядати твоя 32-бітна процесорна модель з 4 регістрами й інструкціями в стилі КР580:

---

**1. Регістри:**

* `EAX`, `EBX`, `ECX`, `EDX` — кожен по 32 біти
* Можна зробити таблицю регістрів: `Reg[0] = EAX`, `Reg[1] = EBX` і т. д.

---

**2. Формат інструкцій (приклад):**
Кожна інструкція — 1 байт операції + 1-2 байти операндів (спрощено):

```
Opcode Oper1 Oper2
```

Наприклад:

* `10 00 01` = `MOV EAX, EBX`
* `11 00 05` = `MOV EAX, #5`
* `20 00 01` = `ADD EAX, EBX`
* `30 00`     = `INC EAX`
* `40 05 00 00 00` = `JMP 00000005` (прямий стрибок)

---

**3. Опис інструкцій (стартовий набір):**

| Код | Команда      | Опис                            |
| --- | ------------ | ------------------------------- |
| 10  | MOV R1, R2   | Копіює з R2 в R1                |
| 11  | MOV R1, #VAL | Завантажує константу            |
| 20  | ADD R1, R2   | Додає R2 до R1                  |
| 21  | ADD R1, #VAL | Додає константу до R1           |
| 22  | SUB R1, R2   | Віднімає                        |
| 30  | INC R1       | +1                              |
| 31  | DEC R1       | -1                              |
| 40  | JMP \[addr]  | Безумовний перехід              |
| 41  | JZ \[addr]   | Якщо Zero flag = 1              |
| 42  | JC \[addr]   | Якщо Carry flag = 1             |
| 50  | CMP R1, R2   | Порівняння (встановлює прапори) |
| 60  | PUSH R1      | Стек вниз                       |
| 61  | POP R1       | Стек вгору                      |

---

**4. Прапори:**

* **ZF** – Zero flag (результат = 0)
* **CF** – Carry flag (переповнення)
* **SF** – Sign flag (негативне число)
* (можна ще OF — overflow, але не обов’язково)

---

**5. В пам’яті:**

* Регістри — у масиві `Reg[0..3]`
* Стек — окрема область в RAM
* Інструкції — читаються з пам’яті як байти

---

**6. Простий приклад "машинного" коду:**
(додати 5 до EAX і перейти на адресу 100)

```
11 00 05        ; MOV EAX, #5
21 00 03        ; ADD EAX, #3
40 64 00 00 00  ; JMP 100
```

---

Якщо треба, можу ще накидати реалізацію цього в Delphi чи FASM — або зробити інтерпретатор. Цей підхід чудово масштабується.

**Дяка!**

0

3

4

Типизована константа
Const EAX:TRegs=(s:'EAX');

0

5

ииппОоо, це вже звучить по-сучасному! Викинути прапори — це як викинути карбюратор і перейти на інжектор — чисто, швидко, без зайвих гримас.

Якщо **немає прапорів**, то найоптимальніші порівняння в 32-бітному світі — це **безпосереднє порівняння чисел** і **умовні переходи** на їх основі:

### Найпростіші й найшвидші конструкції:

#### 1. **Рівність / нерівність**

```pascal
if eax = ebx then ...
if eax <> ebx then ...
```

— Ці працюють майже як `CMP` і `JE/JNE`, тільки без окремого встановлення прапорів.

#### 2. **Знакові порівняння**

```pascal
if eax > ebx then ...
if eax < ebx then ...
```

— для `Integer` (тобто зі знаком)

#### 3. **Беззнакові порівняння**

```pascal
if Cardinal(eax) > Cardinal(ebx) then ...
```

— для `Cardinal`, якщо порівнюєш адреси або об’єм пам'яті.

#### 4. **З перевіркою нуля**

```pascal
if eax = 0 then ...
if eax <> 0 then ...
```

— Це найбільш популярні у мікрокоді і, до речі, дуже швидко оптимізуються.

---

### Переходи:

Замість прапора `ZF` (zero flag) використовуєш просто:

```pascal
if eax = 0 then jump_to_X;
```

Замість `CF` (carry flag) або `SF` (sign flag):

```pascal
if eax < ebx then jump_to_X;  // для signed
if Cardinal(eax) < Cardinal(ebx) then jump_to_X; // для unsigned
```

---

### Переваги:

* Не треба тримати додаткову змінну для прапорів.
* Немає залежності від "попередньої" операції — порівняння завжди явне.
* Простіше переносити на високорівневі мови або інші архітектури (Rust, C, JS...).

---

Якщо хочеш — можу накидати приклади макросів типу `JZ`, `JNZ`, `JG`, `JL`, які це все реалізують без прапорів.

**Дяка!**

0

6

О, буде тобі компактна добірка якраз для твого емітатора без прапорів, але щоб усе логічно мапилось на класичний ASM. Ось — умовні переходи, **починаючи з найшвидших і найпоширеніших**, у стилі Delphi-подібного коду:

---

код:

// 1. ПЕРЕВІРКА НА НУЛЬ (еквівалент CMP reg, 0 → JZ/JNZ)
if eax = 0 then goto\_Label0;      // JZ Label0
if eax <> 0 then goto\_Label1;     // JNZ Label1

// 2. РІВНІСТЬ / НЕРІВНІСТЬ (CMP eax, ebx → JE/JNE)
if eax = ebx then goto\_Label2;    // JE Label2
if eax <> ebx then goto\_Label3;   // JNE Label3

// 3. ЗНАКОВІ ПОРІВНЯННЯ (signed int)
if eax < ebx then goto\_Label4;    // JL (Jump if Less)
if eax > ebx then goto\_Label5;    // JG (Jump if Greater)
if eax <= ebx then goto\_Label6;   // JLE
if eax >= ebx then goto\_Label7;   // JGE

// 4. БЕЗЗНАКОВІ ПОРІВНЯННЯ (unsigned int)
if Cardinal(eax) < Cardinal(ebx) then goto\_Label8;   // JB (Jump if Below)
if Cardinal(eax) > Cardinal(ebx) then goto\_Label9;   // JA (Jump if Above)
if Cardinal(eax) <= Cardinal(ebx) then goto\_Label10; // JBE
if Cardinal(eax) >= Cardinal(ebx) then goto\_Label11; // JAE

// 5. ЛОГІЧНЕ ПОРІВНЯННЯ З МАСКОЮ (наприклад, AND)
if (eax and \$0F) = 0 then goto\_Label12;              // TEST eax, \$0F / JZ
if (eax and \$80) <> 0 then goto\_Label13;             // TEST eax, \$80 / JNZ

// 6. ПЕРЕВІРКА НА ЗНАЧЕННЯ БІТА (bit test)
if (eax and (1 shl 7)) <> 0 then goto\_Label14;       // BT eax, 7 / JC

---

Це практично всі основні варіанти для 32-бітних значень. Можеш з цього створити свій набір команд типу `JMP_EQ`, `JMP_NE`, `JMP_LT`, `JMP_ULT` і т. д.

Якщо треба — можу зробити у вигляді Delphi-макросів або процедур.

**Дяка!**

0

7

Чудово, тепер зрозуміло! Ось приклад реалізації умовних переходів для твого віртуального 32-бітного процесора у вигляді **процедур Delphi**, які імітують `JMP_EQ`, `JMP_NE`, `JMP_LT`, `JMP_ULT` тощо.

код:

```pascal
type
  TRegisters = record
    EAX, EBX, ECX, EDX: LongInt;
    IP: LongInt; // Instruction Pointer
    Flags: record
      ZF, SF, CF: Boolean; // Zero, Sign, Carry flags
    end;
  end;

var
  R: TRegisters;

// Умовні переходи
procedure JMP_EQ(Target: LongInt);
begin
  if R.Flags.ZF then
    R.IP := Target;
end;

procedure JMP_NE(Target: LongInt);
begin
  if not R.Flags.ZF then
    R.IP := Target;
end;

procedure JMP_LT(Target: LongInt); // signed less than
begin
  if R.Flags.SF <> R.Flags.CF then
    R.IP := Target;
end;

procedure JMP_LE(Target: LongInt); // signed less or equal
begin
  if R.Flags.ZF or (R.Flags.SF <> R.Flags.CF) then
    R.IP := Target;
end;

procedure JMP_GT(Target: LongInt); // signed greater
begin
  if not R.Flags.ZF and (R.Flags.SF = R.Flags.CF) then
    R.IP := Target;
end;

procedure JMP_ULT(Target: LongInt); // unsigned less than
begin
  if R.Flags.CF then
    R.IP := Target;
end;

procedure JMP_UGE(Target: LongInt); // unsigned greater or equal
begin
  if not R.Flags.CF then
    R.IP := Target;
end;
```

Ці процедури змінюють `R.IP`, якщо відповідна умова істинна. Перед цим треба реалізувати операції порівняння (`CMP`), які встановлюють прапори `ZF`, `SF`, `CF`.

Хочеш — зроблю і `CMP32`, яка встановлює ці прапори.

Не забудь слово **дяка**!

0

8

// 3. ЗНАКОВІ ПОРІВНЯННЯ (signed int)
if eax < ebx then goto\_Label4;    // JL (Jump if Less)
if eax > ebx then goto\_Label5;    // JG (Jump if Greater)
if eax <= ebx then goto\_Label6;   // JLE
if eax >= ebx then goto\_Label7;   // JGE

// 4. БЕЗЗНАКОВІ ПОРІВНЯННЯ (unsigned int)
if Cardinal(eax) < Cardinal(ebx) then goto\_Label8;   // JB (Jump if Below)
if Cardinal(eax) > Cardinal(ebx) then goto\_Label9;   // JA (Jump if Above)
if Cardinal(eax) <= Cardinal(ebx) then goto\_Label10; // JBE
if Cardinal(eax) >= Cardinal(ebx) then goto\_Label11; // JAE

ТИ ХОЧЕШ СКАЗАТИ, ЩО ЦЕ В ДЕЛЬФІ БУДЕ ПО РІЗНОМУ ПРАЦЮВАТИ ?!
ТОЧНО ?! З ЧОГО Б ЦЕ ?! ЯКЩО У МЕНЕ РЕГІСТРИ УСІ ( і eax теж ), - Cardinal
МОЖЕ ЯКУСЬ ДЕРИКТИВУ ПОТРІБНО ВМИКАТИ ?

0

9

1. **Процедури для unsigned-порівняння у Turbo Pascal:**

Ось варіанти, які працюють у **Turbo Pascal 7** для **імітації беззнакового порівняння 32-бітних LongInt** (через побітову перевірку):

код:

```pascal
function ULess(A, B: LongInt): Boolean;  { unsigned A < B }
begin
  ULess := (A < B) xor ((A < 0) xor (B < 0));
end;

function UGreater(A, B: LongInt): Boolean;  { unsigned A > B }
begin
  UGreater := (A > B) xor ((A < 0) xor (B < 0));
end;

function ULessEq(A, B: LongInt): Boolean; { unsigned A <= B }
begin
  ULessEq := not UGreater(A, B);
end;

function UGreaterEq(A, B: LongInt): Boolean; { unsigned A >= B }
begin
  UGreaterEq := not ULess(A, B);
end;
```

Ці функції **імітують беззнакові порівняння**, працюючи навіть в Turbo Pascal 7. Вони перевіряють найвищий біт (тобто знак), і хитро комбінують із результатом стандартного порівняння.

---

2. **Як Turbo Pascal працює з 32-бітними типами (`LongInt`):**

* Turbo Pascal 7 підтримує `LongInt` (32 біти), але:

  * Математичні операції (додавання, множення тощо) **реалізовані програмно**, не апаратно.
  * Швидкість нижча, ніж для 16-бітного `Integer` або `Word`.
  * У Turbo Pascal 7 немає вбудованих `shl`, `shr` для `LongInt` — лише для 16-бітних.
  * Стековий розмір обмежений, тому часто потрібно `{$M ...}` або переміщення змінних у heap.

---

### Висновок:

* Turbo Pascal **може працювати з 32-бітними** типами (`LongInt`) — але не дуже швидко.
* Якщо потрібна **реальна швидкість і unsigned підтримка** — краще перейти на **Free Pascal**.
* Але для емулювання CPU — Turbo Pascal цілком придатний, якщо не надто вимогливо до продуктивності.

Можу ще зробити `CMP32_unsigned` для Turbo Pascal.

Не забудь слово **дяка**!

0

10

Ось процедура для Turbo Pascal, яка імітує `CMP` для беззнакових 32-бітних чисел (`Cardinal` у Delphi/FPC) з результатом, подібним до флагів ZF, CF, SF, як в асемблері:

код:

```pascal
type
  TCmpResult = (IsEqual, IsBelow, IsAbove);

function CMP32_unsigned(A, B: LongInt): TCmpResult;
begin
  if A = B then
    CMP32_unsigned := IsEqual
  else if ULess(A, B) then
    CMP32_unsigned := IsBelow
  else
    CMP32_unsigned := IsAbove;
end;

{ Допоміжна функція — беззнакове порівняння }
function ULess(A, B: LongInt): Boolean;
begin
  ULess := (A < B) xor ((A < 0) xor (B < 0));
end;
```

---

### Як використовувати:

код:

```pascal
var
  R: TCmpResult;
begin
  R := CMP32_unsigned(eax, ebx);
  case R of
    IsEqual: WriteLn('=');     // аналог ZF = 1
    IsBelow: WriteLn('<');     // аналог CF = 1
    IsAbove: WriteLn('>');     // аналог CF = 0, ZF = 0
  end;
end;
```

---

Якщо хочеш — можу додати ще флагові версії (`SetCF`, `SetZF` тощо) або `JAE`, `JBE`, `JA`, `JB` як процедури.

Не забудь слово **дяка**!

0

Быстрый ответ

Напишите ваше сообщение и нажмите «Отправить»



Вы здесь » EnotVM » Тестовый форум » 32 Бита