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

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

Задача 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)

Комментарии

Программа верна, Рустам.

Программа верна, Рустам. Однако неточности в комментариях (стр.9, 11)

Хорошо, Настя

Хорошо, Настя, программа верна.
Пара замечаний. Почему-то Вы пользуетесь и символическими именами (X, M, например) что логично, и числовыми абсолютными адресами (0200), что не правильно. Нужно бы вместо 0200 использовать, например, D или DIV - частное. Не очень точны и комментарии. Но в целом Вы справились, поздравляю!

Спасибо большое! Я запомнила)

Спасибо большое! Я запомнила) Неужели что-то поняла, приятно даже как-то)

Спасибо за внимания)

Учту и исправлю свои ошибки по МиК-алгоритму, а теперь продолжу тему Анастасии по машинному коду, на асеемблере. Задача: Найти максимум Х и Y т.е. другими словами вычислить какое число больше другого.

  1. IN; //ввод Х с клавиатуры
  2. ST X; //сохраняем Х в ОП
  3. IN; //ввод У с клавиатуры
  4. CMP Х; //из У вычитаем Х, разность нигде не сохраняется, формируя ПР
  5. JM L1; //переходим к шагу L1 если ПР <0
  6. JMP L2; //переходим к шагу L2 если ПР >=0
  7. L1: LD X; //загружаем в S значение Х
  8. L2: OUT; //вывод результата
  9. HALT; //остановка

максиум из двух чисел

Та же задача решенная не на ассемблере.

  1. 0000) 01 //вводим у
  2. 0001) 22 0050// сохраняем у
  3. 0004) 01 // вводим х
  4. 0005) 22 0051 //сохраняем х
  5. 0008) 11 0050 //х-у
  6. 0011) 34 0019 // если х-у<0 то перепрыгиваем
  7. 0014) 21 0050 // загр. х в сумматор
  8. 0017) 02 //вывод х
  9. 0018) 99
  10. 0019) 21 0051 // загрузка у в сумматор
  11. 0022) 02 // вывод у
  12. 0023) 99

Добрый вечер!

Прочитала все посты по МиК, стало более-менее понятно как в нём работать, спасибо! Буду пробовать решать задачи, и в ближайшее время выкладывать на сайт.

Добрый вечер.

В. Шахамболетович здравствуйте, что скажите по поводу задачки: Найти максимум из двух чисел, котоую я вчера вылжил?).

"а" в степени "x"

Здравствуйте. Валерий Шахамболетович извините что так давно не заходил, у меня сейчас проблемы с интернетом. Буду исправляться. Я написал программку, пожалуйста посмотрите и укажите на недостатки.
"а" в степени "x"

  1. in; //вводим а
  2. st a;//сохраняем
  3. st a1;//сохраняем
  4. in;//вводим x
  5. st x;//сохраняем
  6. la 1;//берем 1
  7. st w;//сохраняем
  8. ld x;//загружаем x
  9. sub w;//из x вычетаем 1
  10. st x;//сохраняем уменьшаемое
  11. m:la 1;//метка m.  берем 1
  12. st t;//сохраняем
  13. la 0;//берем 0
  14. st os;//обнуляем os
  15. st r;//обнуляем r
  16. p:ld a1;//метка p начало цикла нахождения  степени
  17. jz d;//проверка выхода из цикла
  18. ld os;//загружаем os
  19. add a;//
  20. st os;//накапливаем в os числа a
  21. ld a1;//загружаем счетчик
  22. sub t;//уменьшаем его
  23. st a1;//и сохраняем
  24. ld r;//загружаем r второй счетчик
  25. add t;//увеличеваем его
  26. st r; //и сохраняем
  27. jmp p;//переход на метку p
  28. d:ld os;//метка d. загружаем os=число а в квадрате
  29. st a;//меняем содержимое a
  30. ld r;//загружаем r
  31. st a1;//возобновляем счетчик
  32. la 0;//берем 0
  33. st r;//обнуляем r
  34. ld x;//загружаем х. степень которую ввел пользователь(счетчик)
  35. sub t;//уменьшаем счетчик
  36. st x;//и сохраняем
  37. jz fin;//проверка условия выхода из цикла
  38. jmp m;//переход на метку m
  39. fin: ld os;//метка fin. если все циклы окончены, загружаем результат
  40. out;//и выводим
  41. halt;//стоп

Как-то уж очень длинно,

Как-то уж очень длинно, мудрённо и крайне не мнемонично, не дочитал ...
Вот чуть получше:

  1. ДЛЯ НАТУРАЛЬНЫХ X И НЕОТРИЦАТЕЛЬНЫХ N ВЫЧИСЛЯТЬ Y=X^N:
  2. LD  1;
  3. ST  ONE; // для вычитания из N
  4. ST  Y;    //0-вая степень X c которой начинаем
  5. IN;
  6. ST  X;  // натуральное X
  7. IN;
  8. Loop1: ST  N; // нач. внешн. ц. ( домножение Y на X  N раз)
  9. JZ   Fin;
  10. LA  0;
  11. ST  P;  // для накопления произведения
  12. LD  X;  // исходный  X
  13. Loop2: ST  X1; // начало внутреннего  ц. (реализация P=Y*Х, добавлением Y к P  X раз)
  14. JZ  Cont; // если 0, то закончим внутр. ц.
  15. LD P;   // взяли P
  16. ADD   Y;
  17. ST  P; // добавили к P ещё раз Y
  18. LD  X1;
  19. SUB   ONE; // скорректировали счётчик внутреннего ц.
  20. JMP Loop2 // и ушли на его перезапись в начало ц.
  21. Cont: LD P;  // продолжили выполнение внешнего ц.
  22. ST Y;  // домножили Y на X
  23. LD    N;
  24. SUB  ONE;   // скорректировали счётчик внешнего ц.
  25. JMP  Loop1; // и ушли на его перезапись в начало ц.
  26. Fin: LD  Y; // вышли из внешн. ц., забираем результат
  27. OUT;
  28. HALT;

