Word dword что это

Уважаемые коллеги, мы рады предложить вам, разрабатываемый нами учебный курс по программированию ПЛК фирмы Beckhoff с применением среды автоматизации TwinCAT. Курс предназначен исключительно для самостоятельного изучения в ознакомительных целях. Перед любым применением изложенного материала в коммерческих целях просим связаться с нами. Текст из предложенных вам статей скопированный и размещенный в других источниках, должен содержать ссылку на наш сайт heaviside.ru. Вы можете связаться с нами по любым вопросам, в том числе создания для вас систем мониторинга и АСУ ТП.


Типы данных в языках стандарта МЭК 61131-3

Уважаемые коллеги, в этой статье мы будем рассматривать важнейшую для написания программ тему — типы данных. Чтобы читатели понимали, в чем отличие одних типов данных от других и зачем они вообще нужны, мы подробно разберем, каким образом данные представлены в процессоре. В следующем занятии будет большая практическая работа, выполняя которую, можно будет потренироваться объявлять переменные и на практике познакомится с особенностями выполнения математических операций с различными типами данных.

Простые типы данных

В прошлой статье мы научились записывать цифры в двоичной системе счисления. Именно такую систему счисления используют все компьютеры, микропроцессоры и прочая вычислительная техника. Теперь мы будем изучать типы данных.

Любая переменная, которую вы используете в своем коде, будь то показания датчиков, состояние выхода или выхода, состояние катушки или просто любая промежуточная величина, при выполнении программы будет хранится в оперативной памяти. Чтобы под каждую используемую переменную на этапе компиляции проекта была выделена оперативная память, мы объявляем переменные при написании программы. Компиляция, это перевод исходного кода, написанного программистом, в команды на языке ассемблера понятные процессору. Причем в зависимости от вида применяемого процессора один и тот же исходный код может транслироваться в разные ассемблерные команды (вспомним что ПЛК Beckhoff, как и персональные компьютеры работают на процессорах семейства x86).

Как помните, из статьи Знакомство с языком LD, при объявлении переменной необходимо указать, к какому типу данных будет принадлежать переменная. Как вы уже можете понять, число B016 будет занимать гораздо меньший объем памяти чем число 4 C4E5 01E7 7A9016. Также одни и те же операции с разными типами данных будут транслироваться в разные ассемблерные команды. В TwinCAT используются следующие типы данных:

Классификация типов данных TwinCAT 3

Биты

BOOL — это простейший тип данных, как уже было сказано, этот тип данных может принимать только два значения ​0 и 1. Так же в TwinCAT, как и в большинстве языков программирования, эти значения, наравне с 0 и 1, обозначаются как TRUE и FALSE и несут в себе количество информации, соответствующее одному биту. Минимальным объемом данных, который читается из памяти за один раз, является байт, то есть восемь бит. Поэтому, для оптимизации скорости доступа к данным, переменная типа BOOL занимает восемь бит памяти. Для хранения самой переменной используется нулевой бит, а биты с первого по седьмой заполнены нулями. Впрочем, на практике о таком нюансе приходится вспоминать достаточно редко.

BIT — то же самое, что и BOOL, но в памяти занимает 1 бит. Как можно догадаться, операции с этим типом данных медленнее чем с типом BOOL, но он занимает меньше места в памяти. Тип данных BIT отсутствует в стандарте МЭК 61131-3 и поддерживается исключительно в TwinCAT, поэтому стоит отдавать предпочтение типу BOOL, когда у вас нет явных поводов использовать тип BIT.

Целочисленные типы данных

BYTE — тип данных, по размеру соответствующий одному байту. Хоть с типом BYTE можно производить математические операции, но в первую очередь он предназначен для хранения набора из ​8 ​бит. Иногда в таком виде удобнее, чем побитно, передавать данные по цифровым интерфейсам, работать с входами выходами и так далее. С такими вопросами мы будем знакомится далее по мере изучения курса. В переменную типа BYTE ​можно записать числа из диапазона 0..255 (0..28-1).

WORD — то же самое, что и BYTE, но размером ​16​ бит. В переменную типа WORD можно записать числа из диапазона 0..65 535​ ​(0..216-1). Тип данных WORD переводится с английского как «слово». Давным-давно термином машинное слово называли группу бит, обрабатываемых вычислительной машиной за один раз. Была уместна фраза «Программа состоит из машинных слов.». Со временем этим термином перестали пользоваться в прямом его значении, и сейчас под термином «машинное слово» обычно подразумевается группа из 16​ бит.

DWORD — то же самое, что и BYTE, но размером 32 бит. В переменную типа DWORD можно записать числа из диапазона 0..4 294 967 295​​ ​(0..232-1). DWORD — это сокращение от double word, что переводится как двойное слово. Довольно часто буква «D» перед каким-либо типом данных значит, что этот тип данных в два раза длиннее, чем исходный.

LWORD — то же самое, что и BYTE, но размером 64 ;бит. В переменную типа LWORD можно записать числа из диапазона 0..18 446 744 073 709 551 615 (0..264-1). LWORD — это сокращение от long word, что переводится как длинное слово. Приставка «L» перед типом данных, как правило, означает что такой тип имеет длину 64 бита.

SINT — знаковый тип данных, длинной 8 бит. В переменную типа SINT можно записать числа из диапазона -128..127​​ ​(-27..27-1). В отличии от всех предыдущих типов данных этот тип данных предназначен для хранения именно чисел, а не набора бит. Слово знаковый в описании типа означает, что такой тип данных может хранить как положительные, так и отрицательные значения. Для хранения знака числа предназначен старший, в данном случае седьмой, разряд числа. Если старший разряд имеет значение 0, то число интерпретируется как положительное, если 1, то число интерпретируется как отрицательное. Приставка «S» означает short, что переводится с английского как короткий. Как вы догадались, SINT короткий вариант типа INT.

USINT — беззнаковый тип данных, длинной 8 бит. В переменную типа USINT можно записать числа из диапазона 0..255​​ ​(0..28-1). Приставка «U» означает unsigned, переводится как беззнаковый.

Остальные целочисленные типы аналогичны уже описанным и отличаются только размером. Сведем все целочисленные типы в таблицу.

Тип данных Нижний предел Верхний предел Занимаемая память
BYTE 0 255 8 бит
WORD 0 65 535 16 бит
DWORD 0 4 294 967 295 32 бит
LWORD 0 264-1 64 бит
SINT -128 127 8 бит
USINT 0 255 8 бит
INT -32 768 32 767 16 бит
UINT 0 65 535 16 бит
DINT -2 147 483 648 2 147 483 647 32 бит
UDINT 0 4 294 967 295 32 бит
LINT -263 -263-1 64 бит
ULINT 0 -264-1 64 бит

Выше мы рассматривали целочисленные типы данных, то есть такие типы данных, в которых отсутствует запятая. При совершении математических операций с целочисленными типами данных есть некоторые особенности:

  • Округление при делении: округление всегда выполняется вниз. То есть дробная часть просто отбрасывается. Если делимое меньше делителя, то частное всегда будет равно нулю, например, 10/11 = 0.
  • Переполнение: если к целочисленной переменной, например, SINT, имеющей значение 255, прибавить 1, переменная переполнится и примет значение 0. Если прибавить 2, переменная примет значение 1 и так далее. При операции 0 — 1 результатом будет 255. Это свойство очень схоже с устройством стрелочных часов. Если сейчас 2 часа, то 5 часов назад было 9 часов. Только шкала часов имеет пределы не 1..12, а 0..255. Иногда такое свойство может использоваться при написании программ, но как правило не стоит допускать переполнения переменных.

Подробно такие нюансы разбираются в пособиях по дискретной математике. Мы на них пока что останавливаться не будем, но о приведенных двух особенностях не стоит забывать при написании программ.

Можно встретить упоминания о данных с фиксированной запятой, это такие данные, в которых количество знаков после запятой строго фиксировано. В TwinCAT типы данных с фиксированной запятой отсутствуют в чистом виде. TwinCAT поддерживает типы данных с плавающей запятой, то есть количество знаков до и после запятой может быть любым в пределах поддерживаемого диапазона.

Типы данных с плавающей запятой

REAL — тип данных с плавающей запятой длинной 32 бита. В переменную типа REAL можно записать числа из диапазона -3.402 82*1038..3.402 82*1038​​.

LREAL — тип данных с плавающей запятой длинной 64 бита. В переменную типа LREAL можно записать числа из диапазона -1.797 693 134 862 315 8*10308..1.797 693 134 862 315 8*10308​​.

При присваивании значения типам REAL и LREAL присваиваемое значение должно содержать целую часть, разделительную точку и дробную часть, например, 7.4 или 560.0.

Так же при записи значения типа REAL и LREAL использовать экспоненциальную (научную) форму. Примером экспоненциальной формы записи будет Me+P, в этом примере

  • M называется мантиссой.
  • e называется экспонентой (от англ. «exponent»), означающая «·10^» («…умножить на десять в степени…»),
  • P называется порядком.

