Программирование в машинном коде

Рассмотрим несколько примеров простейших МиК-программ, иллюстрирующих некоторые принципиальные моменты техники программирования в машинных языках вообще.

Задача 1. Написать МиК-программу  нахождения разности двух заданных целых чисел.

Решение. Алгоритм решения данной задачи, составленный с учётом системы команд МиК, мог бы представлять  собой следующую последовательность шагов:

Шаг_1. Ввести уменьшаемое (in);

Шаг_2. Сохранить введённое данное в ячейке ОП  (st);

Шаг_3. Ввести вычитаемое  (in);

Шаг_4. Сохранить введённое данное в ячейке ОП  (st);

Шаг_5. Загрузить в сумматор сохранённое в ОП уменьшаемое (ld);

Шаг_6. Найти разность (sub);

Шаг_7. Вывести разность (out);

Шаг_8. Остановиться (halt).

Попытка перевода этого алгоритма в МиК-программу немедленно ставит вопрос о распределении памяти. Действительно, мы должны  решить:

1) В какой ячейке оперативной памяти сохранить введённое уменьшаемое;

2) В какой ячейке оперативной памяти сохранить введённое вычитаемое;

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

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

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

 Распределение памяти МиК для решения задачи 1:

Адрес сохранения уменьшаемого

Адрес сохранения вычитаемого

Адрес загрузки программы

       0400

      0402

        0404

Всё же,  существующие в нашем выборе ограничения, которые объясняют употребление нами словосочетания “почти прозвольно”,   сводятся к следующему.

Во-первых, мы должны оставаться в рамках диапазона, составляющего полное адресное пространство  ОП машины  МиК ( 0000 – 9999 );

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

В-третьих, необходимо учесть, что команды программы, начинаясь с адреса её загрузки, записываются строго последовательно, без пропусков занимая непрерывный блок памяти. Поэтому, нужно следить за тем, чтобы блок этот не разрывался ячейками, отводимыми под данные. Так, выбор для уменьшаемого слова с адресом 0410 был бы в этом смысле ошибочен (подумайте, к чему привела бы такая ошибка!);

И, наконец, удобней располагать данные в памяти перед программным кодом, а не после него (почему?).

 Теперь мы готовы привести полную запись МиК-программы решения поставленной задачи:

Адреса Данные Комментарии
0400 ? место уменьшаемого
0402 ? место вычитаемого
0404 01   in (ввод)
0405 22 0400                                       st (запись в память)
0408 01   in (ввод)
0409 22 0402                                       st (запись в память)
0412 21 0400                                       ld (загрузка из памяти)
0415 11 0402                                       sub (вычитание)
0418 02 0ut (вывод)
0419 99 halt (остановка)

 Заметим, что в памяти, в отличие от нашей, “очеловеченной” формы записи, программа никак не структурирована и (начиная с адреса загрузки - 0404)  “выглядит” следующим образом:  01220400012204022104001104020299.

Но если в  РА поместить начальный её адрес 0404 и запустить процессор, то, в соответствии с аппаратным алгоритмом выполнения команд, он последовательно распознает и выполнит все составляющие эту программу команды. Не поленитесь, и мысленно промоделируйте эти действия процессора.

Задача 2. Написать МиК-программу, выбирающую наибольшее из двух заданных чисел.

Решение. Обозначим первое из заданных чисел через m, а второе – через n. Тогда для достижения поставленной цели,  мы можем воспользоваться следующим алгоритмом:

Шаг_1. Ввести значение m (in);

Шаг_2. Сохранить введённое значение m в ячейке ОП  (st);

Шаг_3. Ввести значение n (in);

Шаг_4. Сравнить n  и  m, вычислив их разность n- (cmp);

Шаг_5. Если вычисленная на предыдущем шаге разность отрицательна,   перейти на Шаг_ 7  (jm);

Шаг_6. Перейти на Шаг_8 (jmp);

