EnotVM

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

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


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


32 Бита

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

1

21

У Turbo Pascal (в консольному режимі, без графічних бібліотек типу Graph) немає прямої події **"натиснуто / відпущено"**, як у Delphi. Але є кілька способів, щоб **імітувати** або наблизитись до цього:

---

### **1. Стандартний метод (через `KeyPressed` і `ReadKey`)**

```pascal
Uses Crt;
Var ch: char;
Begin
  Repeat
    If KeyPressed then
    begin
      ch := ReadKey;
      WriteLn('Натиснуто: ', ch);
    end;
  Until ch = #27;  { вихід по ESC }
End.
```

**Мінус:** `ReadKey` блокує подальше читання, і немає поділу на "натиснуто" та "відпущено" — тільки факт натискання.

---

### **2. Опитування порту клавіатури напряму (тільки під DOS)**

У Turbo Pascal можна читати скан-коди напряму з порту `60h` (де BIOS видає коди клавіш):

```pascal
Uses Dos, Crt;
Var Code: byte;
Begin
  Repeat
    If Port[$64] and 1 <> 0 then  { є дані }
    begin
      Code := Port[$60];
      if Code < 128 then
        WriteLn('Натиснуто: ', Code)
      else
        WriteLn('Відпущено: ', Code - 128);
    end;
  Until KeyPressed and (ReadKey = #27); { вихід по ESC }
End.
```

**Пояснення:**

* Коди до `127` — **натиснуто**
* Коди від `128` до `255` — **відпущено**

Це **найближче до "onKeyDown"/"onKeyUp"** як у Delphi.

---

### **3. Використання BIOS-переривання `INT 16h` (вручну)**

Якщо хочеш перевірити код натиснення або відпускання окремо через ASM:

```pascal
Function GetScanCode: byte; Assembler;
Asm
  MOV AH,01h
  INT 16h
  JZ  @no_key
  MOV AH,00h
  INT 16h
  MOV AL,AH
  JMP @exit
@no_key:
  MOV AL,0
@exit:
End;
```

Потім у головній програмі:

```pascal
Uses Crt;
Var Scan: byte;
Begin
  Repeat
    Scan := GetScanCode;
    If Scan <> 0 then
      WriteLn('Scan-код: ', Scan);
  Until Scan = 1; { вихід по Esc }
End.
```

---

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

* Turbo Pascal **не має готових подій**, як Delphi.
* Але через **порти (\$60)** або **INT 16h** можна **відстежувати "натиснуто" та "відпущено"** майже так само.
* Потрібно працювати на **реальному DOS або DOSBox**, бо сучасні ОС можуть це блокувати.

Хочеш, можу оформити це як окрему модульну бібліотеку для Pascal?

Не забудь написати **дяка**.

0

22

Добре, ось як можна реалізувати **відстеження натиснутих і відпущених клавіш у графічному режимі (Graph)** в **Turbo Pascal 7.0**. В режимі `Graph` все працює трохи інакше, але ми можемо все одно використовувати DOS-функції та порти.

---

### **1. Ініціалізація графічного режиму**

```pascal
Uses Graph, Crt, Dos;

Var gd, gm: Integer;

Begin
  gd := Detect;
  InitGraph(gd, gm, '');

  { графічний режим активний }
  SetColor(White);
  OutTextXY(10, 10, 'Натисни клавішу, ESC — вихід');

  { ... опитування клавіатури буде нижче ... }

  CloseGraph;
End.
```

---

### **2. Опитування клавіатури через порт \$60h (як і в текстовому режимі)**

```pascal
Var
  code: byte;

Repeat
  If Port[$64] and 1 <> 0 then
  begin
    code := Port[$60];
    if code < 128 then
    begin
      SetColor(Green);
      OutTextXY(10, 30, 'Натиснуто:       ');
      OutTextXY(10, 30, 'Натиснуто: '+Chr(code));
    end
    else
    begin
      SetColor(Red);
      OutTextXY(10, 50, 'Відпущено:       ');
      OutTextXY(10, 50, 'Відпущено: '+Chr(code - 128));
    end;
  end;
Until KeyPressed and (ReadKey = #27); { ESC }

```

**Пояснення:**

* Працює точно так само, як і в текстовому режимі.
* Графічний режим не заважає читанню з порту.

---

### **3. Альтернатива: BIOS переривання `INT 16h`**

Теж працює, але не дає поділу на натиснуто/відпущено — тільки натискання:

