Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
| Помогли мы вам |
С месяц назад я рассказывал, как написать игрушку FloppyBird, которая тоже умещалась в бутсектор. Но по сравнению с тем, что мы с тобой сотворим сейчас, она покажется тебе мелкой шалостью.
По сути, написав бейсик для бутсектора, мы превратим твой ПК в аналог старых домашних компьютеров типа Commodore 64 или ZX Spectrum, которые имели этот язык в ПЗУ и позволяли программировать на нем сразу после загрузки.
Техническое задание (что будет уметь наш интерпретатор) я сформулирую в виде инструкции пользователя. Вот она.
Интерпретатор работает в двух режимах: интерактивном и обычном. В интерактивном режиме он выполняет команды сразу после ввода.
В обычном режиме сначала надо занести исходник программы в память и затем дать команду run.
Если нужно удалить строку из исходника, просто введи в командной строке ее номер.
Как интерпретатор узнаёт, в каком режиме обрабатывать текст из командной строки? Если строка начинается с номера, интерпретатор обрабатывает ее в обычном режиме. Если не с номера — в интерактивном.
Максимальный размер программы — 999 строчек. Максимальная длина строки — 19 символов. Обрати внимание, что клавиша Backspace функционирует как надо. Хоть на экране символ и не затирается, в буфере все в порядке.
В распоряжении у программиста:
run (стирает программу), list (выводит РёСЃС…РѕРґРЅРёРє РЅР° экран), new (запускает программу);if, goto, =;print, input.Р’РѕС‚ языковые конструкции, которые понимает наш интерпретатор:
var=expr присваивает значение expr переменной var (от a до z);print expr выводит значение expr и переводит курсор на следующую строку;print expr; выводит значение expr и оставляет курсор на текущей строке;print "][ello" печатает строку Рё переводит РєСѓСЂСЃРѕСЂ РЅР° следующую строку;print "][ello"; печатает строку и оставляет курсор на текущей строке;input var считывает значение СЃ клавиатуры, помещает его РІ переменную var (a..z);goto expr переходит РЅР° указанную строку программы;if expr1 goto expr2 — если expr1 РЅРµ 0, прыгнуть РЅР° строку expr2, иначе на следующую после if.Пример: if c-5 goto 2 (если c-5 не 0, прыгаем на строку 2).
Начинаем с того, что задаем области памяти, которыми будем пользоваться:
Все сегментные регистры нацеливаем на CS. Затем сбрасываем «флаг направления», чтобы строки обрабатывались слева направо, Р° РЅРµ наоборот (РєРѕРіРґР° будем обращаться Рє инструкциям РІСЂРѕРґРµ stosb). Буфер, который предназначен для исходника программы, заполняем символом 0x0D (СЃРёРјРІРѕР» возврата каретки, более известный как клавиша Enter).
Р?СЃС…РѕРґРЅРёРє программы РЅР° бейсике будем обрабатывать как двумерный символьный массив: 1000 Г— 20.
Если введешь строку больше 19 символов, она заедет на соседнюю. В текущей реализации интерпретатора этот баг не отслеживается. Просто помни, что больше 19 символов в строчку вписывать нельзя.
Здесь сначала восстанавливаем указатель стека (регистр SP). На тот случай, если программа на бейсике обрушилась из-за ошибки.
Затем сбрасываем указатель running (текущая строка программы). Потом вызываем подпрограмму input_line, которая ждет, РїРѕРєР° программист что-РЅРёР±СѓРґСЊ напечатает. Подпрограмма сохраняет полученную строку РІ регистр SI.
Дальше смотрим, начинается строка с номера или нет. Если с номера, нам надо записать ее в буфер, который отведен под исходник. Для этого сначала вычисляем адрес, куда записывать строку. За это у нас отвечает подпрограмма find_address (результат кладет РІ регистр DI). Определив нужный адрес, копируем туда строку: rep movsb.
Если в начале строки нет номера, сразу выполняем ее: execute_statement.
Строки программы обрабатываем следующим образом. Берем первое слово из строки и последовательно сравниваем его с каждой записью из таблицы @@statements (СЃРј. РІРЅРёР·Сѓ статьи последний РєСѓСЃРѕРє РєРѕРґР°). Р’ этой таблице общим СЃРїРёСЃРєРѕРј перечислены команды, операторы Рё функции, которые понимает наш интерпретатор.
Обрати внимание, какую эвристику СЏ здесь использую, чтобы сэкономить байты РЅР° обработку условного оператора. Перед точкой РІС…РѕРґР° execute_statment я поставил дополнительный вход в ту же самую подпрограмму: @@if_handler.
Зачем? Чтобы РЅРµ надо было писать отдельный обработчик для конструкций РІСЂРѕРґРµ if a-2 goto 10. Если результат выражения (в данном случае a-2) равняется нулю, РјС‹ РЅРµ заходим РІ if, то есть игнорируем остаток строки (в нашем случае goto 10).
РЎ if разобрались. Дальше обрабатываем остальные команды, операторы и функции. Начинаем с того, что пропускаем лишние пробелы, которые программист добавил для своего удобства. Если в строке нет ничего, кроме пробелов, просто игнорируем ее.
Но если строка не пустая, присматриваемся к ней внимательно. Сначала перебираем по порядку таблицу @@statements Рё сверяем СЃРІРѕСЋ строку СЃ каждой записью оттуда. Каким образом сверяем? Считываем размер строки (РІ случае run это 3) Рё затем сравниваем, используя repe / cmpsb.
Если совпадение обнаружилось, то регистр DI теперь указывает РЅР° соответствующий адрес обработчика. Поэтому РјС‹ без лишних телодвижений прыгаем туда: jmp [di]. Чтобы лучше понять, в чем тут прикол, загляни в конец статьи, посмотри, как устроена таблица @@statements. Подсказка: метки, которые начинаются СЃ @@, — это как раз и есть адреса обработчиков.
Если всю таблицу перебрали, но совпадения так и не нашли, значит, текущая строка программы — это не команда, не оператор и не функция. Раз так, может быть, это название переменной? Прыгаем на @@to_get_var, чтобы проверить.
Дальше проматываем регистр DI к следующей записи таблицы. Каким образом? Прибавляем CX (длина имени текущей команды, оператора или функции плюс еще РґРІР° байта (адрес обработчика). Потом восстанавливаем значение регистра SI (rep cmpsb перемотала его вперед), чтобы РѕРЅ опять указывал РЅР° начало строки, РїРѕ которой РјС‹ выполняем РїРѕРёСЃРє РІ таблице операторов.
Теперь DI указывает на следующую запись из таблицы. Если эта запись ненулевая, прыгаем на @@next_entry, чтобы сравнить строку программы, вернее ее начало, СЃ этой записью.
Если РјС‹ прошли РІСЃСЋ таблицу, РЅРѕ так Рё РЅРµ нашли совпадения, значит, текущая строка — РЅРµ команда, РЅРµ оператор Рё РЅРµ функция. Р’ таком случае это, скорее всего, конструкция присваивания РІСЂРѕРґРµ var=expr. По идее, других вариантов больше нет. Если, конечно, в исходник не закралась синтаксическая ошибка.
Теперь нам надо вычислить выражение expr Рё поместить результат РїРѕ адресу, СЃ которым связана переменная var. Подпрограмма get_variable вычисляет нужный нам адрес Рё кладет его РЅР° стек.
После того как адрес найден, проверяем, есть ли после имени переменной оператор присваивания. Если да, нам надо его выполнить. Но в целях экономии байтов мы сделаем это не здесь.
Чуть РЅРёР¶Рµ нам СЃ тобой так Рё так придется реализовывать присваивание внутри функции input. Вот на тот кусок кода мы и прыгнем: @@assign. Целиком нам тут функция input ни к чему. Понадобится только ее финальная часть, вот ее и берем. Обратно в execute_statment возвращаться РЅРµ будем. Нужный ret выполнит сама функция input.
Если знака присваивания нет, печатаем сообщение РѕР± ошибке Рё прекращаем выполнение программы, то есть прыгаем РЅР° @@main_loop. Там интерпретатор восстановит указатель стека и сможет работать дальше, несмотря на то что наткнулся на синтаксическую ошибку.
|
|
|