Шаг_7. Взять в качестве результата значение m (ld).

Шаг_8. Вывести результат (out);

Шаг_9. Остановиться (halt). 

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

Распределение памяти МиК для решения задачи 2 

Адрес сохранения значения m

Адрес загрузки программы

       0300

        0302

 Попытавшись записать МиК-программу, мы обнаружим, что появилась дополнительная трудность: адресные части команд перехода (jm, jmp) не удаётся записать сразу. Они могут быть конкретизированы только после того, как в программе определятся адреса  команд, на которые ссылаются эти команды перехода. Поэтому, возможно, программа сначала будет записана c неопределёнными адресными частями указанных команд  в виде:

Адреса

Данные и команды

Комментарии

0300

?

Резервировано для значения  m

0302

01

In {Шаг_1} Ввод m

0303

22  0300

St {Шаг_2} Cохранение m в ОП.

0306

01

In {Шаг_3} Ввод n;  сохранять n  в ОП не надо!

0307

12  0300

Cmp {Шаг_4} Формирование ПР по разности n-m

0310

34  . . .

Jm {Шаг_5}  Переход, если ПР<0

0313

30  . . .

Jmp {Шаг_6} Безусловный переход (при ПР>=0)

0316

21 0300

Ld {Шаг_7} Загрузка в сумматор числа m

0319

02

Out {Шаг_8} Вывод результата

0320

99

Halt {Шаг_9} Остановка

Теперь определилась информация для  записи адресных частей команд с адресами 0310 и 0313, что позволяет представить программу решения данной задачи в окончательной форме:

Адреса

Данные и команды

Комментарии

0300

?

Резервировано для значения  m

0302

01

In {Шаг_1}

0303

22  0300

St {Шаг_2}

0306

01

In {Шаг_3}

0307

12  0300

Cmp {Шаг_4} Формирование ПР

0310

34  0316

Jm {Шаг_5}  Переход, если ПР<0

0313

30  0319

Jmp {Шаг_6} Безусловный переход (при ПР>=0)

0316

21  0300

Ld {Шаг_7} Загрузка m

0319

02

Out {Шаг_8} Вывод результата

0320

99

Halt {Шаг_9} Остановка

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

Программный эмулятор машины МiK, руководство, справочник и пошаговая инструкция по его использованию показаны ниже и доступны для скачивания ЗАРЕГИСТРИРОВАННЫМ  пользователям.

4.785715
Your rating: Нет Average: 4.8 (14 votes)

Комментарии

Анастасия и Рустем

Анастасия и Рустем, кажется, уже разобрались с "красивым кодом". Давайте двигаться дальше ...

Спасибо) только меня зовут

Спасибо) только меня зовут Сергей...

Спасибо, только меня зовут

Спасибо, только меня зовут Сергей.

Да, простите, Сергей ...

Да, простите, Сергей ...
Хотя это и не умаляет Ваших заслуг :)

8-ой строкой я показываю, что

8-ой строкой я показываю, что если пользователь введет 0 выйдет ошибка, т.к. на 0 делить нельзя.

Вашу цель я понимаю, Сергей.

Вашу цель я понимаю, Сергей. Не понимаю способа её разрешения. Что вы ожидаете от программы в случае нуля? Сообщение об ошибке? Зависание? Выдачу случайного результата? Никто не знает...
Всё зависит от случайного содержимого ячейки 500 и других незадействованных Вами ячеек памяти.

т.к. в ячейке нет команды, то

т.к. в ячейке нет команды, то вылетит ошибка...

Сергей!

Кто Вам сказал что нет?
Железная заповедь программиста - если Вы не заполнили (не инициализировали) ячейку (переменную), а в ЯП явно не оговорено чем заполняются неинициализированные ячейки, то в ней содержится НЕОПРЕДЕЛЁННАЯ информация, т.е. всё что угодно! Я могу смоделировать любую реакцию из перечисленных мною выше.