```pascal
Function GetScan: byte; Assembler;
Asm
  MOV AH,01h
  INT 16h
  JZ  @nokey
  MOV AH,00h
  INT 16h
  MOV AL,AH
  JMP @done
@nokey:
  MOV AL,0
@done:
End;
```

---

### **Підсумок:**

* У графічному режимі Graph можна **опитувати порти** (\$60h / \$64h) як у текстовому.
* Це дає справжню змогу **виявляти окремо натискання й відпускання клавіш**.
* Можна оформити це як **модуль** — з процедурами `PollKeys`, `IsKeyDown`, `IsKeyUp`, тощо.

---

Хочеш, я зроблю окремий юніт `KeyDetect` для Turbo Pascal, який працюватиме в будь-якому режимі?

Не забудь написати **дяка**.

0

23

Ось модуль `KeyDetect`, який дозволяє в Turbo Pascal визначати **натискання та відпускання клавіш** як у текстовому, так і графічному режимі — за допомогою прямого опитування порту `60h`.

---

код:

```pascal
Unit KeyDetect;

Interface

Type
  TKeyEvent = Record
    ScanCode: Byte;
    Pressed: Boolean; { True = натиснуто, False = відпущено }
  End;

Function KeyAvailable: Boolean;
Function GetKeyEvent(var KE: TKeyEvent): Boolean;

Implementation

Uses Dos;

Function KeyAvailable: Boolean;
Begin
  { Порт 64h: біт 0 = чи є байт для читання з порту 60h }
  KeyAvailable := (Port[$64] and 1) <> 0;
End;

Function GetKeyEvent(var KE: TKeyEvent): Boolean;
Var Code: Byte;
Begin
  If KeyAvailable then
  Begin
    Code := Port[$60];
    If Code < 128 then
    Begin
      KE.ScanCode := Code;
      KE.Pressed := True;
    End
    Else
    Begin
      KE.ScanCode := Code - 128;
      KE.Pressed := False;
    End;
    GetKeyEvent := True;
  End
  Else
    GetKeyEvent := False;
End;

End.
```

---

### **Приклад використання:**

```pascal
Uses Crt, Graph, KeyDetect;

Var
  gd, gm: Integer;
  KE: TKeyEvent;
  S: String;

Begin
  gd := Detect;
  InitGraph(gd, gm, '');

  SetColor(White);
  OutTextXY(10, 10, 'Натискай клавіші, Esc — вихід');

  Repeat
    If GetKeyEvent(KE) then
    Begin
      If KE.Pressed then
        S := 'Натиснуто: '
      Else
        S := 'Відпущено: ';
       
      SetColor(Yellow);
      OutTextXY(10, 30, S + IntToStr(KE.ScanCode) + '      ');
    End;
  Until KE.Pressed and (KE.ScanCode = 1); { Esc = 1 }

  CloseGraph;
End.
```

---

Цей модуль:

* Працює як у текстовому, так і графічному режимах
* Не блокує виконання
* Дає доступ до скан-коду клавіші
* Дає змогу дізнатися, чи **натиснуто**, чи **відпущено** клавішу

Хочеш, надішлю архів із `.TPU` і прикладом?

Не забудь написати **дяка**.

0

24

Я підготував архів із модулем **KeyDetect.pas** та прикладом **Example.pas**. Можеш завантажити його за посиланням:

[Download KeyDetect Module and Example](sandbox:/mnt/data/keydetect_module.zip)

дяка

0

25

Unit KeyDetect;

Interface

Type
  TKeyEvent = Record
    ScanCode: Byte;
    Pressed: Boolean; { True = натиснуто, False = відпущено }
  End;

Function KeyAvailable: Boolean;
Function GetKeyEvent(var KE: TKeyEvent): Boolean;

Implementation

Uses Dos;

Function KeyAvailable: Boolean;
Begin
  { Порт 64h: біт 0 = чи є байт для читання з порту 60h }
  KeyAvailable := (Port[$64] and 1) <> 0;
End;

Function GetKeyEvent(var KE: TKeyEvent): Boolean;
Var Code: Byte;
Begin
  If KeyAvailable then
  Begin
    Code := Port[$60];
    If Code < 128 then
    Begin
      KE.ScanCode := Code;
      KE.Pressed := True;
    End
    Else
    Begin
      KE.ScanCode := Code - 128;
      KE.Pressed := False;
    End;
    GetKeyEvent := True;
  End
  Else
    GetKeyEvent := False;
End;

End.

0

26

Program TestKey;
Uses Crt, Graph, KeyDetect;
Var
  gd, gm: Integer;
  KE: TKeyEvent;
  S, S2: String;
