Dos fn 2dh установить время dos


Функция 0ah: Ввод строки с клавиатуры в буфер

DS:DX = адрес входного буфера (смотри ниже)

Выход Буфер содержит ввод, заканчивающийся символом CR (ASCII 0dh)

Описание: При обращении буфер по адресу DS:DX должен содержать значение максимально допустимой длины ввода. На выходе функции в следующем байте содержится действительная длина ввода, затем введенный текст, завершающийся символом возврата каретки (0dh). Символы считываются с устройства стандартного ввода вплоть до CR (ASCII 0dh) или до достижения длины MAX-1. Если достигнут MAX-1, включается консольный звонок для каждого очередного символа, пока не будет введен возврат каретки CR (нажатие Enter). Второй байт буфера заполняется действительной длиной введенной строки, не считая завершающего CR. Последний символ в буфере — всегда CR (не засчитан в байте длины). Символы в буфере (включая LEN) в момент вызова используются как «шаблон». В процессе ввода действительны обычные клавиши редактирования: Esc выдает «\» и начинает с начала, F3 выдает буфер до конца шаблона, F5 выдает «@» и сохраняет текущую строку как шаблон, и т. д. Большинство расширенных кодов ASCII игнорируются. При распознавании Ctrl-Break выполняется прерывание int 23h (буфер остается неизменным).

Завершение программы

Функция 4ch: Завершить программу (EXIT)

Вход AH = 4ch

AL = код возврата

Описание: Возвращает управление от порожденного процесса его родителю, устанавливая код возврата, который можно опросить функцией 4dh WAIT. Управление передается по адресу завершения в PSP завершаемой программы. В векторах Ctrl-Break и Critical Error восстанавливаются старые значения, сохраненные в родительском PSP.

Замечание: Значение ERRORLEVEL (используемое в пакетных файлах DOS) можно использовать для проверки кода возврата самой последней программы.

Функция 4dh: Дать код возврата программы (WAIT)

Вход AH = 4dh

Выход AL = код возврата последнего завершившегося процесса

AH = 0 — нормальное завершение

АН = 1 — завершение через Ctrl-Break int 23h

АН = 2 — завершение по критической ошибке устройства int 24h

АН = 3 — завершение через функцию 31h KEEP

Описание: Возвращает код возврата последнего из завершившихся процессов. Эта функция возвращает правильную информацию только однажды для каждого завершившегося процесса.

Функция 4dh: Дать код возврата программы (WAIT)

Вход AH = 4dh

Выход AL = код возврата последнего завершившегося процесса

AH = 0 — нормальное завершение

АН = 1 — завершение через Ctrl-Break int 23h

АН = 2 — завершение по критической ошибке устройства int 24h

АН = 3 — завершение через функцию 31h KEEP

Описание: Возвращает код возврата последнего из завершившихся процессов. Эта функция возвращает правильную информацию только однажды для каждого завершившегося процесса.

Работа с файлами

Функция 3ch: Создать файл через дескриптор

Вход AH = 3ch

DS:DX = адрес строки ASCIIZ с именем файла

CX = атрибут файла

Выход AX = код ошибки, если CF установлен

АХ = дескриптор файла, если ошибки нет

Описание: DS:DX указывает на строку ASCIIZ в формате: «d:\путь\имяфайла»,0. Если диск и/или путь опущены, они принимаются по умолчанию. файл создается в указанном (или текущем) каталоге файл открывается в режиме доступа чтение/запись вы должны сохранить дескриптор (handle) для последующих операций, если файл уже существует:

· при открытии файл усекается до нулевой длины

· если атрибут файла — только чтение, открытие отвергается (атрибут можно изменить функцией 43h Изменить Атрибут)

CONFIG.SYS определяет число доступных дескрипторов в системе

Используйте функцию 5bh Создать Новый Файл, если вы не хотите

испортить существующий файл.

Функция 5bh: Создать новый файл

Вход AH = 5bh DOS 3.0+

DS:DX = адрес строки ASCIIZ с именем файла

CX = атрибут файла

Выход AX = код ошибки, если CF установлен

АХ = дескриптор файла, если ошибок нет

Описание: DS:DX указывает на строку ASCIIZ в форме: «d:\путь\имя_файла»,0. Если диск и/или путь опущены, они принимаются по умолчанию. Этот вызов идентичен функции DOS 3ch CREATE, с тем исключением, что он вернет ошибку, если файл с заданным именем уже существует. Файл открывается для чтения/записи в совместимом Режиме Доступа.

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

Общие условия выбора системы дренажа: Система дренажа выбирается в зависимости от характера защищаемого.

Механическое удерживание земляных масс: Механическое удерживание земляных масс на склоне обеспечивают контрфорсными сооружениями различных конструкций.

Dos fn 2dh: установить время dos

, Fn+ё, — там изображена кнопка play/pause] — функциональная клавиша мультимедийного софта. Запускает и приостанавливает воспроизведение медиаплеера (если поддерживается ОС и если корректно настроено) Работает и в Windows 7 и в Ubuntu 16.04 (доп. драйверов не требует)
Fn+F1 — включение/отключение тачпада. Работает и в Windows 7 и в Ubuntu 16.04 (доп. драйверов не требует)
Fn+F2 — включение/отключение основного экрана ноутбука Работает и в Windows 7 и в Ubuntu 16.04 (доп. драйверов не требует)
Fn+F3 — выключить звук в ОС Работает и в Windows 7 и в Ubuntu 16.04 (доп. драйверов не требует)
Fn+F4 — регулировка подсветки клавиатуры (6 уровней яркости клавиш + полное отключение подсветки, каждое нажатие комбинации Fn+F4 выполняет переключение яркости на следующий режим). Работает нативно, в т.ч. под DOS.
Fn+F5 — уменьшить громкость в ОС Работает и в Windows 7 и в Ubuntu 16.04 (доп. драйверов не требует)
Fn+F6 — увеличить громкость в ОС Работает и в Windows 7 и в Ubuntu 16.04 (доп. драйверов не требует)
Fn+F7 — переключить вывод с основного экрана ноутбука на HDMI (возможны затыки — мною не проверялось, т.к. не на чём пока проверить)
Fn+F8 — уменьшение яркости основного экрана ноутбука Работает и в Windows 7 и в Ubuntu 16.04 (требуется только установка драйверов от производителя, в случае с Linux — см. ниже по шапке темы в разделе про поддерживаемые ОС)
Fn+F9 — увеличение яркости основного экрана ноутбука Работает и в Windows 7 и в Ubuntu 16.04 (требуется только установка драйверов от производителя, в случае с Linux — см. ниже по шапке темы в разделе про поддерживаемые ОС)
Fn+F10 — включение/выключение (аппаратное) встроенной веб-камеры Работает и в Windows 7 и в Ubuntu 16.04 (доп. драйверов не требует). В Windows7 камера пропадает в диспетчере устройств, в Ubuntu — перестает обнаруживаться в системе, будто выдернули из USB-порта
Fn+F11 — режим «полет» Нет эффекта от нажатия в Windows 7 и в Ubuntu 16.04 (требуется «допиливание», установка софта от производителя)
Fn+F12 — режим гибернации (ноутбук уходит в спящий режим). Работает и в Windows 7 и в Ubuntu 16.04 (доп. драйверов не требует)
Fn+Ins = ScrollLock (аналогичная клавиша есть на полноразмерной клавиатуре полноразмерного ПК) Работает и в Windows 7 и в Ubuntu 16.04 (доп. драйверов не требует)
Fn+PgUp= Pause (аналогичная клавиша есть на полноразмерной клавиатуре полноразмерного ПК). Архаизм. В некоторых играх — постановка на паузу игрового процесса. Работает нативно. Pause+[windows_logo]=диалог свойств о системе в Windows, к примеру. Работает и в Windows 7 и в Ubuntu 16.04 (доп. драйверов не требует)
Fn+PgDn = Break (аналогичная клавиша есть на полноразмерной клавиатуре полноразмерного ПК). Архаизм. Полезна для консольных приложений — посылает прерывание текущему процессу. Реагировать или нет — зависит уже от процесса. Работает и в Windows 7 и в Ubuntu 16.04 (доп. драйверов не требует)
По поводу хоткеев в Windows 7. Порылся на просторах интернета, и нарыл софтину стороннего производителя (305,7 МБ!), которая привела к работоспособности всех комбинаций с Fn и даже к их корректной индикации.

DOSBox инструкция: как запустить игру!

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

DOSBox: как пользоваться?

Чтобы запустить игру с помощью DOSBox для Windows 7, 8, 10 или XP:

  1. Скачать бесплатно DOSBox под свою операционную систему на официальном сайте;
  2. Установить DOSBox. Появится ярлык на рабочем столе;
  3. Скачать игру, если этого еще не сделали;
  4. Распаковать загруженный архив, получится много файлов. Важно: игры под DOS запускаются файлами с расширением *.bat, *.exe, или *.com, в каждой игре по разному, запускайте по очереди, советую начать с файла в имени которого присутствует название игры, например doom.exe;
  5. Зайти в папку с игрой и перетащить файл запуска игры прямо на ярлык DOSBox на рабочем столе Windows. Как это показано на рисунке.

Все. Игра запустилась и работает без проблем.