Примерами такой формы записи будет:

  • 1.64e+3 расшифровывается как 1.64e+3 = 1.64*103 = 1640.
  • 9.764e+5 расшифровывается как 9.764e+5 = 9.764*105 = 976400.
  • 0.3694e+2 расшифровывается как 0.3694e+2 = 0.3694*102 = 36.94.

Еще один способ записи присваиваемого значения переменной типа REAL и LREAL, это добавить к числу префикс REAL#, например, REAL#7.4 или REAL#560. В таком случае можно не указывать дробную часть.

Старший, 31-й бит переменной типа REAL представляет собой знак. Следующие восемь бит, с 30-го по 23-й отведены под экспоненту. Оставшиеся 23 бита, с 22-го по 0-й используются для записи мантиссы.

В переменной типа LREAL старший, 63-й бит также используется для записи знака. В следующие 11 бит, с 62 по 52-й, записана экспонента. Оставшиеся 52 бита, с 51-го по 0-й, используются для записи мантиссы.

При записи числа с большим количеством значащих цифр в переменные типа REAL и LREAL производится округление. Необходимо не забывать об этом в расчетах, к которым предъявляются строгие требования по точности. Еще одна особенность, вытекающая из прошлой, если вы хотите сравнить два числа типа REAL или LREAL, прямое сравнение мало применимо, так как если в результате округления числа отличаются хоть на малую долю результат сравнения будет FALSE. Чтобы выполнить сравнение более корректно, можно вычесть одно число из другого, а потом оценить больше или меньше модуль получившегося результата вычитания, чем наибольшая допустимая разность. Поведение системы при переполнении переменных с плавающей запятой не определенно стандартом МЭК 61131-3, допускать его не стоит.

Строковые типы данных

STRING — тип данных для хранения символов. Каждый символ в переменной типа STRING хранится в 1 байте, в кодировке Windows-1252, это значит, что переменные такого типа поддерживают только латинские символы. При объявлении переменной количество символов в переменной указывается в круглых или квадратных скобках. Если размер не указан, при объявлении по умолчанию он равен 80 символам. Для данных типа STRING количество содержащихся в переменной символов не ограниченно, но функции для обработки строк могут принять до 255 символов.

Объем памяти, необходимый для переменной STRING, всегда составляет 1 байт на символ +1 дополнительный байт, например, переменная объявленная как «STRING [80]» будет занимать 81 байт. Для присвоения константного значения переменной типа STRING присваемый текст необходимо заключить в одинарные кавычки.

Пример объявления строки на 35 символов:

sVar : STRING(35) := 'This is a String'; (*Пример объявления переменной типа STRING*)

WSTRING — этот тип данных схож с типом STRING, но использует по 2 байта на символ и кодировку Unicode. Это значит что переменные типа WSTRING поддерживают символы кириллицы. Для присвоения константного значения переменной типа WSTRING присваемый текст необходимо заключить в двойные кавычки.

Пример объявления переменной типа WSTRING:

wsVar : WSTRING := "This is a WString"; (*Пример объявления переменной типа WSTRING*)

Если значение, присваиваемое переменной STRING или WSTRING, содержит знак доллара ($), следующие два символа интерпретируются как шестнадцатеричный код в соответствии с кодировкой Windows-1252. Код также соответствует кодировке ASCII.

Код со знаком доллара Его значение в переменной
$<восьмибитное число> Восьмибитное число интерпретируется как символ в кодировке ISO / IEC 8859-1
‘$41’ A
‘$9A’ ©
‘$40’ @
‘$0D’, ‘$R’, ‘$r’ Разрыв строки
‘$0A’, ‘$L’, ‘$l’, ‘$N’, ‘$n’ Новая строка
‘$P’, ‘$p’ Конец страницы
‘$T’, ‘$t’ Табуляция
‘$$’ Знак доллара
‘$’ ‘ Одиночная кавычка

Такое разнообразие кодировок связанно с тем, что у всех из них первые 128 символов соответствуют кодовой таблице ASCII, но в статье для каждого случая кодировка указывалась так же, как она указана в infosys.

Пример:

  1. VAR CONSTANT

  2. sConstA : STRING :='Hello world';

  3. sConstB : STRING :='Hello world $21'; (*Пример объявления переменной типа STRING с спец символом*)

  4. END_VAR

Типы данных времени

TIME — тип данных, предназначенный для хранения временных промежутков. Размер типа данных 32 бита. Этот тип данных интерпретируется в TwinCAT, как переменная типа DWORD, содержащая время в миллисекундах. Нижний допустимый предел 0 (0 мс), верхний предел 4 294 967 295 (49 дней, 17 часов, 2 минуты, 47 секунд, 295 миллисекунд). Для записи значений в переменные типа TIME используется префикс T# и суффиксы d: дни, h: часы, m: минуты, s: секунды, ms: миллисекунды, которые должны располагаться в порядке убывания.

Примеры корректного присваивания значения переменной типа TIME:

TIME1 : TIME := T#14ms;
TIME1 : TIME := T#100s12ms; // Допускается переполнение в старшем отрезке времени.
TIME1 : TIME := t#12h34m15s;

Примеры некорректного присваивания значения переменной типа TIME, при компиляции будет выдана ошибка:

TIME1 : TIME := t#5m68s;   // Переполнение не в старшем отрезке времени недопустимо
TIME1 : TIME := 15ms;   // Пропущен префикс T#
TIME1 : TIME := t#4ms13d;   // Не соблюден порядок записи временных отрезок

LTIME — тип данных аналогичен TIME, но его размер составляет 64 бита, а временные отрезки хранятся в наносекундах. Нижний допустимый предел 0, верхний предел 213 503 дней, 23 часов, 34 минуты, 33 секунд, 709 миллисекунд, 551 микросекунд и 615 наносекунд. Для записи значений в переменные типа LTIME используется префикс LTIME#. Помимо суффиксов, используемых для записи типа TIME для LTIME, используются µs: микросекунды и ns: наносекунды.

Пример:

LTIME1 : LTIME := LTIME#1000d15h23m12s34ms2us44ns; (*Пример объявления переменной типа LTIME*)

TIME_OF_DAY (TOD) — тип данных для записи времени суток. Имеет размер 32 бита. Нижнее допустимое значение 0, верхнее допустимое значение 23 часа, 59 минут, 59 секунд, 999 миллисекунд. Для записи значений в переменные типа TOD используется префикс TIME_OF_DAY# или TOD#, значение записывается в виде <часы : минуты : секунды> . В остальном этот тип данных аналогичен типу TIME.

Пример:

TIME_OF_DAY#15:36:30.123
tod#00:00:00

Date — тип данных для записи даты. Имеет размер 32 бита. Нижнее допустимое значение 0 (01.01.1970), верхнее допустимое значение 4 294 967 295  (7 февраля 2106), да, здесь присутствует возможный компьютерный апокалипсис, но учитывая запас по верхнему пределу, эта проблема не слишком актуальна. Для записи значений в переменные типа TOD используется префикс DATE# или D#, значение записывается в виде <год — месяц — дата>. В остальном этот тип данных аналогичен типу TIME.

DATE#1996-05-06
d#1972-03-29

DATE_AND_TIME (DT) — тип данных для записи даты и времени. Имеет размер 32 бита. Нижнее допустимое значение 0 (01.01.1970), верхнее допустимое значение 4 294 967 295 (7 февраля 2106, 6:28:15). Для записи значений в переменные типа DT используется префикс DATE_AND_TIME # или DT#, значение записывается в виде <год — месяц — дата — час : минута : секунда>. В остальном этот тип данных аналогичен типу TIME.

DATE_AND_TIME#1996-05-06-15:36:30
dt#1972-03-29-00:00:00

На этом раз мы заканчиваем рассмотрение типов данных. Сейчас мы разобрали не все типы данных, остальные можно найти в infosys по пути TwinCAT 3 → TE1000 XAE → PLC → Reference Programming → Data types.

Следующая статья будет целиком состоять из практической работы, мы напишем калькулятор на языке LD.


В VC ++ 6.0 BYTE, WORD, DWORD — это целое число без знака, которое определено в WINDEF.h

typedef unsigned char BYTE;

typedef unsigned short WORD;

typedef unsigned long DWORD;

Другими словами, BYTE — это тип без знака, WORD — беззнаковый короткий тип, а DWORD — беззнаковый длинный тип.

В VC ++ 6.0 1 байт символа, short — 2 байта, int и long — 4 байта, поэтому можно считать, что переменные, определяемые BYTE, WORD, DWORD, — это 1 раздел, 2 байта, 4 слова. Раздел.

То есть: BYTE = unsigned char, WORD = unsigned short, DWORD = unsigned long

DWORD обычно используется для сохранения адреса или сохранения указателя

Разница между словом и словом

Определение WORD и DWORD в основном для: 1. Легко трансплантировать; 2. Более строгая проверка типов

WORD фиксируется на 2 байта, DWORD фиксируется на 4 байта

Int, с разными операционными системами, имеет разное количество байтов, в 32-битной операционной системе — 4 байта, в 16-битной операционной системе — 2 байта