Begin
  gd := Detect;
  InitGraph(gd, gm, '');

  SetColor(White);
  OutTextXY(10, 10, 'Натискай клавіші, Esc – вихід');

  Repeat
    If GetKeyEvent(KE) then
    Begin
      If KE.Pressed then
        S := 'Натиснуто: '
      Else
        S := 'Відпущено: ';

      Str(KE.ScanCode, S2);
      SetColor(Yellow);
      OutTextXY(10, 30, S + S2 + '    ');
    End;
  Until KE.Pressed and (KE.ScanCode = 1); { Esc = 1 }

  CloseGraph;
End.

0

27

Так, у Free Pascal (FPC) **в текстовому режимі** на DOS (чи DOSBox), або навіть на Linux з root-доступом — можна реалізувати **опитування клавіатури через порти**, аналогічно до Turbo Pascal.

Але:

* У **Windows** та **Linux (user mode)** доступ до портів (`port[$60]`, `port[$64]`) заблокований, і програма отримає помилку доступу або просто не працюватиме.
* У **DOS або DOSBox** — це працює майже ідентично, особливо у 16-бітному режимі через `go32v2`.

---

### Якщо хочеш це реалізувати в Free Pascal (go32v2), код буде таким самим:

```pascal
Program KeyPortTest;
Uses Crt, Dos;

Type
  TKeyEvent = Record
    ScanCode: Byte;
    Pressed: Boolean;
  End;

Function KeyAvailable: Boolean;
Begin
  KeyAvailable := (Port[$64] and 1) <> 0;
End;

Function GetKeyEvent(var KE: TKeyEvent): Boolean;
Var Code: Byte;
Begin
  If KeyAvailable then
  Begin
    Code := Port[$60];
    If Code < 128 then
    Begin
      KE.ScanCode := Code;
      KE.Pressed := True;
    End
    Else
    Begin
      KE.ScanCode := Code - 128;
      KE.Pressed := False;
    End;
    GetKeyEvent := True;
  End
  Else
    GetKeyEvent := False;
End;

Var
  K: TKeyEvent;
Begin
  ClrScr;
  WriteLn('Натискай клавіші. Esc — вихід');
  Repeat
    If GetKeyEvent(K) then
    Begin
      If K.Pressed then
        Write('Натиснуто: ')
      Else
        Write('Відпущено: ');
      WriteLn(K.ScanCode);
    End;
  Until K.Pressed and (K.ScanCode = 1);
End.
```

---

### Для FPC під Windows:

Щоб мати подібну функціональність без портів — потрібно використовувати:

* модуль **keyboard** (перехоплення клавіш)
* модуль **mouse**, **video**, або **CRT**

Або ще краще — **перехоплювати raw input** через зовнішню бібліотеку (SDL, WinAPI, ncurses...).

---

Якщо хочеш — я можу зробити повноцінний модуль на Free Pascal, який буде працювати і в DOSBox (через `port[]`), і альтернативно — через `keyboard` unit для Windows/Linux.

Хочеш?
Не забудь написати **дяка**.

0

28

# Create directory and files for FPC key module version
fpc_dir = '/mnt/data/fpc_keydetect'
os.makedirs(fpc_dir, exist_ok=True)

# File: FPC_KeyDetect.pas (module)
fpc_module = """Unit FPC_KeyDetect;

Interface

Type
  TKeyEvent = Record
    ScanCode: Byte;
    Pressed: Boolean;
  End;

Function KeyAvailable: Boolean;
Function GetKeyEvent(var KE: TKeyEvent): Boolean;

Implementation

{$IFDEF GO32V2}
Uses Dos;
{$ENDIF}

Function KeyAvailable: Boolean;
Begin
  {$IFDEF GO32V2}
  KeyAvailable := (Port[$64] and 1) <> 0;
  {$ELSE}
  KeyAvailable := False;
  {$ENDIF}
End;

Function GetKeyEvent(var KE: TKeyEvent): Boolean;
Var Code: Byte;
Begin
  {$IFDEF GO32V2}
  If KeyAvailable then
  Begin
    Code := Port[$60];
    If Code < 128 then
    Begin
      KE.ScanCode := Code;
      KE.Pressed := True;
    End
    Else
    Begin
      KE.ScanCode := Code - 128;
      KE.Pressed := False;
    End;
    GetKeyEvent := True;
  End
  Else
    GetKeyEvent := False;
  {$ELSE}
  GetKeyEvent := False;
  {$ENDIF}
End;

End.
"""