Если вдруг игра не запускается: проверьте файлы setup.exe или install.com — в играх под DOS звук настраивался вручную, в то время звуковые карты небыли совместимыми, нужно запустить один из этих файлов, выбрать звуковую карту, например «sound blaster», и сохранить настройки.

Список системных кнопок DOSBox:

ALT-ENTER — Переход dosbox на весь экран

CTRL-F1 — Переназначение кнопок

CTRL-F5 — Сделать скриншот

CTRL-F6 — Старт/стоп записи звука в wave файл

CTRL-ALT-F5 — Старт/Стоп создания ролика с экрана (захват видео и запись в avi)

CTRL-F9 — Закрыть DosBox

CTRL-F10 — Захватить/Отпустить мышь

CTRL-F11 — Замедлить скорость игры

CTRL-F12 — Увеличить скорость игры

При нажатии ALT-F12 (Убрать ограничение скорости (турбо кнопка)) игра ускоряется до предела. Ускорение идет пока комбинация нажата. Бывает очень полезно для промотки заставок, ускорения ожидания, даже если игрой это не предусмотрено. К сожалению комбинация не очень стабильна. Самый частый симптом при таком ускорении — исчезновение в дальнейшем звука.

Как установить (или исправить) часовой пояс и DST в c (функции time.h) и DOS


У меня есть компьютер с DOS (freeDos 1.1) и простой C (скомпилированный с Borland 5.0) с time.h Когда я устанавливаю время компьютера с помощью BIOS или команд DATE и TIME DOS, информация о часовом поясе отсутствует. Я назначил время моему текущему времени 10:25.

Моя программа C делает это.

когда я запускаю код, я получаю правильное текущее время, но с часовым поясом «EST» в конце, как называется форматом %Z

Пнд 2020-03-13 10:25:36 EST

Возвращаемое значение time_t из time(NULL) равно 1489418736.
Это математически ломается до 47 лет, 83 дня, 15 часов, 25 минут, 36 секунд
Ясно, что time.h реализует некоторую информацию о часовом поясе здесь, добавляя 5 часов к моему текущему времени.

Перемещение на javascript, который получает значение time_t

Пн Мар 13 2020 08:25:36 GMT-0700 (Тихоокеанское дневное время)

Кажется, есть некоторая комбинация часового пояса (EST vs PST vs GMT) и летнее время (которое может объяснять дополнительный час?) При игре здесь. Как я могу повлиять на настройки часового пояса и DST для машины, операционной системы или представления библиотеки C, чтобы получить наиболее полезное представление времени, будь то локальное или GMT?

Это было много лет, но я думаю, что есть переменная окружения DOS, которую C ищет для определения часового пояса. C может по умолчанию использовать EST, если он не находит переменную окружения. Я не могу говорить о javascript аспектах вопроса.

Что касается проблем дневного света, код будет иметь проблемы. В США «мое текущее время 10:25 должно было быть» Mon 2020-03-13 10:25:36 EDT «(EDT против EST). Причудливый код TurboC, безусловно, будет иметь проблемы с разработанными правилами DST, которые за последние десятилетия.

IMO, любое использование полей _daylight localtime() , mktime() , struct tm.isdst и _daylight приведет к неудовлетворительным результатам, касающимся летнего времени и, вероятно, будет бесконечным источником проблем с struct tm.isdst _daylight . Используйте либо современный компилятор/библиотеку, либо импортируйте/катите свои собственные (ugh) часовые пояса/дневные процедуры.

Альтернатива, придерживаться time_t и универсального времени — нет локального времени

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

Когда компиляторы C начали писать для соответствия стандартному API C, возникла проблема с реализацией gmtime в DOS. Пользователи DOS устанавливают на своем ПК местное время, а не UTC. К сожалению, вы не можете надежно преобразовать местное время в UTC из-за проблем летнего времени (вы можете надежно преобразовать UTC в любое местное время), но разработчики хотели найти решение. Они решили игнорировать проблемы, которые могут возникнуть при преобразовании локального времени в UTC, и позволили вам установить переменную среды TZ , чтобы указать преобразование из локального времени в UTC.

В отсутствие установленного TZ библиотеки по умолчанию имеют часовой пояс, который часто бывает EST, как в случае с компиляторами Borland. Вот почему вы видите, что EST возвращается как часовой пояс localtime .

Перед запуском приложения (желательно установить его в autoexec.bat при загрузке) вы можете использовать команду set TZ= , чтобы указать преобразование из локального времени в UTC. Хорошую статью о синтаксисе переменной TZ можно найти здесь:

TZN — это трехбуквенный идентификатор часового пояса для стандартного локального времени, а DZN — это трехбуквенный идентификатор для летнего времени. TZN и DZN используются для отображения. Если DZN опущен, то этот флажок означает, что переход на летнее время не относится к вашему часовому поясу. Наиболее важной частью является смещение (+ / -), которое определяет количество времени, которое нужно добавить или вычесть из стандартного локального времени для преобразования его в UTC.

Например, у меня стандартное время в горах (MST) и применяется летнее время (MDT). MST отстает от UTC на 7 часов, поэтому к моему стандартному местному времени нужно добавить 7 часов, чтобы получить UTC. Я бы установил часовой пояс следующим образом:

Если бы я был в Ньюфаундленде, где стандартное время (NST) отстает от UTC на 3,5 часа и действует летнее время, я мог бы использовать:

Если бы я был в Саскачеване, Канада, где используется центральное стандартное время (CST на 6 часов меньше UTC), когда переход на летнее время не применяется, я бы использовал:

Моя рекомендация — использовать UTC в DOS

Я лично обнаружил, что во встроенных системах, где ОС не знает часовых поясов, проще установить системные часы на UTC, а не на местное время. Затем вы пишете программное обеспечение для преобразования UTC в местное время, основываясь на конфигурации программного обеспечения, если это необходимо. Это имеет то преимущество, что вы можете написать правила для летнего времени. Правила для DST, встроенные в библиотеки Borland, больше не применяются поколением позже. Использование UTC также полезно для отметки времени в журналах событий, поскольку вы можете указывать однозначные значения времени. В этой ситуации я использую:

который по местному времени фактически совпадает с UTC.

DST Различия между продуктами

В то время, когда было разработано большинство продуктов Borland, правило, применяемое для DST, было для рынка США. С 1987 по 2006 год летнее время проходило с первого воскресенья апреля до последнего воскресенья октября. После 2006 года он был изменен в США на второе воскресенье марта и первое воскресенье ноября.

Причина, по которой Borland C++ 5 (выпуски выпущены в период с 1996 по 1997 годы), показала EST (а не EDT), потому что с ее точки зрения летнее время в 2020 году не началось до воскресенья 2 апреля 2020 года (первое воскресенье апреля), поэтому оно все еще Считается, что понедельник 13 марта 2020 года по-прежнему на стандартном времени. Среда, в которой работал ваш Javascript, вероятно, знает (основываясь на современных базовых базах данных и правилах часовых поясов), что в 2020 году летнее время вступило в силу 12 марта 2020 года (второе воскресенье марта), поэтому в понедельник 13 марта 2020 года было показано летнее время. (ТИХООКЕАНСКОЕ ЛЕТНЕЕ ВРЕМЯ). Я предполагаю, что было использовано тихоокеанское время, потому что системе, работающей с Javascript, было сказано, что вы находитесь в тихоокеанском часовом поясе, который наблюдает летнее время.

Dos fn 2dh: установить время dos

Начиная с IBM AT, персональные компьютеры содержат два устройства для управления процессами — часы реального времени (RTC) и собственно системный таймер. Часы реального времени получают питание от аккумулятора на материнской плате и работают даже тогда, когда компьютер выключен. Это устройство можно использовать для определения/установки текущих даты и времени, установки будильника с целью выполнения каких-либо действий и для вызова прерывания IRQ8 (INT 4Ah) каждую миллисекунду. Системный таймер используется одновременно для управления контроллером прямого доступа к памяти, для управления динамиком и как генератор импульсов, вызывающий прерывание IRQ0 (INT 8h) 18,2 раза в секунду. Таймер предоставляет богатые возможности для препрограммирования на уровне портов ввода-вывода, но на уровне DOS и BIOS и часы реального времени, и системный таймер используются только как средство определения/установки текущего времени и организации задержек.

Функция DOS 2Ah — Определить дату

Ввод: AX = 2Ah
Вывод: СХ = год (1980 – 2099)
DH = месяц
DL = день
AL = день недели (0 — воскресенье, 1 — понедельник. )

Функция DOS 2Ch — Определить время

Ввод: AX = 2Ch
Вывод: СН = час
CL = минута
DH = секунда
DL = сотая доля секунды

Эта функция использует системный таймер, так что время изменяется только 18,2 раза в секунду и число в DL увеличивается сразу на 5 или 6.

Функция DOS 2Bh — Установить дату

Ввод: АН = 2Bh
СХ = год (1980 – 2099)
DH = месяц
DL = день
Вывод: АН = FFh, если введена несуществующая дата,
АН = 00h, если дата установлена
Илон Маск рекомендует:  transition-duration в CSS

Функция DOS 2Dh — Установить время