Надеюсь, не ошибся, хотя неплохо бы проверить.
А вообще, не следует писать такие длинные коды на Ассемблере. Цикл в цикле - явный перебор.
Учебной пользы для других практически 0, т.к. мало кто будет разбирать такие программы.

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

Второкурсники говорили, что мы будем изучать полный Ассемблер, а не Ассемблер МиК, на таком Ассемблере тоже не будет больших кодов значит?

Кстати, а когда у нас

Кстати, а когда у нас контрольная будет по МИК?

Спасибо

Спасибо..буду исправляться...

Извлечение квадратного корня из числа

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

Программа работает так: пользователь вводит число, если из этого числа извлекается корень, то выводится ответ.
А если не извлекается, то выводится 0.

  1. IN;//вводим число
  2. ST Q;//сохраняем его в q
  3. LA 1;//берем 1
  4. ST Z;//сохраняем ее в z
  5. LA 0;//берем 0
  6. ST Y;//обнуляем
  7. ST N;//обнуляем
  8. st l;//обнуляем
  9. R: LD N;//метка r, начинаем перебор чисел от 1 до q
  10. ADD Z;//накопление переменной
  11. ST N;//сохраняем
  12. ST K;//сохраняем
  13. ST B;//сохраняем
  14. LA 0;//берем 0
  15. ST Y;//обнуляем
  16. M: LD Y;//метка m, находим квадрат данного числа, берем переменную=0
  17. ADD N;//накапливаем ее
  18. ST Y;//сохраняем
  19. LD K;//счетчик: берем данное число
  20. SUB Z;//и уменьшаем его
  21. ST K;//сохраняем
  22. JZ G;//если квадрат найден, то переходим в метку g,
  23. JMP M;//если не найден, то переходим в метку m(цикл)
  24. G: LD Q;//метка g,сравнение  введенного числа и нашего числа,загружаем введенное
  25. пользователем число
  26. SUB Y;//и вычитаем из него квадрат нашего числа
  27. JZ FIN;//если они равны, то корень извлекается(переход на метку fin)
  28. jm fin2;//если их разность отрицательна, то корень не извлекается(переход на метку fin2)
  29. JMP R;//если их разность положительна, переходим в метку r(цикл)
  30. FIN:LD B;//метка fin, загружаем результат
  31. OUT;//и выводим его
  32. HALT;//стоп
  33. FIN2:LD l;//метка fin2, берем 0
  34. OUT;//и выводим его
  35. HALT;//стоп

Сначала чуть-чуть запутался

Сначала чуть-чуть запутался что к чему , а потом разобрался. Всё отлично работает.

Александр

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

Задача1,Задача2

//решение задачи 1
0004| 01 // Запрос уменьшаемого числа, введенное число записывается в сумматор (S) */
0005| 22 0000 // Запись уменьшаемого числа в ячейку памяти 0000 из сумматора (0000=S)*/
0008| 01 // Запрос вычитаемого числа, записывается введенное число в сумматор (S)*/
0009| 22 0002 // Запись вычитаемого числа в ячейку памяти 0002 из сумматора (0002=S)*/
0012| 21 0000 // Запись уменьшаемого числа из ячейки памяти 0000 в сумматор S (S=0000)*/
0015| 11 0002 // Вычитание из сумматора(в нем мы записали выше уменьшаемое) число из ячейки памяти 0002 (S=S-'0002';)
0018| 02 // Вывод результата на экран из сумматора S
0019| 99 // Конец подпрограммы(Стоп)

//решение задачи 2

0022| 01 // Запрос первого числа m, которое записывается в сумматор S
0023| 22 0020 // Запись числа m из сумматора(S) в ячейку 0020
0026| 01 // Запрос второго числа n, которое записывается в сумматор S
0027| 12 0020 // Вычитает из сумматора S число из ячейки 0020(m),т.е. n-m ( S=n, формурует ПР Z )
0030| 34 0036 // Если ПР (Z) отрицателен то переходит к команде с адресом 0036 иначе к команде 0033(т.е. следующей команде)
0033| 30 0039 // Безусловный переход к команде с адресом 0039,т.к. n-m>=0
0036| 21 0020 // В сумматор S записывает число из ячейки 0020(m) (S=m)
0039| 02 // Вывод на экран число сумматора S
0040| 99 // Конец подпрограммы(Стоп)

Молодец Илья!!

Я тоже разобралась))

Правильная вставка кода в пост

Для того чтобы правильно вставить код в пост, необходимо заключить его в теги < pre> и < /pre>(без пробелов после знака '<').
Например,
< pre> (без пробелов после '<')
0022| 01 // Запрос первого числа m, которое записывается в сумматор S
0023| 22 0020 // Запись числа m из сумматора(S) в ячейку 0020
...
< /pre> (без пробелов после '<')
Получается такой 'красиво' оформленный код.

  1.  0022| 01 // Запрос первого числа m, которое записывается в сумматор S
  2.  0023| 22 0020 // Запись числа m из сумматора(S) в ячейку 0020
  3.  ...

Илья.

Существенных замечаний нет.
Пара технических моментов:
1. Код вставлять необходимо с помощью тегов (см. пост Ширшова Н.)
2. Условие решаемой задачи лучше приводить явно (не все знают, да и не обязаны где-то искать).
Спасибо за труд!

Вячеслав Кушнарев аватар

Задача 2

У меня программа не работает если я беру первое положительное,а второе отрицательное