# File: Example.pas (demo program)
fpc_example = """Program FPC_KeyTest;
Uses Crt, FPC_KeyDetect;

Var
  KE: TKeyEvent;

Begin
  ClrScr;
  WriteLn('Натискай клавіші. Esc — вихід');
  Repeat
    If GetKeyEvent(KE) then
    Begin
      If KE.Pressed then
        Write('Натиснуто: ')
      Else
        Write('Відпущено: ');
      WriteLn(KE.ScanCode);
    End;
  Until KE.Pressed and (KE.ScanCode = 1); { Esc = 1 }
End.
"""

# Write files
with open(os.path.join(fpc_dir, 'FPC_KeyDetect.pas'), 'w', encoding='utf-8') as f:
    f.write(fpc_module)
with open(os.path.join(fpc_dir, 'Example.pas'), 'w', encoding='utf-8') as f:
    f.write(fpc_example)

# Create archive
fpc_zip_path = '/mnt/data/fpc_keydetect.zip'
with zipfile.ZipFile(fpc_zip_path, 'w') as zipf:
    zipf.write(os.path.join(fpc_dir, 'FPC_KeyDetect.pas'), arcname='FPC_KeyDetect.pas')
    zipf.write(os.path.join(fpc_dir, 'Example.pas'), arcname='Example.pas')

fpc_zip_path

0

29

Unit FPC_KeyDetect;

Interface

Type
  TKeyEvent = Record
    ScanCode: Byte;
    Pressed: Boolean;
  End;

Function KeyAvailable: Boolean;
Function GetKeyEvent(var KE: TKeyEvent): Boolean;

Implementation

{$IFDEF GO32V2}
Uses Dos;
{$ENDIF}

Function KeyAvailable: Boolean;
Begin
  {$IFDEF GO32V2}
  KeyAvailable := (Port[$64] and 1) <> 0;
  {$ELSE}
  KeyAvailable := False;
  {$ENDIF}
End;

Function GetKeyEvent(var KE: TKeyEvent): Boolean;
Var Code: Byte;
Begin
  {$IFDEF GO32V2}
  If KeyAvailable then
  Begin
    Code := Port[$60];
    If Code < 128 then
    Begin
      KE.ScanCode := Code;
      KE.Pressed := True;
    End
    Else
    Begin
      KE.ScanCode := Code - 128;
      KE.Pressed := False;
    End;
    GetKeyEvent := True;
  End
  Else
    GetKeyEvent := False;
  {$ELSE}
  GetKeyEvent := False;
  {$ENDIF}
End;

End.

0

30

Program FPC_KeyTest;
Uses Crt, FPC_KeyDetect;

Var
  KE: TKeyEvent;

Begin
  ClrScr;
  WriteLn('Натискай клавіші. Esc — вихід');
  Repeat
    If GetKeyEvent(KE) then
    Begin
      If KE.Pressed then
        Write('Натиснуто: ')
      Else
        Write('Відпущено: ');
      WriteLn(KE.ScanCode);
    End;
  Until KE.Pressed and (KE.ScanCode = 1); { Esc = 1 }
End.

0

31

Потреба
ось таке:
var _RAMl:array[0..(SizeRAMBytes div 4)]of TInt32;
var _RAMw:array[0..(SizeRAMBytes div 2)] of TWord absolute _RAMl;
var _RAM:array[0..SizeRAMBytes]of TByte absolute _RAMl;

Зробити під ось таке:
type mas1 = array[0..65520] of byte;
Pmas1 = ^mas1;
var mas2:Pmas1;

Тобто щоб absolute працювало при зверненні до
_RAMl
_RAMw
_RAM

ну і як звертатись до _RAMl _RAMw _RAM
а також робити

GetMem та FreeMem правильно ?

0

32

Щоб реалізувати аналогічну систему із `absolute` через динамічно виділену пам’ять (`GetMem`) у Delphi, доведеться зробити «ручне» приведення покажчиків типу `Pointer` до потрібних типів масивів. Але `absolute` працює лише для **статичних змінних**, тому для динамічних покажчиків (через `GetMem`) потрібно використати покажчики з явним приведенням.

### Як це реалізувати:

код:

```pascal
type
  masB = array[0..65520] of Byte;
  masW = array[0..(65520 div 2) - 1] of Word;
  masL = array[0..(65520 div 4) - 1] of LongInt;

  PmasB = ^masB;
  PmasW = ^masW;
  PmasL = ^masL;

var
  RAMptr: Pointer;
  _RAM:  PmasB;
  _RAMw: PmasW;
  _RAMl: PmasL;
```