Ввод: АН = 2Dh
СН = час
CL = минута
DH = секунда
DL = сотая доля секунды
Вывод: AL = FFh, если введено несуществующее время,
AL = 00h, если время установлено

Функции 2Bh и 2Dh устанавливают одновременно как внутренние часы DOS, которые управляются системным таймером и обновляются 18,2 раза в секунду, так и часы реального времени. BIOS позволяет управлять часами напрямую:

INT 1Ah АН = 04h — Определить дату RTC

Ввод: АН = 04h
Вывод: CF = 0, если дата прочитана
СХ = год (в формате BCD, то есть 1998h для 1998-го года)
DH = месяц (в формате BCD)
DL = день (в формате BCD)
CF = 1, если часы не работают или попытка чтения пришлась на момент обновления

INT 1Ah АН = 02h — Определить время RTC

Ввод: АН = 02h
Вывод: CF = 0, если время прочитано
СН = час (в формате BCD)
CL = минута (в формате BCD)
DH = секунда (в формате BCD)
DL = 01h, если действует летнее время, 00h, если нет
CF = 1, если часы не работают или попытка чтения пришлась на момент обновления

INT 1Ah АН = 05h — Установить дату RTC

Ввод: АН = 05h
СХ = год (в формате BCD)
DH = месяц
DL = день

INT 1Ah АН = 03h — Установить время RTC

Ввод: АН = 03h
СН = час (в формате BCD)
CL = минута (в формате BCD)
DH = секунда (в формате BCD)
DL = 01h, если используется летнее время, 0 — если нет

Кроме того, BIOS позволяет использовать RTC для организации будильников и задержек:

INT 1Ah АН = 06h — Установить будильник

Ввод: АН = 06h
СН = час (BCD)
CL = минута (BCD)
DH = секунда (BCD)
Вывод: CF = 1, если произошла ошибка (будильник уже установлен или прерывание вызвано в момент обновления часов)
CF = 0, если будильник установлен

Теперь каждые 24 часа, когда время совпадет с заданным, часы реального времени вызовут прерывание IRQ8 (INT 4Ah), которое должна обрабатывать установившая будильник программа. Если при вызове СН = FFh, CL*nbsp;= FFh, a DH = 00h, то будильник начнет срабатывать раз в минуту.

INT 1Ah АН = 07 — Отменить будильник

Ввод: АН = 07h

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

BIOS отслеживает каждый отсчет системного таймера с помощью своего обработчика прерывания IRQ0 (INT 8h) и увеличивает на 1 значение 32-битного счетчика, который располагается в памяти по адресу 0000h:046Ch, причем при переполнении этого счетчика байт по адресу 0000h:0470h увеличивается на 1.

INT 1Ah АН = 00h — Считать значение счетчика времени

Ввод: АН = 00h
Вывод: CX:DX = значение счетчика
AL = байт переполнения счетчика

INT 1Ah АН = 01h — Изменить значение счетчика времени

Ввод: АН = 01h
CX:DX = значение счетчика

Программа может считывать значение этого счетчика в цикле (через прерывание или просто командой MOV) и организовывать задержки, например пока счетчик не увеличится на 1. Но так как этот счетчик использует системный таймер, минимальная задержка будет равна приблизительно 55 микросекундам. Частоту таймера можно изменить, программируя его на уровне портов, но BIOS предоставляет для этого специальные функции.

INT 15h АН = 86h — Формирование задержки

Ввод: АН = 86h
CX:DX = длительность задержки в микросекундах (миллионных долях секунды!)
Вывод: AL = маска, записанная обработчиком в регистр управления прерываниями
CF = 0, если задержка выполнена
CF = 1, если таймер был занят

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

INT 15h АН = 83h — Запуск счетчика времени

Ввод: АН = 83h
AL = 0 — запустить счетчик
AL = 1 — прервать счетчик
CX:DX = длительность задержки в микросекундах
ES:BX = адрес байта, старший бит которого по окончании работы счетчика будет установлен в 1
Вывод: AL = маска, записанная обработчиком в регистр управления прерываниями
CF = 0, если задержка выполнена
CF = 1, если таймер был занят

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

Аналоговые часы

Стандартные графические режимы SVGA могут быть 4-, 8-, 15-, 16-, 24- и 32-битными.

4-битные режимы (16 цветов):

012h: 640×480 (64 Кб)

102h: 800×600 (256 Кб)


104h: 1024×768 (192 Кб)

106h: 1280×1024 (768 Кб)

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

Видеоадаптер может поддерживать и собственные нестандартные видеорежимы. Список их номеров можно получить, вызвав подфункцию 00h, а получить информацию о режиме по его номеру — вызвав подфункцию 01h видеофуикции 4Fh. Более того, для стандартных режимов также следует вызывать подфункцию 01h, чтобы проверить реальную доступность режима (например, режим может быть в списке, но не поддерживаться из-за нехватки памяти). VBE 2.0 разрешает видеоадаптерам не поддерживать никаких стандартных режимов вообще.

INT 10h АН = 4Fh, AL = 00 — Получить общую SVGA-информацию

AX = 4F00h
ES:DI = адрес буфера (512 байт)

AL = 4Fh, если функция поддерживается
АН = 01, если произошла ошибка
АН = 00, если данные получены и записаны в буфер

1.3 Часы реального времени и системный таймер

Начиная с IBM AT, персональные компьютеры содержат два устройства для управления процессами — часы реального времени (RTC) и собственно системный таймер. Часы реального времени получают питание от аккумулятора на материнской плате и работают даже тогда, когда компьютер выключен. Это устройство можно использовать для определения/установки текущих даты и времени, установки будильника с целью выполнения каких-либо действий и для вызова прерывания IRQ8 (INT 4Ah) каждую миллисекунду. Системный таймер используется одновременно для управления контроллером прямого доступа к памяти, для управления динамиком и как генератор импульсов, вызывающий прерывание IRQ0 (INT 8h) 18,2 раза в секунду. Таймер предоставляет богатые возможности для препрограммирования на уровне портов ввода-вывода, но на уровне DOS и BIOS и часы реального времени, и системный таймер используются только как средство определения/установки текущего времени и организации задержек.

Функция DOS 2Ah — Определить дату

СХ = год (1980 – 2099)
DH = месяц
DL = день
AL = день недели (0 — воскресенье, 1 — понедельник. )

Функция DOS 2Ch — Определить время

СН = час
CL = минута
DH = секунда
DL = сотая доля секунды

Эта функция использует системный таймер, так что время изменяется только 18,2 раза в секунду и число в DL увеличивается сразу на 5 или 6.

Функция DOS 2Bh — Установить дату

АН = 2Bh
СХ = год (1980 – 2099)
DH = месяц
DL = день

АН = FFh, если введена несуществующая дата,
АН = 00h, если дата установлена

Функция DOS 2Dh — Установить время

АН = 2Dh
СН = час
CL = минута
DH = секунда
DL = сотая доля секунды

AL = FFh, если введено несуществующее время,
AL = 00h, если время установлено

Функции 2Bh и 2Dh устанавливают одновременно как внутренние часы DOS, которые управляются системным таймером и обновляются 18,2 раза в секунду, так и часы реального времени. BIOS позволяет управлять часами напрямую:

INT 1Ah АН = 04h — Определить дату RTC

CF = 0, если дата прочитана
СХ = год (в формате BCD, то есть 1998h для 1998-го года)
DH = месяц (в формате BCD)
DL = день (в формате BCD)
CF = 1, если часы не работают или попытка чтения пришлась на момент обновления

INT 1Ah АН = 02h — Определить время RTC

CF = 0, если время прочитано
СН = час (в формате BCD)
CL = минута (в формате BCD)
DH = секунда (в формате BCD)
DL = 01h, если действует летнее время, 00h, если нет
CF = 1, если часы не работают или попытка чтения пришлась на момент обновления

INT 1Ah АН = 05h — Установить дату RTC

АН = 05h
СХ = год (в формате BCD)
DH = месяц
DL = день

INT 1Ah АН = 03h — Установить время RTC

АН = 03h
СН = час (в формате BCD)
CL = минута (в формате BCD)
DH = секунда (в формате BCD)
DL = 01h, если используется летнее время, 0 — если нет

Описание логической структуры

2.1 Алгоритм программы

  1. Очистка экрана
    1. Вывод текущего времени в виде часов, оснащенных окружностью и тремя стрелками (часовой, минутной, секундной).
  1. Выход в ОС осуществляется при нажатии клавиши ESC.

Сохранение исходного видеорежима

Установка нового видеорежима

Отрисовка текущего времени

Проверка нажатия клавиши Esc

2.2 Структура программы с описанием основных процедур

    1. ClrScr — Очистка экрана
    2. PressAnyKey — Ожидание нажатия клавиши
    3. GoToCursor — Переход в нужную позицию экрана
    4. PutPixel – Рисует окружность
    5. OutCros — Затирает старые стрелки и отображает на новом месте
    6. CurUgols — Устанавливает углы для стрелок в зависимости от текущего времени
    7. GradToRad — Переводит радианы в градусы
    8. SinCos — Вычисляет синус и косинус
    9. Plot — Отображает точку на экране
    10. Line — Отображает линию по начальной точке, углу и радиусу
    11. Time — Загружает текущие время в поля данных

3.Инструкция пользователю