В операции сериализации, поскольку сериализация хранится в соответствии с потоком байтов, чтобы гарантировать, что она не будет выровнена, необходимо использовать тип данных с четким числом байтов.

Дневники чайника. Чтива 0, виток0

Пятый день.
О словах и двойных словах
(форматы данных)

Не сказать чтоб эта тема была самая сложная, но то, что она самая запутанная — это 100%.

То, что я здесь напишу, довольно туго для восприятия, однако со временем всё уляжется.
Не расстраивайтесь, если вдруг в этой главе покажется, что Ассемблер очень сложный, вспомните о таком фокусе.

Не каждый водитель знает, что для того, чтоб машина ехала вперёд, колесо в точке соприкосновения с дорогой должно двигаться назад.
Хотя такая информация была сообщена каждому водителю и каждому человеку.
Ну, едет машина и едет, какая разница, куда крутятся колёса.
Это нужно знать только при смене колёс, чтоб не перепутать направленность резины, или во время ремонта.

Примерно такая же информация будет изложена в этой главе. На практике всё быстро усвоится, если вам придется сталкиваться с машинным кодом и данными.

Матрос, ты должен знать своего друга (со стороны потенциального противника) лучше, чем себя.

Бинарники организуются в отряды по восемь, эти отряды называются байтами. Запомнил?

Байт — это минимальная расчетная единица бинарной армии. Запомнил?

2 байта организуются в слово (word).

4 байта организуются в двойное слово (dword, а пОлно — double word).

Dword — это самый распространённый набор битов в Win32-программах. Так как:

Dword =4 байта = 32 бита.

В предыдущей программе мы столкнулись вот с такой строкой:

00000003: 66C70701020304      mov  d,[bx],004030201

«d,» здесь как раз и заменяет dword.

Я объяснил, что когда операнд находится в квадратных скобках, при команде mov
это означает, что нужно производить действие по адресу в памяти, указанному операндом.
То есть в BX раньше должен быть положен адрес. В ходе выполнения этой строки BX не изменяется,
изменится только память по адресу, указанному BX. Размер изменяемой памяти dword (4 байта, двойное слово).

Остаётся маленький такой вопросик: почему в дизассемблере байты операндов команд процессора
мы видим зеркально значениям операндов в командах Асма?

Вот:

          байты инструкции    Команда Ассемблера
00000003: 66C707 01020304     mov  d,[bx],004030201

Запомни, матрос, Бинарники очень хитрые. Для того, чтоб их враг путал байты по старшинству,
в каждой целой боевой единице, будь то word (2 байта), dword (4 байта), qword (8 байт),
байты строятся от младшего к старшему, черт их дери!

mov   dword ptr [bx],04030201

Число — 04030201h

Адрес, который был указан в BX, равен 0133h.

А действие выглядит так:

Адрес  значение
0133   01
0134   02
0135   03
0136   04

В отладчике вы видели это вот так:

0130  16 CD 20 01 02 03 04 24 E2 04 B7 9A 66 B9 FF FF
                ^  ^  ^  ^

Мы пишем и читаем текст по-европейски — слева направо.
Но для чисел большинство людей использует арабскую запись — справа налево (хотя читаем числа тупо от старшей цифры =).

К великому огорчению, программистами был принят смешанный формат отображения данных.
Каждый байт отображается по арабской системе, а целая группа байтов — по европейской.
Выходит, что на экране мы видим разную запись. Если программа-дизассемблер или отладчик
воспринимают группы байтов как ЦЕЛОЕ число, то оно отображается арабской записью, как в колонке команд Ассемблера: 04 03 02 01,
а если речь идёт просто о нескольких байтах, то мы видим европейскую запись, только за букву принят целый байт, что и показано выше: 01 02 03 04.
Всё это лишь вопрос отображения на экране или в документах.
Например, если использовать запись цифровых значений от нижнего правого угла экрана до верхнего левого (справа налево, снизу вверх), то вообще ничего переворачивать не нужно! То есть если бы была принята запись
«справа налево всё» или «слева направо всё», то подобных проблем не было бы вообще.

Допустим, мы набрали вот такую строку:

mov  word ptr [00000800h],0BBAAh

Здесь мы указали, что размер данных word (2 байта) и эти данные будут помещены в память по адресу 800h.

Объясню сейчас коротко.

Раз мы имеем заданный размер word (или как в Hiew’е «w,»), мы имеем некое ЦЕЛОЕ.

Младший байт (у нас AA) будет находиться по наименьшему адресу,
а старший байт (BB) — по более старшему адресу.

Вот как эта строка будет выглядеть в Hiew’e:

Адрес     Байты              имя        операнды
00000000: C7060008AABB       mov      w,[0800],0BBAA

В колонке операндов — так, как мы вводили (число BBAAh). А вот в колонке байтов
мы видим зеркальное расположение байтов операндов — 00 08, AA BB.

После выполнения такой команды в память байты запишутся вот так:
0800 AA (младший адрес — младший байт целого)
0801 BB (старший адрес — старший байт целого)

И точно так же устроены dword. Допустим:

mov  dword ptr [00000800h],0DDCCBBAAh

0800 AA (0-й адрес — 0-й байт целого)
0801 BB (1-й адрес — 1-й байт целого)
0802 CC (2-й адрес — 2-й байт целого)
0803 DD (3-й адрес — 3-й байт целого)

Вопрос, который наверняка возник у всех (и я предполагаю, что у многих в нецензурной форме): «На… в смысле зачем?»

Это нужно для того, чтобы процессор забирал байты из памяти, начиная с младшего (так быстрее вычислять).
Ведь получается, что адрес числовой переменной любого размера, хоть word, хоть dword,
будет указывать на младший байт в числе, далее пойдёт следующий байт в числе, и самый старший байт в числе всегда окажется в конце.
Вычислять процессору так значительно быстрее.

Я думаю, что многие читатели совсем запутались, тут есть только одно утешение: перекладывать байты в голове совсем не нужно.
Чтобы получить целое число из байтов или, наоборот, из числа сложить байты, достаточно переключить режим отображения в программе.
Мало того, при простом программировании можно забыть об этих премудростях и вспоминать лишь изредка.

Всё, из теории остались только циклы и стек, о них мы будем говорить завтра.

Матрос! Я что-то не заметил, чтоб ты разрабатывал кнопку F10!

Если ты хочешь стать капитаном собственного корабля, пусть даже и без гипердвигателя, ты должен отлаживать и отлаживать.

Послезавтра я увижу своё отражение в F10-key у тебя на клавиатуре или я высажу тебя на ближайшей заброшенной планете.

А чтоб было веселей давить на кнопку F10, загони в отладчик следующую программу (prax03.com).

Набивайте всё сами, только так можно научиться.

В Hiew’e она должна выглядеть так:

00000000: B80300     mov  ax,00003
00000003: CD10       int  010
00000005: B402       mov  ah,002
00000007: 8B167501   mov  dx,[0175]
0000000B: CD10       int  010
0000000D: FEC6       inc  dh
0000000F: 80C203     add  dl,003
00000012: 89167501   mov  [0175],dx
00000016: B409       mov  ah,009
00000018: BA5001     mov  dx,00150
0000001B: CD21       int  021
0000001D: 803E760119 cmp  b,[0176],019
00000022: 75E1       jne  000000005
00000024: B410       mov  ah,010
00000026: CD16       int  016
00000028: CD20       int  020

Но это не всё, теперь переключитесь на Hex-режим (F4) и добейте программу следующими байтами после всего кода.
Это будут «данные».

00000020:                             20 20 20-20 20 20 20
00000030:  20 20 20 20-20 20 20 20-20 20 20 20-20 20 20 20
00000040:  91 E2 E0 AE-AA A0 20 E2-A5 AA E1 E2-A0 3A 20 20  Строка текста:
00000050:  2D 3D 80 E1-AC 3D 2D 24-20 20 20 20-20 20 20 20  -=Асм=-$
00000060:  20 20 20 20-20 20 20 20-20 20 20 20-20 20 20 20
00000070:  77 6F 72 64-21 00 00 21-77 6F 72 64-20 20 20 20  word!  !word
00000080:  20 20 20 20-20 20 20 20-20 20 20 20-20 20 20 20

На самом деле все эти пробелы (20h) программе НЕ нужны. Но когда вы будете смотреть программу в отладчике, они вам помогут.

Если у вас будет сдвиг хоть на байт, программа будет ошибочной. Поэтому проверьте, чтобы строка «-=Асм=-$» начиналась с 50h и,
что ещё более важно, два нулевых байта (00 00) должны быть в файле по адресам 75 и 76h.
Обязательно посмотрите в отладчике, что будет происходить с этими байтами (там они будут 175h и 176h).
Всё остальное здесь мишура и для выполнения программы совершенно не имеет значения.

При отладке могут появляться сообщения насчёт экрана… ну и фиг с ними.
Если в CV опция Screen Swap ещё не выключена, то это обязательно нужно сделать.

Результат действия этой программы — вывод строк по диагонали от верхнего левого угла до нижнего правого угла.