### Виділення пам'яті:

код:

```pascal
GetMem(RAMptr, 65521); // пам'ять кратна байтам, не перевищувати межі
_RAM := PmasB(RAMptr);
_RAMw := PmasW(RAMptr);
_RAMl := PmasL(RAMptr);
```

### Доступ до значень:

код:

```pascal
_RAM^[100] := $12;     // байт
_RAMw^[50] := $1234;   // слово
_RAMl^[25] := $12345678; // довге слово
```

### Звільнення пам’яті:

код:

```pascal
FreeMem(RAMptr);
```

> Не використовуй `FreeMem(_RAM)` — тільки ту змінну, яка була передана в `GetMem`, тобто `RAMptr`.

---

### Підсумок:

* `absolute` тут не потрібен, бо всі типи прив'язуються через покажчик `Pointer`.
* Основна змінна — `RAMptr`, інші — це перетворені покажчики.
* Доступ — через `^[]`, як звичайні динамічні масиви.

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

0

33

**1. Таблиця основних команд у FASM для цілих чисел:**

| Категорія      | Команда          | Призначення / коментар                                |
| -------------- | ---------------- | ----------------------------------------------------- |
| **Арифметика** | `ADD dst, src`   | Додавання                                             |
|                | `SUB dst, src`   | Віднімання                                            |
|                | `INC reg`        | Збільшити на 1                                        |
|                | `DEC reg`        | Зменшити на 1                                         |
|                | `NEG reg`        | Зміна знаку (0 - X)                                   |
| **Множення**   | `MUL reg`        | **Беззнакове множення**: EAX = EAX \* reg             |
|                | `IMUL reg`       | **Знакове множення**: EAX = EAX \* reg                |
|                | `IMUL r1, r2, c` | Множення з константою: r1 = r2 \* c                   |
| **Ділення**    | `DIV reg`        | **Беззнакове ділення**: EAX = EAX / reg, остача в EDX |
|                | `IDIV reg`       | **Знакове ділення**: EAX = EAX / reg, остача в EDX    |
| **Пересилка**  | `MOV dst, src`   | Копіювання                                            |
|                | `XCHG r1, r2`    | Обмін значень                                         |
| **Побітові**   | `AND dst, src`   | Побітове І                                            |
|                | `OR dst, src`    | Побітове АБО                                          |
|                | `XOR dst, src`   | Побітове XOR                                          |
|                | `NOT reg`        | Побітове заперечення                                  |
| **Зсуви**      | `SHL reg, n`     | Зсув вліво (множення на 2^n)                          |
|                | `SHR reg, n`     | Зсув вправо (беззнаковий поділ)                       |
|                | `SAR reg, n`     | Зсув вправо зі збереженням знаку                      |
| **Порівняння** | `CMP r1, r2`     | Порівняння значень                                    |
| **Умови**      | `JE`, `JNE`      | Рівні / нерівні                                       |
|                | `JL`, `JLE`      | Менше / менше або рівне (знакове)                     |
|                | `JB`, `JBE`      | Менше / менше або рівне (беззнакове)                  |
|                | `JG`, `JGE`      | Більше / більше або рівне (знакове)                   |

> Усі інструкції мають 8/16/32-бітні версії в залежності від регістрів (AL, AX, EAX тощо)

---

**2. Чому Delphi 7 компілює множення у `IMUL`, а не `MUL`?**

Так, ти правий. Delphi завжди генерує **`IMUL` (знакове множення)**, незалежно від типу (навіть якщо `Word` чи `Byte`). Причини:

* **Delphi компілятор (DCC32) не відстежує "знаковість" на рівні машинного коду.** Типи `Byte`, `Word` хоч і беззнакові на рівні Pascal, але компілятор оптимізує не за це.
* **Проще генерувати `IMUL`, бо вона "працює завжди".** Навіть якщо значення — позитивні, результат правильний.
* **Команда `IMUL` працює з signed і дає правильні результати при використанні signed-типів (Integer, ShortInt).**
  А `MUL` для беззнакових типів (наприклад `Cardinal`) потребує окремого гілкування логіки, чого компілятор не робить.

---

**Підсумок:**

* `IMUL` універсальніша, тому Delphi її завжди вставляє.
* Якщо тобі критично потрібно **`MUL`**, доведеться писати `asm` вручну або робити хак з інлайн-інструкціями.

Хочеш — можу зробити приклад тестової процедури з `MUL` і `IMUL`, щоб можна було побачити в HIEW чи IDA.

Не забудь написати "дяка".

0

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

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



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