3.1 Запуск программы

Запуск программы осуществляетс я путем вводу в командной строке (Пуск / Программы / Стандартные / Командная строка):

3.2 Порядок работы

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

При нажатии клавиши ESC произойдет выход из программы.


Рис.1 Отображение аналоговых часов

Итак, подводим вывод о проделанной работе. Мы реализовали на языке ассемблер программу «Аналоговые часы» в том представление, каком мы себе представляли вначале. Проделав такую работу мы не только узнали, что такое язык низкого уровня, но и познакомились с ним на практике. Этот процесс расширил наш кругозор, а так же усовершенствовал наши навыки в работе с системами счисления. Благодаря языку ассемблер мы улучшили свои знания по программированию, которые ранее были приобретены в процессе изучения более высокого уровня языка.

Этот язык заинтересовал многих учащихся и не думаю, что хоть кто-нибудь из тех, кто изучал этот язык остался равнодушным к нему.

Список использованной литературы

  1. В. Юров, С. Хорошенко, “Ассемблер. Учебный курс.”, С.-П. “Питер”, 1999г.
  2. Зубков, “Язык ассем блера для DOS, WINDOWS, UNIX”, М. “ДМК”, 2000г.
  3. П.В.Беспалов С.В. Горин С.М. Коновалов, «Программирование на языке Ассемблер», М. “Высшая школа”, 1993г.
  4. П.Нортон , «Персональный компьютер фирмы IBM и операционная система M-DOS», М. “Радио и связь”, 1991г.
  5. Питер Абель ,«Язык Ассемблера для IBM PC и программирования», М. “Высшая школа”, 1992г.
  6. П.Нортон , «Язык ассемблера для IBM PC», М. “Радио и связь”, 1990г.

Системный таймер

Начиная с IBM AT, персональные компьютеры содержат два устройства для управления процессами — часы реального времени (RTC) и собственно системный таймер. Часы реального времени получают питание от аккумулятора на материнской плате и работают даже тогда, когда компьютер выключен. Это устройство можно использовать для определения/установки текущих даты и времени, установки будильника с целью выполнения каких-либо действий и для вызова прерывания IRQ8 (INT 4Ah) каждую миллисекунду. Системный таймер используется одновременно для управления контроллером прямого доступа к памяти, для управления динамиком и как генератор импульсов, вызывающий прерывание IRQ0 (INT 8h) 18,2 раза в секунду. Таймер предоставляет богатые возможности для препрограммирования на уровне портов ввода-вывода, но на уровне DOS и BIOS и часы реального времени, и системный таймер используются только как средство определения/установки текущего времени и организации задержек.

Функция DOS 2Ah — Определить дату

AX = 2Ah

Вывод:

СХ = год (1980 – 2099)
DH = месяц
DL = день
AL = день недели (0 — воскресенье, 1 — понедельник. )

Функция DOS 2Ch — Определить время

AX = 2Ch

Вывод:

СН = час
CL = минута
DH = секунда
DL = сотая доля секунды

Эта функция использует системный таймер, так что время изменяется только 18,2 раза в секунду и число в DL увеличивается сразу на 5 или 6.

Функция DOS 2Bh — Установить дату

АН = 2Bh
СХ = год (1980 – 2099)
DH = месяц
DL = день

Вывод:

АН = FFh, если введена несуществующая дата,
АН = 00h, если дата установлена

Функция DOS 2Dh — Установить время

АН = 2Dh
СН = час
CL = минута
DH = секунда
DL = сотая доля секунды

Вывод:

AL = FFh, если введено несуществующее время,
AL = 00h, если время установлено

Функции 2Bh и 2Dh устанавливают одновременно как внутренние часы DOS, которые управляются системным таймером и обновляются 18,2 раза в секунду, так и часы реального времени. BIOS позволяет управлять часами напрямую:

Библиотека Интернет Индустрии I2R.ru

Малобюджетные сайты.

Продвижение веб-сайта.

Контент и авторское право.

Программирование на Ассемблере под DOS

2.1. Проба молотка

#1. Теоретически гвозди можно забивать и голыми руками. Но намного быстрее и безболезненнее делать это с помощью молотка. Пользоваться им, как известно, каждый дурак умеет. Чего там сложного? Взял оный в руки — и молоти: раз по гвоздю, два раза по пальцам (понимание приходит с опытом). Молотки бывают разные: большие и маленькие, с длинной ручкой и с короткой ручкой, железные и деревянные, приспособленные для забивания гвоздей и приспособленные для пробивания черепов. Да и гвоздей разнообразие еще то! И разной длины, и разной толщины, и со шляпками разной формы, из разного материала сделанные. есть даже специализированные для прибивания рук к кресту! :(.
Так вот: виды молотков и особенности их использования мы пока что оставим в покое. Для начала просто возьмем гвоздь, который уже не раз забивали голыми руками (больно было?) и забьем его при помощи молотка! Помните, как мы программу, выводящую окошки, без компилятора одними мнемониками писали?
А теперь ее же, дуру, «под компилятор» перелопатим. В общем, настало время, братья, тайну нервную и страшную узнать. Сегодня мы познакомимся с компилятором.

#2. Нижеследующий текст набираем в текстовом редакторе:

Сохраняем получившуюся дуру под именем proga_1.asm.
Далее запускаем файл tasm.exe и в качестве параметра передаем ему имя файла с исходным текстом программы. То есть командная строка у вас (в том же Windows Commander’е) вот какая должна быть:

Если вы правильно набрали текст программы, то TASM должен выплюнуть вам вот что:

Самое тут главное — это чтоб «Error messages» был «None». Это значит, что ошибок в программе нет.
Поехали дальше. Если ошибок у вас действительно никаких не было, то в том же каталоге, что и proga_1.asm ищите файл proga_1.OBJ. Можете даже по F3 попробовать просмотреть его содержимое. Что-нибудь поняли? Если нет — отсылаю вас к «Введению в машинный код».
А теперь запускаем хорошую программу TLINK следующим образом:

Обратите внимание: линкуем мы именно файл с типом OBJ, а не ASM.
Что получилось? А получился файл proga_1.COM!! И этот .COM работает!
Посмотрите на его содержимое в DZEBUG’е :). Отличется ли оно чем от той проги, что мы делали ранее?
Нет, не отличается!
Медитируем.

#3. А теперь несколько слов о том, почему не стоит писать программы так, как мы это делали раньше:
1. Эти проклятые адреса! Пойди рассчитай на какой адрес прыгнуть нужно, с какого смещения подпрограмма начинается, с какого блок данных. В принципе, как мы уже убедились, в этом нет ничего сложного. просто занятие это очень уж муторное и неинтересное.
Как абсолютно верно заметил некто Евгений Олейников в RTFM_Helpers: «Когда я пишу программу, я не знаю точного адреса начала будущей подпрограммы».
2. Очень сложно было в том случае, когда возникала необходимость ВСТАВИТЬ ту или иную команду в середину кода. Приходилось перебивать код по новой. по новой пересчитывать адреса. ужас, в общем!
Так вот: основная и самая главная функция «ассемблерного компилятора» — это как раз и есть «просчитывание адресов»!
Смотрите, как хорошо и приятно: Мы готовим исходный текст в обыкновенном текстовом редакторе :). Просто набиваем строчка за строчкой — нам не важны адреса (мы их вообще не видим!), не важны «точки входа» в процедуру. Спокойненько вставляем какую надо команду, спокойненько удаляем. Без лишнего напряга!
Да вы посмотрите на блок 3 программы :). Там все те же хорошо знакомые команды :).
Особое внимание обратите на команду CALL, которая у нас, как известно, вызывает процедуру. После нее идет не привычный адрес начала процедуры, а всего лишь ее «имя собственное»! А сама процедура находится между строчками WINDOW proс (начало процедуры) и WINDOW endp (конец процедуры).
«WINDOW» — понятно, это «имя собственное». «Proc» — потому что процедура. «Endp» — потому что конец процедуры.
Тут еще один момент. подобные «словеса» КОМАНДАМИ АССЕМБЛЕРА НЕ ЯВЛЯЮТСЯ. Они называются инече — «директивами». Это не ПРИКАЗ делать то-то и то-то, а ЦЕННОЕ УКАЗАНИЕ компилятору (не процессору!), что и как ему делать с данным куском кода.
Процедуры — вещь хорошая! Все процедуры хороши, и в большой программе их чертовски много! Это вам не языки высокого уровня, где можно длиннющие простыни кода лабать и все будет работать! («Дельфи-компилятор не даст вам выстрелить себе в ногу, но будет так удивлен попыткой сделать это, что через некоторое время сделает это сам.» (С) DZ WhiteUnicorn).
Это ASM! Тут запутаться легче простого! Исходники неимоверно длинны и сложны! Все делается ручками (хоть и с помощью компилятора), а ошибки довольно трудно отслеживаются (для не-дZенствующих это вообще занятие безнадежное)! Вот и выкручиваются низкоуровневые программеры таким вот образом: всю программу (даже в тех случаях, когда это не обоснованно!) делят на меленькие кусочки-процедурки. Напишут процедурку, протестируют ее так и сяк. если работает — за следующую берутся.
Сии «методы проектирования» мы еще с вами еще не раз рассмотрим :). Пока что знайте вот что: любую прогу можно/нужно рассматривать как КУЧУ ПРОЦЕДУР, которые все между собой повязаны.
Но есть среди этих процедур САМАЯ ГЛАВНАЯ! Это та, С КОТОРОЙ НАЧИНАЕТСЯ ВЫПОЛЕНИЕ ПРОГРАММЫ! Ее никто не вызывает. Она — босс! Она всеми командует, все гребет под себя. Описывают ее те же самые директивы, что и прочие «подчиненные процедуры». Но есть у нас еще одна директивка, которая указывает, КАКАЯ ИМЕННО процедура ИЗ ВСЕХ вроде бы «равноправных» является ГЛАВНОЙ.
Видите строчку end MAIN в конце исходного текста программы? Именно она и указывает ГЛАВНУЮ ПРОЦЕДУРУ («MAIN» — ее имя собственное). Если бы мы написали end WINDOW, то выполнение программы у нас началось бы с первой строчки процедуры WINDOW, и ни одна строчка из MAIN выполнена не была бы.
Уф. в общем долго и упорно медитируйте.