Я понял...Спасибо за

Я понял...Спасибо за разъяснения..

Задание №2.

y=2x-1

  1. 200)?          // для x
  2. 202)?          // для -1
  3. 204)01       // ввод x
  4. 205)22 0200    // запись x в адрес 0200
  5. 208)10 0200    // 2*x
  6. 211)22 0200    // запись x в адрес 0200
  7. 212)23 0001    // загружаем 1
  8. 217)11 0002    // вычитаем 2
  9. 220)22 0202    // запись -1 в адрес 0202
  10. 223)21 0200   // загружаем x из адреса 0200 в сумматор
  11. 226)11 0202    // вычитаем сумматор из адреса 0202
  12. 207)02        //  выводим
  13. 208)99        // стоп

Прошу комментировать

Прошу комментировать решение Дениса (y=2X). Спасибо, Денис, за повод для критики (в любом случае балл за активность Ваш). Кстати, Денис, почему бы не попробовать программку прогнать через МиК и посмотреть что получится?

Янпольская Катя

Прошу прощения, вынужден Ваш пост удалить не читая. Плохо изучаете мои комментарии, в которых я сформулировал требования.
1. Размещать решение только одной задачи за один раз.
2. Оформлять правильно вставку в комментарий программного кода.

Рустэм, Ирина, Валерия

Рустэм, Ирина, Валерия - поактивней!

Рустэм - "насчёт Д.З."

Посмотрите, пожалуйста, чуть выше, замечание 2 моего обращения к Янпольской Кате. Ваш пост тоже удалён без прочтения. Вы же уже демонстрировали умение правильно оформлять код, так в чём же дело? Лень написать пару тегов?
И ещё. Вы что, читаете только те посты, которые адресованы непосредственно Вам?

я занята была, очень

я занята была, очень понравилось на ассемблере работать, а сейчас работаю над мик, звяла примеры у вас, вот работаю. Если я не пишу Вам, это не значит что меня нет (я каждый день сюда захожу и читаю все), я еще с мик работаю просто пока таких вопросов нету, хочу сама все понять!

Исправление ошибок)

y=2x-1

  1. 200)         //  для х
  2. 202)         //  для 1
  3. 204) 01      //  ввод х
  4. 205) 22 0200  //  записываем в адрес 0200
  5. 208) 10 0200  //  2*х
  6. 211) 22 0200  //  записываем 2*х в адрес 0200
  7. 214) 23 0001  //  загружаем 1
  8. 217) 22 202  //  записываем в адрес 0202
  9. 220) 21 0200  //  загружаем в сумматор х
  10. 223) 11 0202  //  вычитаем из сумматора единицу из адреса 0202
  11. 206) 02      //  выводим
  12. 207)99        //  стоп

Цикл

  1. in; //вводим n
  2. st n; //записываем
  3. la 1; //берем 1
  4. st one; //записываем 1
  5. ld n; //загружаем n
  6. m: jz final; // метка(адрес) M  ,куда вернется программа  а метка(адрес) final - куда перейдет программа при z=0
  7. out; //вывод
  8. sub one; //вычитаем 1
  9. st n; //записываем n
  10. imp m; //прыжок на метку m
  11. final: halt; // метка(адрес) куда перейдет программа если z=0<\pre>
  12. извините...буду внимательней..

Денис

Всё исправили сами. Хотя жаль, конечно, что публичного обсуждения ошибок не получилось.
Одно замечание - не точный комментарий в строке 9. Загружаете 2X а не X (если говорить об X из условия задачи). Возможно, обозначнние X неудачно.

Сергей

Напоминаю, код без условия решаемой залачи не выкладывать!
И я не понял предмета самоупрёка в последней строке.

Д.З.