Здесь очень много нового, и я надеюсь, вам будет интересно узнать, как работают новые команды CMP и JNE.

Попробуйте сами разобраться, что происходит в программе на практике.
Как я уже писал, прерываний (команда int) при написании программ Win32 мы использовать не будем.
Поэтому можете не заострять на них внимание.
Достаточно знать, что это полезные подпрограммы,
часть которых заложена ещё в BIOS (basic input/output system — базовая система ввода/вывода).

Я вынужден их использовать только для того, чтобы программа была полноценная и вы могли наглядно изучать главные команды Ассемблера.

В данном примере будут задействованы int 10h для очистки экрана (AL=3) и для расположения курсора текста (AH=2).
Ну и int 21h для вывода текста на экран.
Всё, других прерываний в уроках больше не будет. О них за долгие годы написано достаточно.

Вот как программа должна трактоваться (сразу скажу — про метки я всё объясню позже).

mov    ax,03      ; В AX значение 3 (параметр видеорежима 80x25)
int    010        ; Подпрограмма установит текстовый видеорежим 80х25,
                  ; при этом экран ДОС очистится

TuLuLa:           ; Всего лишь условная метка, в коде программы её нет 
                  ; Здесь могли быть любые буквы и ":"

mov    ah,02      ; В AH поместить 02 (для int10 - установка курсора)
mov    dx,[0175h] ; В DX загрузить значение из памяти по адресу 175h
                  ; в первый раз там нули, 
                  ; а в следующий проход значения будут увеличены

int    10         ; Установит текстовый курсор в положение, указанное в DX
                  ; DH - номер линии, DL - номер колонки (верх.лев.- 0000)

inc    dh         ; Прибавить 1 к значению DH
add    dl,03      ; Прибавить 3 к значению DL

mov    [0175h],dx ; Сохранить DH и DL в память по адресу 175h

mov    ah,09      ; Функция в int21h - вывод на экран с позиции курсора
mov    dx,00150h  ; В DX адрес текстовой строки, заканчивающейся $
int    021        ; Подпрограмма выведет текстовую строку на экран


cmp    byte ptr [176h],19h ;Сверяется значение байта в памяти с числом 25d
jne    TuLuLa              ;и если значения не равны,
                           ;то прыг на выполнение от метки TuLuLa

mov    ah,010     ; а если были равны, то выполнится эта строка
int    016        ; Подпрограмма дождётся нажатия клавиши
                  ; и вернёт управление на следующую строку

int    020        ; код завершения программы

Перед разбором этой программы я хочу рассказать о ключевом понятии в программировании — циклы.

Bitfry

<<предыдущая глава     следующая глава>>

Вернуться на главную

Hosted by uCoz

Целые числа


Данные каким-либо образом необходимо представлять в памяти компьютера. Существует множество различных типов данных, простых и довольно сложных, имеющих большое число компонентов и свойств. Однако, для компьютера необходимо использовать некий унифицированный способ представления данных, некоторые элементарные составляющие, с помощью которых можно представить данные абсолютно любых типов. Этими составляющими являются числа, а вернее, цифры, из которых они состоят. С помощью цифр можно закодировать практически любую дискретную информацию. Поэтому такая информация часто называется цифровой (в отличие от аналоговой, непрерывной).

Первым делом необходимо выбрать систему счисления, наиболее подходящую для применения в конкретных устройствах. Для электронных устройств самой простой реализацией является двоичная система: есть ток — нет тока, или малый уровень тока — большой уровень тока. Хотя наиболее эффективной являлась бы троичная система. Наверное, выбор двоичной системы связан еще и с использование перфокарт, в которых она проявляется в виде наличия или отсутствия отверстия. Отсюда в качестве цифр для представления информации используются 0 и 1.

Таким образом данные в компьютере представляются в виде потока нулей и единиц. Один разряд этого потока называется битом. Однако в таком виде неудобно оперировать с данными вручную. Стандартом стало разделение всего потока на равные последовательные группы из 8 битов — байты или октеты. Далее несколько байтов могут составлять слово. Здесь следует разделять машинное слово и слово как тип данных. В первом случае его разрядность обычно равна разрядности процессора, т.к. машинное слово является наиболее эффективным элементом для его работы. В случае, когда слово трактуется как тип данных (word), его разрядность всегда равна 16 битам (два последовательных байта). Также как типы данных существую двойные слова (double word, dword, 32 бита), четверные слова (quad word, qword, 64 бита) и т.п.

Теперь мы вплотную подошли к представлению целых чисел в памяти. Т.к. у нас есть байты и различные слова, то можно создать целочисленные типы данных, которые будут соответствовать этим элементарным элементам: byte (8 бит), word (16 бит), dword (32 бита), qword (64 бита) и т.д. При этом любое число этих типов имеет обычное двоичное представление, дополненное нулями до соответствующей размерности. Можно заметить, что число меньшей размерности можно легко представить в виде числа большей размерности, дополнив его нулями, однако в обратном случае это не верно. Поэтому для представления числа большей размерности необходимо использовать несколько чисел меньшей размерности. Например:

  • qword (64 бита) можно представить в виде 2 dword (32 бита) или 4 word (16 бит) или 8 byte (8 бит);
  • dword (32 бита) можно представить в виде 2 word (16 бит) или 4 byte (8 бит);
  • word (16 бит) можно представить в виде 2 byte (8 бит);

Если A — число, B1..Bk — части числа, N — разрядность числа, M — разрядность части, N = k*M, то:

Например:

  • A = F1E2D3C4B5A69788 (qword)
  • A = 232 * F1E2D3C4 (dword) + 20 * B5A69788 (dword)
  • A = 248 * F1E2 (word) + 232 * D3C4 (word) + 216 * B5A6 (word) + 20 * 9788 (word)
  • A = 256 * F1 (byte) + 248 * E2 (byte) + … + 28 * 97 (byte) + 20 * 88 (byte)

Существуют понятия младшая часть (low) и старшая часть (hi) числа. Старшая часть входит в число с коэффициентом 2N-M, а младшая с коэффициентом 20. Например:

Байты числа можно хранить в памяти в различном порядке. В настоящее время используются два способа расположения: в прямом порядке байт и в обратном порядке байт. В первом случае старший байт записывается в начале, затем последовательно записываются остальные байты, вплоть до младшего. Такой способ используется в процессорах Motorola и SPARC. Во втором случае, наоборот, сначала записывает младший байт, а затем последовательно остальные байты, вплоть до старшего. Такой способ используется в процессорах архитектуры x86 и x64. Далее приведен пример:

Используя подобные целочисленные типы можно представить большое количество неотрицательных чисел: от 0 до 2N-1, где N — разрядность типа. Однако, целочисленный тип подразумевает представление также и отрицательных чисел. Можно ввести отдельные типы для отрицательных чисел от -2N до -1, но тогда такие типы потеряют универсальность хранить и неотрицательные, и отрицательные числа. Поэтому для определения знака числа можно выделить один бит из двоичного представления. По соглашению, это старший бит. Остальная часть числа называется мантиссой.

Если старший бит равен нулю, то мантисса есть обычное представление числа от 0 до 2N-1-1. Если же старший бит равен 1, то число является отрицательным и мантисса представляет собой так называемый дополнительный код числа. Поясним на примере:

Как видно из рисунка, дополнительный код равен разнице между числом 2N-1 и модулем исходного отрицательного числа (127 (1111111) = 128 (10000000) — |-1| (0000001)). Из этого вытекает, что сумма основного и дополнительного кода одного и того же числа равна 2N-1.

Из вышеописанного получается, что можно использовать только целочисленные типы со знаком для описания чисел. Однако существует множество сущностей, которые не требуют отрицательных значений, а значит, место под знак можно включить в представление неотрицательного числа, удвоив количество различных неотрицательных значений. Как результат, в современных компьютерах используются как типы со знаком или знаковые типы, так и типы без знака или беззнаковые типы.

В итоге можно составить таблицу наиболее используемых целочисленных типов данных:

Общее название Название в Pascal Название в C++ Описание Диапазон значений
unsigned byte byte unsigned char беззнаковый 8 бит 0..255
signed byte shortint char знаковый 8 бит -128..127
unsigned word word unsigned short беззнаковый 16 бит 0..65535
signed word smallint short знаковый 16 бит -32768..32767
unsigned double word cardinal unsigned int беззнаковый 32 бита 0..232-1
signed double word integer int знаковый 32 бита -231..231-1
unsigned quad word uint64 unsigned long long
unsigned __int64_t (VC++)
беззнаковый 64 бита 0..264-1
signed quad word int64 long long
__int64_t (VC++)
знаковый 64 бита -263..263-1

Прежде
чем переходить к непосредственно
изучению МЭК-языков,
необходимо познакомиться с общими
элементами этих
языков. Общие элементы служат единым
фундаментом, по­зволяющим
объединить многоязычные компоненты в
одном про­екте.
В главе будут рассмотрены данные, с
которыми способен работать
стандартный ПЛК, форматы их представления
и наиболее общие
приемы и тонкости работы с ними.