Илон Маск рекомендует:  Что такое код pg_untrace

#4. Наличие многочисленных директив — это своего рода плата за то, что компилятор избавляет нас от необходимости просчитывать адреса. Как говорит дZенская программерская мудрость «любишь кататься, люби и саночки возить».
Как и в DZEBUG’е, «в TASM’e» мы также должны четко инструктировать компилятор (дабы он в свою очередь также четко проинструктировал процессор) что у нас является кодом, а что — данными.
Посмотрите на исходник. Весь текст программы у нас хранится между директивами CODESG segment и CODESG ends. Где CODESG — это «имя собственное», «segment» — потому что «CODESG» он, собственно, и есть сегмент :), и «ends» — потому что конец сегмента. (сравните с процедурными директивами).
Но тут такой вопрос:
Директива ASSUME производит связывание сегментного решистра CS с «именем собственным».
Далее у нас следует директива org 100h. Нужна она нам для того, чтобы компилятор понял, что мы хотим получить именно COM-файл, который, как известно, помещается в сегмент памяти начиная со смещения 100 (в общем-то, это необходимое, но вовсе не достаточное условие для получения COM-файл). Директивка очень интересная, о ней мы, надеюсь, еще поговорим подробнее, когда коснемся вирмейкерства.

#5. Ладно. с директивами более-менее разобрались, исходник приготовили, пора разбираться че там дальше происходит.
Дальше происходит так называемое «ассемблирование», т.е. перевод команд в соответствующие машинные коды. При этом просчитываются адреса меток, адреса начала подпрограмм, адреса начала/конца сегментов. и многое другое.
Причем ассемблирование происходит как минимум в два приема. Посудите сами: откуда компилятору знать, с какого адреса начнется процедура WINDOW, если не известно, какая еще простыня команд ПОСЛЕ этого CALL’а будет? В DZEBUG’е мы это «в уме на листике» считали.
TASM это тоже аналогичным образом делает :).
При первом проходе он подсчитывает, сколько какая команда занимает места, с каких адресов начинаются процедуры и т. д. и только при втором проходе подставляет в call’ы КОНКРЕТНЫЕ АДРЕСА начала этих процедур. всего лишь. ну еще и ваши «d» и «b» в машинные «h» (которые на самом деле «b») переводит. (во завернул!).
А вообще TASM много еще чего делает. программеры народ ленивый.
В результате ассемблирования мы получаем так называемый «объектный файл».
«И что это за дрянь?» — спросте вы и правильно спросите.
А вы сравните шестнадцатеричное содержимое OBJ и COM файлов. В OBJ присутствует та же последовательность байтов, что и в OBJ. Но помимо этого и еще какая-то шестнадцатеричная ерунда присутствует: имя сассемблированного файла, версия ассемблера, «имя собственное» сегмента и т. д.
Это своего рода «служебная» информация, предназначенная для тех случаев, когда ваш исполнимый файл вы хотите собрать из нескольких. При разработке больших приложений исходный текст, как правило, хранится в нескольких файлах в каждом из них прописаны свои сегменты кода/данных/стека. А исполнимый получить нам нужно только один — с единым сегментом кода/данных/стека. Именно это TLINK и делает: завершает определение адресных ссылок и объединяет, если это требуется, несколько программных модулей в один.
И этот один у нас и является исполнимым. УРА!

#6. Итак, первую прогу с использованием компилятора мы написали. Теперь напишем вторую. Набиваем ее исходный текст, и компилим:

Итак, в этой программе мы использовали функцию 13h прерывания 10h (INT 10h, AH=13h).
Вот ее описание:

[INT 10h, ФУНКЦИЯ 13h] — записывает на экран символьную строку, начиная от указанной позиции.
ВХОДНЫЕ ПАРАМЕТРЫ:
AH = 13h;
AL — код формата(0-3):
AL=0, формат строки <симв., симв. симв.>и курсор не перемещается,
AL=1, формат строки <симв., симв. симв.>и курсор перемещается,
AL=2, формат строки <симв., атр. симв., атр.>и курсор не перемещается,
AL=3, формат строки <симв., атр. симв., атр.>и курсор перемещается;
BH — страница дисплея;
BL — атрибут (для режимов AL=0, AL=1);
CX — длина строки;
DX — позиция курсора для записи строки;
ES:BP — указатель строки.
ВЫХОДНЫЕ ПАРАМЕТРЫ: отсутствуют.

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

2.2. Несколько «тупых» процедурок

#1. Дабы сходу «обломать» нездоровую критику, сразу предупреждаем, что нами сознательно допущена некоторая излишняя «процедуризация» исходного кода. А по сему, прежде чем взяться за изучение нижеследующего материала, твердо уясните: в данном случае (как есть сейчас) дробление кода на процедуры — дурь полная. А обосновываем мы эту дурь только тем, что впоследствии будем совершенствовать эти процедуры до уровня, когда, собственно, сие «дробление» и будет обосновано.
И еще. Подобный модульный подход (это понятие немножко шире, чем просто «использование процедур») в некоторых случаях снижает быстродействие и увеличивает размер исполнимого файла, однако он же и значительно увеличивает «читабельность» исходника, что вполне обоснованно с точки зрения обучения. И с точки зрения «командной» разработки программ — тоже. (В общем, критика по этому поводу не принимается!).
Итак, для начала набиваем вот какой текст (исходник это!):

Здесь вам все должно быть понятно. INT 20h вынесен в отдельную процедуру и только. Плюс еще какие-то нездоровые заголовки добавлены, которые и весят-то больше, чем сам код. Это нормально. После точки с запятой в исходнике вы можете писать все, что угодно. Все равно при компиляции это будет проигнорированно и, следовательно, на размер исполнимого файла не повлияет. (Точно так, как и длина «имен собственных» процедур и меток).
Мы предлагаем использовать именно такую «шапку» комментария к каждой из ваших процедур. Все очень просто. Первая строчка — это «что делает процедура». Вторая — какие ей необходимо передать параметры. Третья — какие она возвращает параметры. Четвертая и пятая, соответственно, — какие в процедуре использовались прерывания и хм. ранее написанные процедуры :). Это намного облегчит понимание вашего исходника как вами самими, так и теми, кому вы его предоставите на поругание.
Для тех, кто в танке: последующие процедуры вставляйте между процедурами TESTING и EXIT_COM — не ошибетесь :-p.

#2. Следующая процедура также основана на одном-единственном прерывании. Все, что она делает — это возвращает текущие координаты курсора.

Прежде всего обратите внимание на цепочку push’ей и pop’ов (далее — просто «поп»), на очередность записи в стек (AX, BX, CX) и извлечения (CX, BX, AX — обратное то есть). Все регистры, которые мы собираемся изменять внутри процедуры, должны обязательно сохраняться в ее начале и восстанавливаться в ее конце. В важности соблюдения этого правила вы еще не раз убедитесь на своем горьком опыте. Те, кто медитировал над заданием из главы 1.10 #4, уже знают, о чем тут идет речь (а кто не пытался — самое время!).
Давайте посмотрим на нашу процедуру с точки зрения пушей и поп. AH мы изменяли для указания функции прерывания, которую мы хотим использовать. BH обнуляли для указания видеостраницы (пока будем только одну-единственную, нулевую, юзать). «А CX зачем» — спросите. — «Вроде мы его не трогали. «. И точно, мы — не трогали. А посмотрите в описании, что в этот регистр нам засунуло прерывание в «результате» своего выполнения. Посмотрели? Оно вам надо?? То, что нам надо — координаты — засунуты в DX (DH, DL), поэтому их значения мы не сохраняем. Если бы нам нужно было получить информацию о типе курсора — мы бы запушили DX, а CX бы оставили в покое.
Что? Без пива не разобраться?
Так в чем, черт подери, дело? Разбирайтесь под пиво!!
Вот вам еще одна аналогичная процедура, которая не определяет, а УСТАНАВЛИВАЕТ курсор в заданные коодинаты.

Если кто чего не понимает — смотрите комментарии к предыдущей процедуре (+ описание прерываний и команд! это обязательно!). Если кто не понял и предыдущую — снова отсылаю к главе 1.10