кое что было непонятно, но уже разобрался.

  1. (In);
  2. (St) N;
  3. (La) 1;
  4. (St) one;
  5. (Ld) N;
  6.  M: (Jz) Final;
  7. (Out);
  8. (Sub) one;
  9. (St) N;
  10. (Jmp) M;
  11. Final

думал вы ко мне обращались...

думал вы ко мне обращались...когда делали замечания Ачмиз Рустэму..

Спасибо, понял.

Спасибо, понял.

Сергей, помогите ...

Сергей, попытайтесь прокомментировать код Рустэма и помочь ему

Неточно, Рустэм

Не дождался Сергея ... комментирую сам:
1.Когда это я ставил круглые скобки внутри программ на Ассемблере?
2.Последняя строка некорректна - метка не может "висеть в воздухе"

помощь

  1. In; //ввод
  2. St N; //записываем то что ввели
  3. La 1; //берем 1
  4. St one; //записываем 1
  5. Ld N; //загружаем в сумматор n - то что ввели
  6.  M: (Jz) Final; //метка m(адрес) это место куда перейдет программа после прыжка(jmp m). final -метка(адрес) указание куда перейти программе если z=0.
  7. Out;// вывод
  8. Sub one;//вычитаем 1
  9. St N;//записываем n
  10. Jmp M;// метка(адрес) указание места перехода
  11. Final: halt;//метка (адрес) куда перейдет программа если z=0

опоздал

извините что так долго...я решил каждую строку объяснить...

Задача №4

y=-2x+4z

  1. 200)            // для x
  2. 202)           //  для z
  3. 204) 01         //  вводим x
  4. 205) 22 0200   //  запись в адрес 0200
  5. 208) 10 0200   //  сумматор прибавляем x
  6. 211) 22 0200   //  2*x записываем в 0200
  7. 214) 11 0200   //  2*x-2*x
  8. 217) 11 0200    //  сумматор 0-2*x
  9. 220) 22 0200   // -2*x записываем в адрес 0200
  10. 223) 01        //  вводим z
  11. 224) 22 0202    //  записываем z в адрес 0202
  12. 227) 10 0202   //  2*z
  13. 230) 22 0202   //  2*z записываем в адрес 0202
  14. 233) 10 0202   //  сумматор 2*z прибавляем 2*z из адреса 0202
  15. 236) 22 0202    //  записываем 4*z в адрес 0202
  16. 239) 21 0200    //  загружаем -2*x в сумматор
  17. 242) 10 0202    //  сумматор -2*x прибавляем 4*z из адреса 0202
  18. 245) 02         //  выводим -2*x+4*z
  19. 246) 99         //  стоп

Спасибо

Учту свои ошибки. Спасибо

Целочисленное деление a и b с остатком

  1. 0100) ?           //делимое
  2. 0102) ?            //делитель
  3. 0104) ?            //счетчик
  4. 0106) 01           //ввод делимого с клавиатуры
  5. 0107) 22 0100      //запись делимого
  6. 0110) 01            //ввод делителя
  7. 0111) 22 0102      //запись делителя
  8. 0114) 23 0000      //берем 0
  9. 0117) 22 0104      //обнуляем счетчик
  10. 0120) 21 0100      //загружаем в s делимое
  11. 0123) 11 0102      //вычитаем из делимого делитель
  12. 0126) 34 0150      //если частное отрицательное выводим предыдущее частное и остаток        
  13. 0129) 22 0100      //запись полученного делимого
  14. 0132) 23 0001      //берем 1
  15. 0135) 10 0104      //начало счетчика
  16. 0138) 22 0104      //записываем данные счетчика
  17. 0141) 21 0100      //загружаем в s делимое
  18. 0144) 33 0150      //если s=0, то продолжение программы начнется с 0150 ячейки
  19. 0147) 30 0126      //возвращение к ячейке 0126
  20. 0150) 21 0104      //загрузка в s данных счетчика
  21. 0153) 02            //вывод данных счетчика(частное)
  22. 0154) 21 0100      //загружаем остаток
  23. 0157) 02            //вывод остатка
  24. 0158) 99            //стоп