Типы данных

Тип
данных переменной определяет род
информации, диапазон представления и
множество допустимых операций. Языки
МЭК
используют идеологию строгой проверки
типов данных. Это означает,
что любую переменную можно использовать
только по­сле
ее объявления. Присваивать значение
одной переменной другой
можно, только если они обе одного типа.
Допускается также присваивание
значения переменной совместимого типа,
имеющей более
широкое множество допустимых значений.
В этом случае происходит
неявное преобразование типа без
потерь. Неявные преобразования
типов данных с потерями запрещены. Так,
например,
логическую переменную, способную
принимать только два
значения (логические 0 и 1), можно присвоить
переменной типа
SINT
(-128…+127), но не наоборот.

При
трансляции программы все подобные
попытки отслеживаются и считаются
грубыми ошибками. Если же это действительно
необходимо,
то выполнить присваивание с потерями
возможно, но
только при помощи специальных операторов.
Операторы преобразования
в МЭК выполняют также и более сложные
операции, например
преобразование числа или календарной
даты в текстовую
строку, и наоборот.

Наибольшее
разнообразие типов данных в стандарте
предусмотрено
для представления целых
чисел.
Смысл
применения широкого
спектра целочисленных переменных
заключается в первую
очередь в оптимизации кода программы.
Скорость вычислений
зависит от того, как микропроцессор
оперирует с переменными
данного типа. Так, вполне очевидно, что
16-разрядный процессор
выполняет сложение двух 16-разрядных
значений одной командой.
Сложение же двух значений 32-разрядных
переменных
— это подпрограмма из нескольких команд.

Дополнительные
задержки могут образовываться за счет
мультиплексирования
шины данных, связывающих процессор и
память,
особенностей микросхем памяти и т. д. В
общем случае, меньшие
по диапазону представляемых значений
типы переменных требуют меньше памяти,
меньше кода, и вычисления с их участи­ем
выполняются значительно быстрее.

Типы
данных МЭК разделяются на две категории
— элементарные и составные. Элементарные
или
базовые
типы
являются
основой для построения составных типов.
К составным
типам
относятся
перечисления, массивы, структуры, массивы
структур
и т. д.

Элементарные
типы данных

Целочисленные
типы

Целочисленные
переменные
отличаются
различным диапазо­ном
сохраняемых данных и, естественно,
различными требованиями
к памяти. Подробно данные характеристики
представлены в следующей
таблице.

Тип

Нижний

предел

Верхний

предел

Размер,

в
байтах

BYTE

8
бит

1

WORD

16
бит

2

DWORD

32
бита

4

LWORD

64
бита

8

SINT

-128

127

1

INT

-32768

32767

2

DINT

-231-1

231-1

4

LINT

-263-1

263-1

8

USINT

0

255

1

Тип

Нижний
предел

Верхний

предел

UINT

0

65535

UDINT

0

232-1

ULINT

0

264-1

Нижний
предел диапазона целых без знака 0,
верхний предел определяется
как (2n)
— 1, где n
— число разрядов числа. Для чисел
со знаком нижний предел —(2n-1),
верхний
предел (2n-1).

Наименования
целых типов данных образуются с
применением
префиксов, выражающих отношение размера
к 16-разрядным словам:
S
(short
*1/2) короткое, D
(double
*2) двойное, L
(long
*4) длинное.
Префикс U
(unsigned)
указывает на представление це­лых
без знака.

Переменные
типов BYTE,
WORD,
DWORD
и
LWORD
определяются
стандартом как битовые строки ANY_BIT.
Говорить
о диапазоне значений чисел для этих
переменных вообще некорректно. Они
представляют строки из 8, 16 и 32 бит,
соответственно.
Помимо обращения с такими переменными
как к единым целым,
их можно использовать побитно.

Целые
числа могут быть представлены в двоичной,
восьмеричной,
десятичной или шестнадцатеричной
системе счисления. Числовые
константы, отличные от десятичных,
требуют указания основания
системы счисления перед знаком «#».
Например:

2#0100_1110

8#116

16#4E78

Для
обозначения шестнадцатеричных цифр от
10 до 15 испо­льзуются
латинские буквы от А до F.
Символ
подчеркивания «_» не влияет на значение
и использу­ется
исключительно для улучшения зрительного
восприятия числа.
Например: 10_000, 16#01_88. Подчеркивание можно
применять
только между цифрами или в конце числа.
Два или более подчеркивания
подряд применять нельзя.

Логический тип

Два значении False
и True.
Начальная инициализация – ложь.

По
определению BOOL

это строка из одного бита, но из соображений
эффективности кода при автоматическом
распределении
памяти транслятором под битовую
переменную выделяется, как
правило, 1 байт памяти целиком. Переменные
типа BOOL,
связанные
с дискретными входами-выходами или
определенные с прямым битовым адресом,
действительно физически представлены
одним битом.

Действительные
типы

Переменные
действительного типа
REAL
представляют действительные
числа в диапазоне ±10±38.
Из 32 бит, занимаемых числом, мантисса
занимает 23 бита. В результате точность
пред­ставления
приблизительно составляет 6 — 7 десятичных
цифр.

Длинный
действительный формат LREAL
занимает
64 бита. Число
содержит 52-битовую мантиссу. Точность
представления приблизительно
составляет 15 — 16 десятичных цифр.
Диапазон чисел
длинного действительного ±10±307.

Числа
с плавающей запятой, записываются в
формате с точкой:
14.0, -120.2, 0.33_ или в экспоненциальной форме:
-1.2Е10, 3.1е7.

Интервал времени

Переменные
типа TIME
используются для выражения интервалов
времени.
В
отличие от времени суток (TIME_OF_DAY)
временной
интервал не ограничен максимальным
значением в 24
часа.

Числа,
выражающие временной интервал, должны
начинаться с
ключевого слова TIME#
или в сокращенной форме Т#. В общем случае
представление времени составляется из
полей дней (d),
ча­сов
(h),
минут (m),
секунд (s)
и миллисекунд (ms).
Порядок представления
должен быть именно такой, хотя ненужные
элементы можно опускать. Для лучшего
зрительного восприятия поля допускается
разделять символом подчеркивания.
Например:

VAR

TIME1:
TIME:=
T#10h_14m_5s;

END_VAR

Старший
элемент может превышать верхнюю границу
диапа­зона представления. Так, если
в представлении присутствуют дни или
часы, то секунды не могут превышать
значения 59. Если секунды
стоят первыми, то их значение может быть
и большим. Смысл
этого правила состоит в том, что если
вы хотите выражать интервал,
например, исключительно в секундах —
пожалуйста. Но
если вы задействуете минуты, то для
единообразия представ­ления, секунды
обязаны соблюдать принятые «правила
субординации».

Младший
элемент можно представить в виде
десятичной дроби:
TIME1:=
T#1.2S; (*равносильно
Т#1s200ms*)

Время суток и
дата

Типы
переменных, выражающие время дня или
дату, пред­ставляются
в соответствии с ISO
8601.

Тип

Короткое
обозначение

Начальное

значение

DATE

D

1
января 1970г.

TIME_OF_DAY

TOD

00:00

DAT
E_AND_TIME

DT

00:00
1 января 1970г.