#3. На основе двух предыдущих процедур мы напишем третью «курсорную» :), которая будет сдвигать курсор на одну позицию вправо.

А здесь очень простая идеология :)). Из двух процедур мы собрали третью :)). Вызвали CURSOR_READ, получили в DX текущие координаты курсора. Ту координату, что столбец (DL, младшая часть DX), увеличили на единицу. А потом вызвали процедуру CURSOR_SET, которая у нас устанавливает координаты курсора. Новые координаты в нее передаются опять таки через тот же DX. Улавливаете?
Естественно, мы запросто можем отказаться от процедур CURSOR_SET и CURSOR_READ и решить данную задачу внутри одной процедуры. В общем, свой выбор вы сделаете сами. Страшна Сцилла оптимизации по быстродействию, еще страшнее — Харибда оптимизации по размеру, но тварь самая страшная — это Программер, который оптимизирует свой код по собственной «удобноваримости». (Хм. интересно, что бы сказали по этому поводу программеры Мелкософта. )
Обратите также внимание, на push/pop DX внутри процедуры. Мы просто сдвигаем курсор вправо. ПРОСТО СДВИГАЕМ на одну позицию. То, что в DX нам надо? Сто лет оно нам не надо. херим. А остальные регистры — еще процедурами CURSOR_SET и CURSOR_READ неоднократно «похерены». В каком смысле «похерены»?? А в таком, что состояние регистров ПОСЛЕ вызова CURSOR_RIGHT в точности такое же, как и было ДО. Хотя сами помните, что всю четверку регистров мы еще как юзали.
Вот теперь можно сделать паузу и (это мериканцы пускай свой пластмассовый твикс кушают) ПОМЕДИТИРОВАТЬ.

#4. Следующая процедура ну вааще элементарна:

Она символ на монитор выводит. Через 9-ю функцию 10-го прерывания. А потом (после вывода) курсор на позицию вправо перемещает. Догадайтесь сами, «путем вызова» какой процедуры. Ага, правильно :)), CURSOR_RIGHT.
Посмотрите на описание этого прерывания. Код символа должен быть в AL. А у нас в комментариях он в DL прописан. А перед INT 10h mov AL,DL нездоровый стоит. Нахрена он тут?? И правильно!! Этот mov можно удалить, и передавать значение через AL. Но я тварь вредная. Привык я, понимаете-ли, через DX передавать. привычка — сила страшная!! Лень с ней бороться. Лень, а поэтому и не буду. Кто-то в подобном мове может и более глубокий смысл найдет — наверняка найдет!! В общем — ищите сами. На блюдечке с голубой каемочкой вам это не преподнесу :)). Вредный.
mov BL,00000111b (не 07h) написано специально. Это чтоб вы посмотрели, как кодируется атрибут (фон, цвет) энтого символа. В одном из предыдущих номеров даже табличка есть, из справочника содранная.

#5. В языке «командного интерпретатора DOS» есть хорошая команда — CLS (то бишь очистка дисплея). Хорошая команда! Кто не поленится заглянуть, внутрь command.com’а, увидят приблизительно следующее (на самом деле все чуть-чуть навороченнее, но прерывание то же):

Короче, элементарный скроллинг, только заданы максимально возможные координаты скроллируемого окошка и CX=0. в общем, окошки рисовали, помните.

#6. Тестируем, штоль?? Дописываем процедуру TESTING:

Компилим. CLS тож тестируем. Все работает??

А теперь самое интересное :)) и благоприятно влияющее на нервную систему :). Прелесть модульного подхода вот в чем: написали процедуру, протестировали успешно — и МОЖЕТЕ ЗАБЫТЬ нафиг, как она у вас работает, какие функции там используются, какие хитроПОПые алгоритмы там применены.
Просто смотрите на заголовок, че она делает, чего ей надобно на входе, чего возвращает. а ее «внутренности» вам глубоко фиолетовы. Работает — и ладно. Ааааaaa?? Круто?
Уф. медитируйте!!

2.3. Печать «шестнадцатеричных циферек»

#1. Мы уже неоднократно юзали хорошую мнемоническую (aka ассемблерную) команду ADD :). Напомню, что в результате выполнения команд

в регистр AX у нас помещалась сумма (AX=AX+BX).

Мы смотрели на это дело под отладчиком, и, к своей неописуемой радости, убеждались в том, что эта дрянь действительно работает. Но толку нам знать, что она работает?? Программа ведь не только работать должна, но еще и диалог какой-нить между юзверем и компутером обеспечивать! Например, спрашивать у него эти два числа и выплевавать на монитор результат их сложения.
Вот именно — выводить на монитор, а не заносить в какой-то абстрактный регистр.
С клавиатурным вводом пока обождем, а вот с выводом (на монитор) разберемся прямо сейчас.

Как мы это сделаем? Вы уже неоднократно слышали, что «в ассемблере» все делается «ручками» :). Сейчас вы лишний раз убедитесь в том (некоторые замрут в ужасе), что это утверждение истинно. Для вывода значения регистра мы вовсе не «познакомимся с новым прерыванием». Даже такая простейшая операция, как «вывод на дисплей значения регистра (переменной)» — это целая процедура. И не одна, как вы скоро в этом убедитесь. Страшно?

#2.Задача: вывести на монитор значение регистра DL.
Народ! Давайте сразу расставим границы между КОДОМ СИМВОЛА и его НАЧЕРТАНИЕМ.
Например, у нас F3h в DL. Как мы хотим это вывести? Как символы ‘F3’ или же как ASCII символ, соответствующий коду F3h? Определяемся. Если в DL у нас F3h — то надо чтоб именно ‘F3’ у нас на монитор и выводилась. Не ‘э перевернутое’, не ’46 33′, а именно ‘F3’. Помедитируйте. Уловите разницу между ‘э’, ’46 33′ и ‘F3’.

Эту задачу мы немножко упростим :). Для начала напишем процедуру, которая выводит только младшую тетраду регистра DL (цифру «3» в нашем примере). Для этого мы обратимся к процедуре WRITE_CHAR из прошлого номера. Именно она печатает нам на монитор символ, ASCII-код которого находится в DL.
Но тут загвоздка: в DL-код, а печатается-то символ :). А нам, собственно, именно две циферки шестнадцатеричного кода, как два символа, и нужно напечатать. Ну, или хотя бы младшую циферку этого кода.
Решается эта задача элементарно :). Главное — это правильно ее сформулировать!
Вот что я тут «нарисовал»:

Только во второй колонке вместо вопросительных знаков должны быть соответствующие всякие символы (посмотрите в ASCII-таблице, какие они на вид страшные!).

Процедурка наша вот что должна делать — Всего-навсего перевести (конвертировать) тетраду в код соответствующего ей символа. Завернуто? Если разобраться, то не очень-то и завернуто.
Смотрите: в DL у нас 03h. Хотим мы эту ‘3’ на монитор вывести. Если вызовем WRITE_CHAR, то у нас символ «сердечко» выплюнется. А надо, чтоб символ ‘3’ вывелся, код которого 33h.
Соответственно и для остальных смотри по табличке.

А теперь обратите внимание, насколько «шестнадцатеричная циферка» (тетрада) отличается от ASCII-кода, этой «циферке» соответствующего. Сам скажу: на 30h для цифр от ‘1’ до ‘9’, и на 37h для цифр от ‘A’ до ‘F’. То есть «переконвертацию» мы запросто можем сделать командами add DL,30h (если тетрада в диапазоне 0. 9) и add DL,37h (если тетрада в диапазоне A. F).


Короче, вот код (пропиваю!):

Сначала, ессно, изменяемые регистры сохраняем (мы ж их изменяем!). (Ну, и восстанавливаем в конце процедуры (PUSH и POP соответственно)).
Потом у нас логическое ветвление организовано. Сравниваем значение DL с «общей границей» наших двух диапазонов (команда — CMP, «граница» — 0Ah). Если это значение больше или равно 0Ah, то прыжок на метку HEX_LETTER, прибавление к DL 37h и печать цифры (WRITE_CHAR). Иначе добавляем 30h и безо всяких условий перепрыгиваем на вызов WRITE_CHAR (минуя add DL,37h то бишь).
Все. Тестируем.

#3. Про тестирование — базар отдельный. В данном случае мы можем запросто проверить нашу процедуру на абсолютно всех возможных значениях этой тетрады (всего-то ничего 16 вариантов). Но намного правильнее, проанализировав алгоритм, установить своего рода «критические» значения, на которых целесообразно проводить проверку. Плюс, естественно, минимальное и максимальное значения.

Если сия «тестовая» (она же — главная) процедура выведет на монитор

значит мы с высокой долей вероятности можем быть уверенными, что процедура WRITE_HEX_DIGIT работает правильно на всех значениях младшей тетрады DL.
Кто не просто скопировал процедуру из буфера обмена, а действительно разобрался с тем, как она работает — сами знают, что значение старшей тетрады нашей процедуре НЕбезразлично. Оно должно быть равным 0.