от Души Сержант)))

Спасибо) все стало намного ясней только код длинный не проще ли добавить просто функцию или переменную? т.е. что бы писать такой код а просто например 46 - это деление?) я за простоту действий ... хотя как говорят нужно взять самого ленивого и он сделает как будет проще и быстрей=))))

Пожалуйста

Сейчас наша задача состоит в том, чтобы научится программировать на НЯП...а если судить о простоте...то зачем "изобретать велосипед" можешь работать на ЯВУ... К примеру ЯВУ Pascal..
— оператор «div» используется для целочисленного деления
— оператор «mod» используется для получения остатка от деления

Валерий Шахамболетович!не

Валерий Шахамболетович!не думайте что я отстранилась!сижу читаю статьи и понимаю, что ничего не понимаю! пока что не дается мне написание алгоритма на ассемблере, и я даже не знаю как вопросы свои сформулировать, каша в голове! Буду разбираться сама и как можно скорее присоединюсь к обсуждениям или выложу свой алгоритм!

Деление - отлично.

Отлично, Сергей. Да и с текстом для Рустэма постарались. Вот только отреагировал он на наши с Вами усилия как-то вяло ...

Рустэм, если я уделяю Вам столько внимания (может быть придираюсь, на Ваш взгляд), то только потому, что увидел в самом начале творческую искру и желание работать. К сожалению, Вы похоже, сникли при первых же неудачах и не учитесь ни на своих ошибках, ни на ошибках товарищей. Если не проснётесь, то я Вас "оставлю в покое", где и будете пребывать со многими другими к которым я не испытываю, в силу полной их пассивности, всё меньше интереса.

Задача 2(х+y)-1

  1.  ?                        //ввод ячейки под "Х"
  2.  ?                        //ввод ячейки под (Х+Y)
  3.  ?                        //ввод ячейки под 1-цу
  4.  In;                     //ввод "Х"
  5.  St X;                  //сохран. "Х" в ячейке 1
  6.  In;                     //ввод "Y"
  7.  Add;                  //(Y+X)
  8.  St (X+Y);           //сохран. (Х+Y) в ячейку 2
  9.  La;                    //получим "1"
  10.  St 1;                 //сохран. "1" в ячейку 3
  11.  Ld 2;                //вывод (Х+Y)
  12.  Add;                //2(X+Y)
  13.  Sub 1;             //отнимаем 1-цу          
  14.  Final: Out        //метка (адрес)

Доработал немного.

Извините, что в последнее

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

Всё получится, Настя.

Не бойтесь, Настя, просто говорить в каком месте Вам не понятно. Мне почему-то кажется, что дело в каких-то пустяках. Давайте всё же попробуем вместе. Всё получится!

А на эти выходные будут

А на эти выходные будут какие-нибудь задачи? Мне кажется я быстрее пойму если буду пробовать решать их, а так я читаю статьи, обсуждения и только больше путаюсь(

А еще, подскажите,

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

Спасибо))

Спасибо))

Станислав, Ира

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

Жду, Ира, включайтесь ...

Я Вас прекрасно понимаю.

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

Настя, Ира

Задачи есть на сайте, в комментариях и форумах. Но я подброшу в ближайшие дни несколько новых.
Спокойной ночи!

Простите, Рустэм, но это не

Простите, Рустэм, но это не программа. Сравните с тем, что я писал сегодня на лекции ...
Утро вечера мудренее, а на сегодня, Рустэм, Настя, Ира, Сергей - всем спать!

я спать)

я спать)

Многое учту))) спасибо)

многое учту) спасибо)