Дата
записывается в формате «год» — «месяц»
— «число». Время
записывается в формате «часы»: «минуты»:
«секунды».«со­тые».
Дата определяется ключевым словом DATE#
(сокращенно D#),
время дня TIME_OF_DAY#
(сокращенно TOD#),
дата и время
DATE
AND
TIME#
(сокращенно DT#).

DATE#2002-01-31
или
D#2002-01-31 TIME_OF_DAY#16:03:15.47
или
TOD#16:03:15.47

DATE_AND_TIME#2002-01-31-16:03:15.47
или

DT#2002-01-31-16:03:15.47

Все
три типа данных физически занимают 4
байта (DWORD).
Тип
TOD
содержит время суток в миллисекундах
начиная с 0 ча­сов. Типы DATE
и DT
содержат время в секундах начиная с 0
ча­сов
1 января 1970 года.

Строки

Тип
строковых
переменных
STRING
определяет
переменные, содержащие текстовую
информацию. Размер строки задается при
объявлении.
Например, объявление строки strl,
вмещающей до 20
символов, и str2
— до 60 символов:

VAR

str1:
STRING(20); str2:
STRING(60):= ‘Тест’;

END_VAR

Если
начальное значение не задано, то при
инициализации будет
создана пустая строка.

Количество
необходимой памяти определяется заданным
при объявлении
размером строки. Для типа STRING
каждый
символ занимает
1 байт (WSTRING
2
байта). Строковые константы задаются
между одинарных кавычек:

str1
:= ‘Полет нормальный’;

При
необходимости помещения в строку кода,
не имеющего печатного
отображения, используется знак ($) и
следующий за ним
код из двух цифр в шестнадцатеричной
системе счисления. Для
распространенных управляющих терминальных
кодов можно
применить следующие сокращения:

$$

$’

$L
или $l
для LF

$N
для CR

$T
для Tab

Иерархия
элементарных типов

Приведенная
ниже иерархия
элементарных типов
применяет­ся
исключительно для удобства описания
программ. Каждое наи­менование ANY_…
объединяет некоторое множество типов.
Так, при
описании любой битовой операций удобнее
указать, что она применима
для ANY_BIT,
чем перечислять всякий раз допусти­мые
элементарные типы. Применять ANY_
при
объявлении пере­менных,
конечно, нельзя.

ANY

ANY_NUM

ANY_INT

SINT,
INT, DINT, LINT, USINT, UINT,
UDINT, ULINT

ANY_REAL

REAL,
LREAL

ANY_BIT

BOOL,
BYTE, WORD DWORD, LWORD

STRING

TIME

ANY_DATE

DATE, TIME_OF_DAY,

DATE_AND_TIME

Пользовательские
типы данных

Описание
пользовательских
типов данных
(кроме
массивов) должно
выполняться на уровне проекта (в CoDeSys
на вкладке «Типы
данных» — «Организатор Объектов»).
Объявление типа всегда
начинается с ключевого слова TYPE
и
заканчивается строкой END
TYPE.

Массивы

Массивы
представляют
собой множество однотипных элемен­тов
с произвольным доступом. Массивы могут
быть многомерны­ми.
Размерность массива и диапазоны индексов
задаются при объявлении
(см. пример задания трехмерного массива):

<Имя
массива>:
ARRAY

[<li1…lh1>,
<li2…lh2>,
<li3…lh3>]
OF
<mun
элемента>;

где
,
li1,
li2,
li3
указывают нижние пределы индексов; lh1,
lh2
и lh3
— верхние пределы. Индексы должны быть
целого типа и только
положительные, отрицательные индексы
использовать нельзя.

Примеры объявления
массивов:

XYbass: ARRAY
[1..10,1..20] OF
INT;

TxtMsg: ARRAY
[0..10] OF STRING(32);

Massl: ARRAY
[1..6] OF SINT := 1,1,2,2,2,2;

Mass2: ARRAY
[1..6] OF
SINT
:= 1,1,4(2);

Два
нижних примера показывают, как можно
выполнить элементов
массива при объявлении. Оба примера
со­здают
одинаковые массивы. В первом примере
все начальные зна­чения приведены
через запятую. Во втором примере
присутствует сокращение N(a,b,c..),
которое означает — повторить
последовательность а, b,
с.. N раз. Многомерные массивы
инициализируют­ся построчно:

Mass2d:
ARRAY
[1..2,1..4] OF
SINT
:= 1,2,3,4,5,6,7,8;

Для
доступа к элементам массива применяется
следующий синтаксис:

<Имя_массива>[Индекс1,Индекс2,ИндексЗ]

Для
двухмерного массива используются два
индекса. Для одномерного,
очевидно, достаточно одного. Например:

XYbass[2,12]
:= 1;

i
:= STR_TO_INT(TxtMsg[4]);

Если
это не принципиально, используйте в
массивах нумерацию с
0. В этом случае вычисление физического
адреса элемента при ис­полнении
проще. В результате код получается
несколько короче.

Структуры

Структуры
предназначены
для создания новых типов данных на
основе элементов разных базовых типов.
С переменной типа структура
можно обращаться как с единым элементом,
передавать в
качестве параметра, создавать указатели,
копировать и т. д.

В
отличие от массивов структура действительно
вводит новый тип данных. Это означает,
что до применения конкретной пере­менной
нужно выполнить как минимум два
объявления. Сначала нужно
описать структуру. Описание структуры
происходит глоба­льно,
на уровне проекта. Описанная структура
получает иденти­фикатор
(имя
структуры). Но это еще не переменная,
это новый тип
данных. Теперь, используя новый
идентификатор, нужно объявить
одну или сколько угодно переменных,
точно так же, как
и для базовых типов. Только теперь
переменная нового типа получает
«телесную оболочку» или, иными словами,
конкретное место
в памяти данных.

Объявление
структуры
должно
начинаться с ключевого слова STRUCT
и заканчиваться END_STRUCT.
Синтаксис объявления выглядит
так:

TYPE
<Имя_структуры>:
STRUCT

<Объявление
переменной 1>

• • •

<Объявление
переменной п>

END_STRUCT

END_TYPE

Пример
объявления структуры по имени Trolley:

TYPE Trolley: STRUCT

Start: TIME;

Distance: INT;

Load,
On: BOOL;

Articl: STRING(16);

END_STRUCT

END_TYPE

Объявление
в программе переменной Telegal
типа Trolley
и начальная
инициализация структуры выглядит так:

Telegal:
Trolley
:= (Articl:=’Пустой’);

При
начальной инициализации не обязательно
задавать значе­ния для всех элементов.
Элементы, не имеющие явно указанных
начальных
значений, по умолчанию получат нулевые
значения.

Для доступа к
элементам структуры используется
следующий синтаксис:

<Имя__переменной>.<Имя_элемента>

Например:

Telegal.On
:= True;

Структуры
могут включать другие структуры, массивы
и сами образовывать
массивы. Пример объявления и инициализации
массива
структур:

TrolleySet:
ARRAY[0..2]
OF
Trolley
:= (Articl
:= ‘Т1’), (Articl
:= ‘Т2’), (Articl
:= ‘ТЗ’); TrolleySet[i].On
:= TRUE;

Если
структура содержит вложенную структуру,
то доступ к элементам
вложенной структуры осуществляется с
применением составного имени, содержащего
две точки:

train.wagon[5].
weight;
(*wagon[]
вложенный массив структур*)

Поскольку
физический размер элементов структуры
известен транслятору
заранее, обращение к элементу структуры
не дает каких-либо накладных расходов
в сравнении с простой перемен­ной.
Транслятор имеет возможность рассчитать
абсолютные адре-

са
элементов при компиляции. Естественно,
это не относится к массивам структур.
Чтобы не иметь проблем при использовании
нескольких
различных переменных одной структуры,
применять прямые
адреса в структуре нельзя.

Перечисления

Перечисление
позволяет
определить несколько последователь­ных
значений переменной и присвоить им
наименования. Перечисление
— это удобный инструмент, позволяющий
ограничить множество
значений переменной и усилить контроль
при трансляции.
Как и структура, перечисление создает
новый тип дан­ных,
определение которого выполняется на
уровне проекта:

TYPE
<Имя
перечисления>:

(<Элемент
0>, < Элемент 1>, …
<
Элемент
п>);

END_TYPE

Объявленная
позднее переменная типа <Имя
перечисления>
может
принимать только перечисленные значения.
При инициа­лизации переменная получает
первое из списка значение. Если числовые
значения элементов перечисления не
указаны явно, им присваиваются
последовательно возрастающие числа
начиная с 0. Фактически элемент перечисления
— это число типа INT
с огра­ниченным набором значений.
Если необходимо, значения элемен­там
можно присвоить явно при объявлении
типа перечисления. Например:

TYPE
TEMPO:

(Adagio
:= 1,

Andante
:= 2,

Allegro
:= 4);

END_TYPE

Идентификаторы
элементов перечисления используются
в про­грамме
как значения переменной:

VAR

LiftTemp:
TEMPO:= Allegro;

END_VAR

Если
в разные перечисления включены элементы
с одинаковыми именами, возникает
неоднозначность. Для решения этой
проб­лемы
применяется префикс, содержащий
перечисление: TEMPO#Adagio.
В CoDeSys
все наименования элементов перечис­ления
обязаны быть уникальными.

Ограничение
диапазона

Тип
переменных с ограниченным
диапазоном значений
позволяет
определить допустимое множество значений
переменной. Объявление
типа переменной с ограниченным диапазоном
должно происходить
непосредственно между ключевыми словами
TYPE
и
END_TYPE:

TYPE
<Имя>
:

<Целый
тип> (от ..до)

END_TYPE

Например:

TYPE
DAC10:

INT(0..16#3FF);

END_TYPE

Применение
переменной с ограничением диапазона
покажем на
примере:

VAR

dac: DAC10;

END_VAR

dac:= 2000;

При
попытке трансляции данного примера
возникает законная ошибка:

‘Error:
Type mismatch: Cannot convert ‘2000’ to INT(0.
.1023)’.

Псевдонимы типов

Проблема
выбора подходящего типа данных не всегда
решает­ся легко. Допустим, вы работаете
с температурой, замеренной 16-разрядным
АЦП. Может ли быть температура только
выше нуля
или когда-либо потребуется работать в
отрицательной облас­ти,
еще не совсем очевидно. В одном случае
нужно использовать тип
переменных UINT,
а в другом — INT.
Здесь удобно опреде­лить
новый тип данных:

TYPE
TEMPERATURA:

UINT;

END
TYPE

Далее
везде в программе вы используете тип
TEMPERATURA
при
объявлении переменных. Если вдруг
понадобится изменить тип
температуры на INT,
то это легко и быстро можно будет сде­лать
в одном месте.

Аналогичные
псевдонимы
типов
удобно
создавать для лю­бых
часто используемых в программе типов.
Например, для массивов
или других типов, имеющих длинное и
невыразитель­ное
определение.

Переменные

Каждая
переменная
обязательно
имеет наименование и тип. Сущность
переменной может быть различной.
Переменная может представлять
вход или выход ПЛК, данные в оперативной
или энергонезависимой памяти. Далее мы
рассмотрим правила объяв­ления
и некоторые практические сложности и
тонкости, возника­ющие при работе с
переменными.

Идентификаторы

Имя
переменной
(ее
идентификатор) должно быть составлено
из
печатных символов и цифр. Цифру нельзя
ставить на первое место. Пробелы в
наименовании использовать нельзя.
Вместо них
обычно
применяется символ подчеркивания.
Символ подчеркива­ния
является значимым. Так имена ‘Varl’,
‘Var_l’
и ‘_Varl’
яв­ляются
различными. Два подчеркивания подряд
использовать нельзя.
Регистр букв не учитывается. Так ‘VAR1’
и ‘Varl’
одно и то значение.
Как минимум, 6 первых знаков идентификатора
являются значимыми
для всех систем программирования.

Аналогичные
требования относятся и к любым
идентификаторам
МЭК-программ (компоненты, метки, типы и
т. д.).

Распределение
памяти переменных

Контроллер
с точки зрения МЭК программы имеет
несколько областей
памяти, имеющих разное назначение.

  1. Область входов
    ПЛК.

  2. Область выходов
    ПЛК.

  3. Область прямо
    адресуемой памяти.

  4. Оперативная память
    пользователя (ОЗУ).

Аппаратные
ресурсы ПЛК присутствуют в МЭК-проектах
в неявной форме. Размещение переменной
в одной из трех первых об­ластей
приводит к ее связи с определенной
аппаратурой — входа­ми,
выходами или переменными системы
исполнения (диагности­ка
модулей, настройка параметров ядра и
т. д.). Распределение пе­ременных
в этих областях определяется изготовителем
ПЛК. Привязка
к конкретным адресам задается при помощи
прямой адреса­ции.
Для обеспечения переносимости программного
обеспечения прямые
адреса нужно использовать только в
разделе объявлений. В языках программирования
стандарта не предусмотрено опера­ций
прямого чтения входов-выходов. Эту
работу выполняет систе­ма исполнения.
При необходимости для низкоуровневого
обращения
изготовителем ПЛК поставляются
специальные библиотеки.

Объявление
переменной без префикса AT
физически означает выделение
ей определенной памяти в области ОЗУ.
Распределение доступной
памяти ОЗУ транслятор осуществляет
автоматически.

Переменные
принято разделять на глобальные и
локальные по области
видимости. Глобальные
переменные
определяются
на Уровне
ресурсов проекта (VAR_GLOBAL)
и доступны для всех про­граммных
компонентов проекта. Локальные
переменные
описываются
при объявлении компонента и доступны
только внутри него. Описание любого
программного компонента содержит, как
мини­мум, один раздел объявления
локальных переменных VAR,
пере­менных
интерфейса VAR_INPUT,
VAR_OUTPUT,
VAR_IN_OUT
и
внешних глобальных переменных VAR_EXTERNAL
(см.
по­дробнее
«Компоненты организации программ»).

Наименования
разделов объявления переменных могут
содер­жать дополнительные ключевые
слова, уточняющие способ при­менения.

Ключевое
слово

Применение
переменной

RETAIN

Переменные
нужно разместить в энергонезависи­мой
памяти, сохраняющей значения при
выклю­ченном питании. Такая память
не является обяза­тельной и
присутствует далеко не во всех ПЛК

CONSTANT

Константы,
доступные только для чтения

Прямая адресация

Для
создания прямо
адресуемой
переменной
используется сле­дующее
объявление:

имя
переменной
АТ%
прямой
адрес тип;

Прямой адрес
начинается с буквы, определяющей область
па­мяти:

Символ

Область
памяти

I

Область
входов

Q

Область
выходов

M

Прямо
адресуемая память

Далее следует
символ, определяющий тип прямого адреса:

Символ

Область
памяти

нет

Бит

X

Бит

_
_ —

В

Байт

W

Слово

D

Двойное
слово

L

Длинное
слово

Завершает
прямой адрес число — составной
иерархический ад­рес, поля которого
разделены точкой. В простейшем случае
испо­льзуется
два поля адреса: номер элемента и номер
бита.

В конце
объявления, как и для автоматически
размещаемых переменных,
необходимо указать тип переменной. При
указании адреса
одного бита тип переменной может быть
только BOOL.

В
прямом адресе указывается именно номер
элемента. Это ко­ренным
образом отличается от физических адресов
микропроцес­сора.
Если прямой адрес определяет байт, то
номер элемента — это
номер байта. Если прямой адрес определяет
слово, то номер элемента
— это номер слова, и, естественно, один
элемент зани­мает два байта. Так,
следующие два объявления адресуют один
и тот
же байт:

a1 AT%QB5 BYTE;

a2 AT%QW2 WORD;

a1:=5;

a2.0:=8;

В
каждой области памяти адресация элементов
начинается с нуля.
Физическое размещение прямо адресуемых
областей в ОЗУ определяется
конфигурацией контроллера. Очевидно,
что сопо­ставление идентификаторов
переменных прямым адресам является
делом, требующим большой аккуратности.
Поэтому для слож­ных
модульных контроллеров применяются
специальные фирменные
конфигураторы, подключаемые к оболочке
комплекса программирования
и позволяющие графически «собрать» ПЛК
и определить
все необходимые интерфейсные переменные.

Входы
ПЛК — это переменные с прямыми адресами
в области
I.
Они доступны в прикладных программах
только по чтению, Выходы Q
— только по записи. Переменные в области
М доступ­ны
по записи и чтению.

В
области памяти М размещают обычно
переменные, которые нельзя
однозначно отнести к входам или выходам.
Это могут быть
диагностические ресурсы модулей,
параметры системы ис­полнения
и т. д.

Прямые
адреса можно использовать в программах
непосредственно:

IF
%
IW4
> 1
THEN

(*3начение входа
IW4*)

Тем
не менее все же желательно компактно
сосредоточить в проекте
все аппаратно-зависимые моменты.

Обратите
внимание, что прямая адресация позволяет
разместить
несколько разнотипных переменных в
одной и той же памяти.
Например, специально для быстрого
обнуления 16-дискретных выходов
(BOOL)
можно использовать переменную типа
WORD.
Или, например, совместить переменную
STRING
и несколько пе­ременных
типа BYTE,
что даст возможность организовать
форма­тирование
вывода без применения строковых функций.
Посколь­ку
физическое распределение адресов
известно на этапе трансля­ции,
компилятор формирует максимально
компактный код для таких
объединений, чего не удается достичь
при работе с элемен­тами
массива, где требуется динамическая
адресация.

Синтаксис
прямых или МЭК адресов выглядит следующим
образом:
каждый адрес начинается с
символа %,
с последующим префиксом области.
Префикс
I
указывает на область входов, Q
— область выходов и M
— область маркеров.
Далее следует
идентификатор размера: X
или ничего – один бит, B
обозначает байт (8 бит), W
— это 16 разрядное слово и D
— это двойное слово (32 бита). Завершают
адрес цифры, указывающие последовательные
адреса в заданной области, начиная с
0.

Например:
%IW215 — это 216-тое слово
в области входов.
%QX1.1 — это 2-й бит 2-го
слова в области выходов.
%MD48 — это 49-е
двойное слово в области маркеров.

Поразрядная
адресация

В
стандарте предусмотрена удобная форма
работы с отдельны­ми битами переменных
типа битовых строк — поразрядная
адре­
сация.
Необходимый
бит указывается через точку после
иденти­фикатора.
Аналогичным образом можно использовать
отдельные биты прямо адресуемой памяти.
Младшему биту соответствует нулевой
номер. Поразрядная нумерация не должна
превышать гра­ницы
соответствующего типа числа.

VAR

a: WORD;

bStop AT %IX64.3:
BOOL;

END_VAR

a:=0;

a.3:=
1; (*или
а.З := TRUE;
— результат 2#0000_1000*)

a.18:=
TRUE; (*ошибка,
в WORD
не может быть бит а.18*)

IF
a.15
THEN
… (*а
меньше нуля?*)

Преобразования
типов

Преобразование
типов
происходит
при присваивании значе­ния
переменной одного типа переменной
другого типа. Преобра­зование
меняет физическое представление значения
в памяти данных,
но не должно изменять само значение.
Если это невоз­можно,
то преобразование приводит к частичной
потере данных. Но
в таком случае транслятор требует явного
указания необходи­мости
выполнения такой операции.

Рассмотрим
сначала работу с целыми числами. Пусть,
на­пример,
объявлена переменная siVar
типа короткое целое (SINT
8 бит) и переменная iVar
типа целое (INT
16 бит). Допус­тим
siVar
= 100, a
iVar
= 1000. Выражение iVar
:= siVar
являет­ся
вполне допустимым, поскольку числа типа
SINT
являются подмножеством
INT
(iVar
примет значение 100). Здесь преобра­зование
типа будет выполнено транслятором
автоматически, без каких-либо дополнительных
указаний. Обратное присваивание siVar:=
iVar
приведет к переполнению и потере данных.
Заста­вить
транслятор выполнить преобразование
с вероятной поте­рей
данных можно только в явной форме при
помощи специа­льного
оператора siVar
:= INT_TO_SINT(iVar).
Результат равен 24 (в шестнадцатеричной
форме 1000 это 1б#ОЗЕ8 и только младший его
байт перейдет в SINT,
значение 16#Е8 соответст­вует десятичному
числу 24).

Аналогичная
ситуация возникает при работе с
действительны­ми
числами длинного LREAL
и короткого REAL
типов.

Операторы
явного преобразования базовых МЭК-типов
образу-к>т свои наименования из двух
частей. Вначале указывается «ис­ходный
тип», затем «_ТО _» и «тип результата*.
Например:

(*
Результат 16#АА*) (*120*)

i
:= REAL_TO_INT(2.7); (*Результат
3*)

i
:= TRUNC(2.7); (*Результат
2*)

t
:= STRING_TO_TIME(‘T#216ms’); (*РезультатТ#216ms*)

Операция
TRUNC
выполняет отбрасывание дробной части
в отличие
от преобразования REAL__TO_INT,
выполняющего округление.

Обратите
внимание, что операции преобразования
допустимы для
любых комбинаций базовых типов, а не
только для совмести­мых
типов (например, дату в строку). Так,
преобразования <…>_TO_STRING
фактически заменяют оператор PRINT,
распространенный
в языках общего применения.

В
конкретной реализации отдельные
преобразования могут не поддерживаться
или иметь определенные особенности, в
первую очередь это относится к
преобразованиям строк в другие типы и
обратно.
Поэтому мы не будем здесь приводить
подробные описа­ния
всех возможных преобразований. При
необходимости исполь­зуйте
руководство по применению системы
программирования или
оперативную справку.

Венгерская запись

При
наличии строгой типизации данных очень
полезной ока­зывается
возможность узнавать тип переменной
по ее наименова­нию
непосредственно в тексте программ. В
этом случае некоррек­тное
применение переменных бросается в глаза
и позволяет избе­жать
многих сложно локализуемых ошибок.

Для
этого может использоваться специальная
запись имен переменных.
Впервые такая запись имен была предложена
Чарль­зом
Симони (Charles
Simonyi)
и обоснована в его докторской дис­сертации.
Возможно, потому что Симони родился в
Будапеште и образованные
по его системе наименования причудливы
(на пер­вый
взгляд), как венгерский язык, за его
методикой записи за­крепилось
название «венгерская запись». В настоящее
время Си­мони
является ведущим инженером Microsoft,
а венгерская за­пись
стала общепризнанной при программировании
под Windows.

Идея
венгерской записи заключается в
прибавлении к идентификаторам коротких
префиксов, определяющих тип и некоторые
другие
важные характеристики переменной.
Префиксы принято записывать
строчными буквами, а имя переменной с
заглавной буквы.
Поскольку венгерская запись «работает»
для любых типи­зированных языков,
имеет смысл применить ее и при
программи­ровании
ПЛК.

Для
базовых типов МЭК можно предложить
следующие пре­фиксы
типов.

Префикс

Тип

b

BOOL

by

BYTE,
USINT

si

SINT

w

WORD,
UINT

i

INT

dw

DWORD,
UDINT

di

DINT

r

REAL

Ir

LREAL

st

STRING

t

TIME

td

TIME_OF_DAY

d

DATE

dt

DATE_AND_TIME

Примеры обозначений:

bStop: BOOL;

bySet: BYTE;

wSize UINT;

«Венгерские»
имена сами говорят о корректности их
примене­ния.
Очевидно, следующее выражение является
бессмысленным; bStop
:= wSize
* 2; а выражение bStop
:= wSize
> 2; вполне допус­тимым.

Уточнить
назначение переменной можно добавлением
еще од­ного
символа перед префиксом типа:

Префикс

Назначение

переменной

а

Составной
тип,

массив

п

Индекс

с

Счетчик

Для
временных переменных можно вообще не
утруждать се­бя
придумыванием имен, а использовать
только префиксы. На­пример:

aiSample:
ARRAY
[1..32]
OF
INT;
ci: INT;

FOR ci := 1 TO 32 DO

(*без
комментариев*)

siSample[ci]
:= -1; END_FOR

К
сожалению, некоторые из предложенных
префиксов совпа­дают
с зарезервированными словами (BY,
AT,
D,
DT,
N,
ST).
При использовании
их в качестве временных переменных вы
можете добавить
порядковый номер или букву алфавита.
Например:

ЬуА,
ЬуВ, byl,
Ьу2: BYTE;

Структуры
и функциональные блоки образуют имена
экземп­ляров
с включением полного или сокращенного
наименования типа.
Например, tpUpDelay:
TP;

Символ
подчеркивания удобно использовать для
индикаций способа
обращения к переменной. Подчеркивание
в начале имени указывает
— только чтение. Идентификаторы
переменных, соответствующих
входами ПЛК, начинаются символом
подчеркива­ния.
Подчеркивание в конце имени указывает
— только запись. Идентификаторы выходов
заканчиваются символом подчеркива­ния.
Например, Jylnpl,
byOut2_.

Если
система обозначений хорошо продумана,
то ее примене­ние
не вызывает сложности. Единый подход к
наименованию очень
здорово облегчает чтение программы и
позволяет отказать­ся от излишних
комментариев. Уникальные префиксы
удобны не только
для базовых типов, но и для широко
используемых в про­екте собственных
типов данных и функциональных блоков.
Стан­дарт
МЭК не содержит рекомендаций по
составлению имен пере­менных
и компонентов программы. Никакого
стандартного набо­ра
префиксов венгерской записи также нет.
Вы можете использо­вать
вышеописанную систему или разработать
свою собственную. Главное,
чтобы принятая система была понятна
всем программи­стам
— участникам проекта.

Описанные
правила образования венгерских имен
применяют­ся в приведенных ниже
примерах. В простых случаях, когда тип
переменной
не имеет значения или очевиден, венгерская
запись не
используется.

Формат
BCD

Двоично-кодированный
десятичный формат представления BCD
(binary
coded
decimal)
представляет собой числа в позицион­ной
десятичной системе, где каждая цифра
числа занимает 4 бита.
Например, десятичное число 81 будет
представлено в виде 2#1000_0001.
Арифметические
операции с BCD-числами
требуют применения специального
математического аппарата, малоэффек­тивны
в сравнении с обычным двоичным
представлением. Но, с другой
стороны, BCD
оказывается очень удобным при организа­ции
клавиатурного ввода и индикации.
Например, функции вы­вода
числа на принтер или даже на сегментный
индикатор полу­чаются тривиальными
(одна одномерная таблица на 10 констант).

Для
хранения чисел в формате BCD
стандарт МЭК предлагает использовать
переменные типов ANY_BIT
(кроме BOOL,
конеч­но).
Арифметика BCD-вычислений
обычно не поддерживается в стандартном
комплекте библиотек систем программирования
ПЛК.
В библиотеке утилит CoDeSys
реализованы две простые Функции
BCD
преобразования: BCD_TO_INT
и INT_TO_BCD.

К.Р.3

  • #2

В каком из стандартов С++ нашел тип DWORD ?

  • #3

word это условное название 16 битной переменной, обозначающая машинное слово в 16 битном программировании. по мере развития появились 32 битные программы, и придумали двойное слово — DWORD — переменная размером в 32 бита это синоним синонимы нужны для определённой независимости. только ты решаешь какого размера будет твой синоним и какого типа

  • #4

Раньше в C/C++ не было целочисленных типов фиксированного размера (таких как uint32_t, например), а у обычных типов размер не определён — один и тот же тип в разных компиляторах может быть и 16, и 32, и 64-битным. Эта неопределённость приводит к проблемам при использовании функций из внешних библиотек (например: если функция возвращает 32-битное значение, а программа сохранит его в 16-битную переменную, то половина его разрядов будут потеряны). Поэтому в заголовочных файлах Windows и были объявлены такие типы как DWORD, которые всегда имеют один и тот же размер (что достигается использованием директив условной компиляции и макросов, подставляющих разные определения для этих типов).

  • #5

Тип DWORD нужен для создания переменных или указателей типа DWORD

Sarkas


  • #6

долго думал как объяснить по понятнее, загуглил и вот что нашел. UINT – 32-битное беззнаковое целое. Аналоги: unsigned long int, DWORD. Короче DWORD это тоже самое что и в с++ long int. Это просто тип данных как и int, как int a = 555; так и dword a = 555; вот и все только я точно не знаю туда можно и цифры и буквы записывать или только цифры, но думаю что только цифры)

Понравилась статья? Поделить с друзьями:
  • Word drink in past
  • Word documents open как
  • Word documents open python
  • Word documents on web
  • Word documents on i cloud