#4. Что мы имеем? Процедуру для вывода на дисплей одной шестнадцатеричной циферки — младшей тетрады (в DL). Но нам-то нужно две вывести! Сначала старшую циферку-тетраду, и только потом — младшую!
Таким образом очередная задача разбивается на две части: печать старшей тетрады DL и печать младшей тетрады DL.
Первая «подзадача» решается легко: нужно просто старшую тетраду переместить на место младшей и вызывать процедуру (основательно протестированную и 99,9%-но работающую) WRITE_HEX_DIGIT.
А вторая подзадача — хм. заключается в восстановлении предыдущего (мы ж тетраду перенесли) значения DL и снова — вызове WRITE_HEX_DIGIT.
(Хе! Вот теперь-то вы уж точно почувствуете всю прелесть «дробления кода на процедуры»!)

Перенос тетрады мы осуществим при помощи команды SHR, которую в умных книжках обзывают как «логический сдвига разрядов операнда вправо». Объясню.
Представьте себе деревянную доску, длинной в 8 бутылок пива и шириной в одну. (В принципе, доску эту можно и в два раза длиннее представить, но тогда на ней надо «DX» написать, мы же пока только «DL» напишем). А еще дурня, у которого на лбу SHR написано. Так вот, если этому придурку стукнуть по хребту, то он слева от доски поставит ПУСТУЮ бутылку, а остальные сдвинет на одну позицию вправо, в результате чего самая правая бутылка, ессно, с доски упадет.
Бутылки, которые сразу стояли, могут быть пустыми или полными, а вот дурень SHR — только пустые ставит. И только слева.

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

В DL — наша цепочка битов. (11110011b = F3h, естественно).
В CL заносим «на сколько позиций» нам нашу цепочку сдвинуть.
Ну и SHR — это дурень, который сдвигает вправо, а слева нули дописывает.

#5. Думаете, это все?? Разогнались!! Не все так просто :).
WRITE_HEX_DIGIT у нас требует, чтобы первой тетрадой были только одни нули. Я заострял на этом ваше внимание.
При печати первой тетрады это условие соблюдается. SHR слева нули дописывает.
А вот при печати второй цифры нужно вот что: ничего никуда не сдвигая, обнулить старшую тетраду, а младшую (которая, собственно, и есть «цифра») оставить в покое.
Решим мы эту команду при помощи логической операции «и» (and по-аглицкому).

А теперь и для особо одаренных:

Смотрите, интересно как получается:
Если мы AND чего-либо (нуля или единички) с 0 делаем, то у нас в результате 0 и только 0 получается.
А если AND с единичкой — то ЧТО БЫЛО, ТО И ОСТАЕТСЯ.
(Это и есть потаенный дZенский смысл команды AND)

Решение нашей проблемы (обнулить старшую тетраду, а младшую оставить без изменений) таким образом сводится к тому, что старшую тетраду нужно «AND 0», а младшую — «AND 1».
То есть значению DL с 00001111b (оно же — 0Fh) «AND» сделать.

На ассемблере это вот как выглядеть будет:

Естественно, 00001111b = 0Fh

#6. Уфф. Вот что получиться в итоге должно:

Ну че тут объяснять?? Я уже объяснил все!! Единственное, что могу добавить — это про mov DH,DL. Этой командой мы значение регистра копируем перед тем как биты «ему» сдвинуть. А потом статус-кво mov DL,DH восстанавливаем, чтоб и младшую цифру напечатать.
Все. Тестируем. Вроде должно работать.

#7. Так сказать «к вопросу о шаблонах мышления».
Мы тут доооолго трахались с тетрадами. Вроде успешно.
Когда при тестировании понимаемости материала мы предложили пяти «подопытным» самостоятельно написать процедуру для вывода на монитор «большого» регистра (DX), они все как один начали сдвигать байты. :(
Народ!! Это не есть правильно!!

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

#8. Ну, и напоследок, — информация к размышлению:

Команду shr можно использовать для деления целочисленных операндов без знака на степени 2 :)).

Думаете эти «фокусники» который в уме офигенны уравнения считать умеют, шибко умные?? Нифига!! Они просто люди ЗНАЮЩИЕ. А делить на десять и вы умеете.
Над этим я настоятельно рекомендую дооооолго помедитировать. А еще над тем, что девушки весьма и весьма любят, когда им фокусы показывают. Впрочем, это (вычисления в уме) — тема отдельная. Мы ее тоже когда-нить коснемся :). MATRIX MUST DIE!!

2.4. Печать десятичных циферек

#1. Для тех, кто медитировал над главой 1.1 алгоритм должен быть понятен как 2+2=(вписать желаемое). Кто не въедит в алгоритм WRITE_DECIMAL — научитесь сначала переводить числа между радиксами на листике в клеточку.
Итак, ставим новую задачу. В DX у нас шестнадцатеричное число. Нам нужно напечатать его на монитор в «десятичном формате». Еще раз обращаю ваше внимание на то, что существует множество способов ее решения. Мы же выбрали способ, который:
а). Вам легче всего будет понять.
б). Требует минимального количества «новых» команд.
Кто скажет, что мы не правы — пусть первый кинет камень в эхо-конференцию RTFM_Helpers.

#2. Прежде всего посмотрите на процедуру WRITE_HEX_DIGIT и вспомните, какой алгоритм положен в основу ее работы. Вспомнили? Рад за вас!!
А теперь мы познакомимся к командой деления. Что будем делить?? Ну естественно, «регистры» :).

Новая команда называется «деление беззнаковое» (DIVide unsigned).
Что такое делимое/делитель/частное, я вас грузить не буду, это в учебнике арифметики для младших классов более чем понятно расжевано. Ну во всяком случае детишки это понимают.
(Кто скажет, что 10/3=3 а остаток 333 в периоде — тот дурак. Кто скажет, что остаток будет равен 1, скажет правильно.)

Следующий кусок кода демонстрирует деление значения регистра AX на значение регистра BL.

Обратите внимание: ДЕЛИМОЕ у нас может располагаться только в регистре AX (другими словами, делимое задается неявно), а ДЕЛИТЕЛЬ — в любом регистре.
Кто не верит — посмотрите под отладчиком.

1. Если делитель размером в байт, то после операции частное помещается в AL, а остаток — в AH.
2. Если делитель размером в слово, то делимое должно быть расположено в паре регистров DX:AX (младшая часть делимого в AX). После операции частное помещается в AX, а остаток — в DX.
2. Если делитель размером в двойное слово, то делимое должно быть расположено в паре регистров EDX:EAX (младшая часть делимого находится в EAX.) После операции частное помещается в EAX, а остаток — в EDX.

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

у вас вовсе не AX на BX делиться будет, а парочка DX:AX на BX.
Помедитируйте над этим :))

#3. А теперь, собственно, пропиваю саму процедуру вместа с традиционным расжевыванием оной:

Алгоритм простой: пока частное не равно 0, делим его, делим и еще раз делим на 10d, запихивая остатки в стек. Потом — извлекаем из стека. Вот и вся «конвертация» из HEX в BIN :). Это если в двух словах.
А если подробно, то вот что получается:
Бряк 1 — подготавливаем делимое. Как уже говорилось, оно у нас задается неявно — обязательно через AX. А параметр у нас — через DX процедуре передается. Вот и перемещаем.
Бряк 2 — это, собственно, делитель.
Бряк 3 — очищаем CX. Он у нас будет в качестве счетчика. О нем мы еще поговорим.
Бряк 4 — очищаем DX. Если не очистим, то мы не 1234h какое-нить на 10 делить будем, а 12341234h. Первое 1234 нам надо? Вот и я говорю — очищаем!
Бряк 5 — делим! Частное — в AX, остаток — в DX.
Бряк 6 — заносим остаток (DX) в стек ;).
Бряк 7 — CX=CX+1. Это мы считаем сколько раз «щемили остаток», который «щемится», по кругу (прыжок на метку non_zero), пока AX не равно 0 (бряк 8). То есть делим, делим AX, пока он не окажется, что делить, собственно, нечего.
Так-с. Деление закончено, число раз, которое мы поделили AX до его полного обнуления, хранится в CX.
Дальше все просто. Нам нужно такое же количество раз (CX) извлечь значение (DX) из стека. И это будет «HEX», переведенный в DEC. (Оно же: число в двоично коде, разобранное на последовательность десятичных цифр).
Помните, как у нас организуется цикл? Через loop и CX?
Если бы в качестве счетчика мы использовали какой-нибудь другой регистр, то пришлось бы извращаться со всякими метками и прыжками. а так все просто, все продуманно :). Цикл, в теле которого ИЗВЛЕЧЬ ЦИФЕРКУ (бряк 9) и НАПЕЧАТАТЬ ЦИФЕРКУ (бряк 10). Столько же раз, сколько мы и делили наше исходное шестнадцатеричное число.

Для тех, кто не понял: бряк — это брекпоинт. Для тех, кто еще не знает, что такое брекпоинт — ищите объяснение в ‘DZebug. Руководство юZверя’

Двое из десяти «подопытных» чайников (есть такие) возмутились:
В DX же только четыре циферки влазят!
Ага! Аж два раза четыре!

Как видите, туда еще и две буковки «влазят» ;)
А то и все четыре, если кавычки считать.

2.5. Hello, world! или Изврат-2