Правильная карактировка задачи Рустэма.

  1. in; // ввод х
  2. st x; // заполнение в ячейку х
  3. in; // ввод у
  4. add x; // получаем (х+у)
  5. st x; // заполняем в ячейку х
  6. add x; // получаем 2(х+у)
  7. st x; // заполняем в ячейку х
  8. la 1; //  получаем единицу
  9. st y; // заполняем в ячейку у
  10. ld x; // вводим число в сумматр из ячейки х
  11. sub y; // отнимаем единицу
  12. out; // вывод на дисплей
  13. halt; // стоп

Да, Дмитрий

Да, Дмитрий, здесь всё в порядке. Поздравляю с удачным дебютом!
Вот только писать надо "корректировка" или "коррекция". Да и вычисляемую формулу, по нашим правилам, требовалось воспроизвести вместо туманного "задача Рустэма" - в следующий раз удалю пост не читая.

Спасибо)

...ну и грамотей я (((

Посмотритста пожалуйста.

Спасибо Дмитрий за помощь, я понял свои ошибки. В Шахамболетович увидев пост с информацией о завтрешнем тесте, решил попробывать запрограммировать эту же задачу 2(Х+Y)-1 (в качестве тренеровки))). Данные уже вводил в МиК и алгоритм работал, по записи посмотрите есть ли ошибки?:

  1. 0400) ?// оставляем ячейку по Х
  2. 0402) ?//оставляем ячейку под х+y
  3. 0404) ?//выделим ячейку под 1-цу
  4. 0406) 01//ввод Х
  5. 0407) 22 0400// сохран. Х в ячейку 400
  6. 0410) 01//ввод Y
  7. 0411) 10 0400//Y+X (делаем сложения переменных)
  8. 0414) 22 0402//сохран. результат сложения в  ячейку 402
  9. 0417) 23 0001//сохраняем а памяти 1
  10. 0420) 22 0404//заносим 1 в ячейку 404
  11. 0423) 21 0402//вывод Х+Y
  12. 0426) 10 0402// складываем скобку со скобкой (иммитируем умножения)
  13. 0429) 11 0404//отнимаем 1-цу
  14. 0432) 02//вывод
  15. 0433) 99//остановка

Валерий Шахамболетович,

Валерий Шахамболетович, что-то проясняться стало в моей голове, по этому поводу решила попробовать написать алгоритм целочисленного деления с остатком, предложенный ранее Сергеем в машинном коде, на асеемблере! Своего пока что ничего выдумывать не стала, потому что мне сейчас главное суть понять!
Сергей, не серчайте на меня, за то что я Вашу идею за основу взяла, просто очень уж доходчиво и понятно вы все написали))

  1. IN;           //вводим Х с клавиатуры
  2. ST X;         //сохраняем Х
  3. IN;           //вводим У с клавиатуры
  4. ST Y;         //сохраняем У
  5. LA 0;         //присваиваем S номер нулевой ячейки
  6. ST 0200;      //сохраняем 0 в ячейку ОП под номером 200
  7. LD X;         //загружаем в сумматор Х
  8. N: SUB Y;     //отнимаем У
  9. JM M;         //если результат вычитания <0 переходим к шагу М
  10. ST X;         //сохраняем результат вычитания в Х
  11. LA 1;         //присваиваем S номер первой ячейки
  12. ADD 0200;     //прибавляем 1 к ячейке под номером 200, тем самым запоминая сколько раз У полностью "уместился" в Х
  13. ST 0200;      //сохраняем результат сложения в ячейке ОП под номером 200
  14. LD X;         //загружаем в S значение Х
  15. JZ M;         //если Х равен нулю, переходим к шагу М
  16. JMP N;        //возвращаемся к шагу N
  17. M: LD 0200;   //загружаем значение ячейки ОП под номером 200 в S
  18. OUT;          //выводим целочисленный результат "деления" сохраненный в ячейке 200 на дисплей
  19. LD X;         //загружаем в память S "остаток" сохраненный в ячейке Х        
  20. OUT;          //выводим значение Х на дисплей
  21. HALT;         //остановка