#1. Мы уже писали программу «Hello, World!» с использованием 13-й функции 10 прерывания. Посмотрите на ее исходник.
Сегодня мы слабаем еще одно «Hello, World!», но уже несолько другим способом. Какой из этих способов более дZенский — решайте сами ;).
Для начала мы создадим блок данных после всех-всех-всех процедур но между директивами начала и конца сегмента.
Блок данных будет выглядеть следующим образом:

Вы должны спросить «А почему мы хотим напечатать Hello, World-2, а в конце строки у нас 2$? $ — это что? World за баксы продавать, штоль?? Да ну вас.
Эту строчку мы будем выводить на монитор особым дZенским способом — посимвольно. То есть: возьмем первый символ из блока данных, выведем, потом второй и т. д. аналогично пока не встретим символ ‘$’.

Опять-таки, это если в двух словах. Но ведь наверняка вам этого покажется мало ;).

«Щемить» символы мы будем двумя способами. (Тут я хотел было написать «неправильным» и «правильным», но потом передумал и решил обозвать их «первым» и «вторым»).
В алгоритм первого способа вы и сами без труда въедете (я только «рабочую часть» приведу, пуши с попами сами проставляйте):

#2. А вот второй способ немножко навороченнее :). Чтобы в нем разобраться, мы сначала познакомимся со следующей группой команд: LODSB, LODSW, LODSD
Их назначение — это загрузка элемента из последовательности (строки, цепочки) в регистр-аккумулятор al/ax/eax.
Для тех, кто не понял — наша строчка «Hello, World-2$» как раз и является «последовательностью/строкой/цепочкой» из элементов размером в байт.
Адрес цепочки передается через ds:esi/si, сами «элементы» (фиксированной ширины) возвращаются в al (байт, команда LODSB), ax (слово, команда LODSW) или eax (двойное слово, команда LODSD). В общем, последние буковки команд как раз и указывают на размерность элемента: [B]yte, [W]ord, [D]ouble word. Т. е. размер мы определяем неявно.
После выполнения одной из этих команд значение регистра si изменяется на величину, равную длине элемента, но. хм.

Пришло время еще одну большую тайну познать, братья. В справочнике Юрова написано, «знак этой величины зависит от состояния флага df:
df=0 — значение положительное, то есть просмотр от начала цепочки к ее концу;
df=1 — значение отрицательное, то есть просмотр от конца цепочки к ее началу.»
Обидно, да? Флаги-то мы с вами еще не расколупали. Чего-ж дальше-то делать, а?

Как что? «Пропивать» стандартную процедуру и колупать ее, колупать, колупать, ногами ее, ногами, и по морде, по морде, по морде.

Итак, что делает команда lodsb, вы уже поняли. А вот с df=0/df=1 пока что непонятки.
Будем разбираться.

Нам нужно, чтобы «просмотр цепочки» осуществлялся командой lodsb слева направа, для этого нужно установить значение df=0. Делаем мы это при помощи команды cld (бряк 2).
Если нам нужно, чтобы «просмотр цепочки» осуществлялся справа налево, мы используем команду std. (Можете попробовать. Ерунда получится.)

А теперь вспомним «золотое правило». Все регистры, которые мы изменяли ВНУТРИ процедуры, «на выходе» должны восстанавливать свои ПРЕДЫДУЩИЕ значения (кроме тех регистров, через которые мы возвращаем РЕЗУЛЬТАТ).
Так вот: то же самое касается и флагов, которые мы изменяем.
Для регистров мы использовали команды PUSH и POP. Для регистра флагов (изменять у которого мы можем только БИТЫ) используются команды pushf (записать в стек значения флагов, бряк 1) и popf (извлечь из стека, бряк 10).

ПРИМЕЧАНИЕ: Это несколько вольное положение (хотя, вобщем-то, верное). Видимо, здесь следует хотя бы добавить, что сохранять/восстанавливать флаги нужно при изменении специальных флагов (if, df). (С) Хемуль

Теперь колупаем дальше. (С) Serrgio

Бряк 3. Мне удобнее передавать данные «в процедуру» через регистр DX, о чем и написано в заголовке. А lodsb (бряк 4) хотит, чтоб адрес ему в SI подавали. Удовлетворим его желание :)
Бряк 4. После выполнения этой команды ASCII-код первого символа «цепочки» «Hello, World-2$» помещается в AL, а значение регистра SI увеличивается на 1 и указывает теперь на второй элемент цепочки (символ ‘e’).
Бряк 7. Удовлетворяем «пожелания» процедуры write_char. Из AL переносим в DL и печатаем (бряк 9), write_char’ом.

Тэкс. Мы пропустили бряки 5 и 6-й.
Смотрите: на бряке 9 мы «безусловно» зацикливаем «извлечение» и печать элементов цепочки. Безусловно — это значит до потери пульса. Чтоб этого не произошло, каждый из элементов цепочки мы сравниваем с символом-конца-цепочки (в нашем случае это ‘$’, но можно использовать и любой другой). Если текущий символ равен символу-конца-цепочки, то выпрыгиваем (бряк 6) из этого безусловного цикла, восстанавливаем статус-кво (бряк 10) и все на этом.

Напечаталось то, что надо?
Если true — читайте дальше.
Если false — расколупывайте и медитируйте до полного просветления.

Функция DOS 3Dh — Открыть существующий файл

Дата добавления: 2013-12-23 ; просмотров: 1071 ; Нарушение авторских прав

Признаком ошибки при выполнении функции DOS является взведенный флаг С (переноса).

В конце работы файл следует закрыть.

Работа с файлами.

Объединения

Getfield имя_элемента_записи, регистр_назначения, источник

Setfield имя_ элемента_записи, назначение, регистр_источник

Дополнительные возможности обработки

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

Для установки значения некоторого поля записи используется команда setfield с синтаксисом:

Для выборки значения некоторого поля записи используется команда getfield с синтаксисом:

Ассемблер даёт возможность переопределить область памяти для объекта с другими типом и именем с помощью специального типа данных, называемого объединением. Объединение — тип данных, позволяющий трактовать одну и ту же область памяти как имеющую разные типы и имена.

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

имя_объединения UNION

имя_обьединения ENDS

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

Метод работы с файлами в системе MS DOS называется методом описателя или дескриптора. Идея работы с файлами методом описателя в MS DOS заключается в следующем:

1. Вначале файл должен быть открыт, при этом должно быть указано имя файла — либо полное (полный путь), либо краткое. В последнем случае файл берется из текущего каталога. В конце имени файла должен стоять код 0.

2. После того как файл был удачно открыт, ему присваивается описатель — число от 5 до 256 (записывается в регистр АХ). Дальнейшая работа с файлом будет вестись через этот описатель. Под таблицу описателей отводится 20 байт, поэтому программа не может открыть одновременно более 20 файлов.

Количество одновременно открытых файлов (включая предопределенные файлы) не может превышать 20, хотя количество описателей может быть больше. Поскольку 5 первых описателей всегда открыто, то получается, что можно одновременно открыть не более 15 файлов.

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

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

При открытии выделяются буфера отдельно на чтение и запись. В MS DOS имеется функция 68Н, с помощью которой можно сбрасывать буфера, выделенные для записи, на диск, не закрывая файла. Эту функцию часто используют для того, чтобы обезопасить себя от возможной потери данных. Аналог ее имеется во всех языках высокого уровня.

DOS поддерживает как работу с длинными именами, так и с короткими именами файлов и каталогов.

При открытии или создании файла, DOS возвращает в регистр АХ описатель файла или HANDLE -двухбайтное число. Все операции с данным файлом после этого можно производить, зная только это число.

Стандартным устройствам ввода-вывода по умолчанию присваиваются свои описатели — от 0 до 4. Вот эти описатели:

● STDIN стандартное устройство ввода (клавиатура) — 0,

● STDOUT стандартное устройство вывода (экран) -1,

● STDERR устройство для вывода ошибок (обычно экран) — 2,

● AUX асинхронный порт(СОМ1) — 3,

● PRN печатающее устройство (LPT1) — 4.

Это позволяет перенаправить ввод или вывод с одного устройства на другое.

Можно выделить четыре аспекта работы с файлами в программах на ассемблере:

– работа с системой файлового ввода-вывода MS DOS, использующей короткие имена;

– работа с системой файлового ввода-вывода MS DOS, использующей длинные имена (длиной до 255 символов);

– работа с системой файлового ввода-вывода Win 32;

– использование файлов особого вида, поддерживаемых Win 32 — проецированных на память.

Ввод: AH = 3Dh

AL = режим доступа

биты 0-1: открыть для записи или для чтения

Установка/чтение времени

При старте MS DOS запрашивает у пользователя текущее время. Введенное значение помещается в 4 байта, хранящие счетчик времени суток (начиная с 0040:006C, младший байт хранится первым). Hо сначала оно преобразуется в форму, в которой подсчитывается время суток, т.е. время преобразуется в число восемнадцатых долей секунды, прошедших с полночи. Это число постоянно обновляется 18.2 раз в секунду прерыванием таймера. Когда появляется очередной запрос на время, то текущее значение счетчика времени суток преобразуется обратно в привычный формат часы-минуты-секунды. Если при старте не было введено значения, то счетчик устанавливается в ноль, как будто сейчас полночь. Kомпьютеры снабженные микросхемой календаря-часов могут автоматически устанавливать счетчик времени суток.

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL