Как делаю я:
прежде всего, по-моему, сам подход "а можно и на С" - некорректный, плодит ваххабитов, которые потом с пеной у рта будут доказывать, что код асм оптимальнее и т.п, (потому что для конкретных задач это действительно так).
1. предполагается, что студенты уже знакомы с асм, вызовом ф-ий, обработкой прерываний и т.п.
2. Задание - написать на асм ф-ю с параметрами, которая возвращает значение.
типа: int add (int, int) и т.п. список составьте сами.
3. Рассмотреть используемость (usability) такой функции в реальном проекте: какие регистры использует, как сохраняет/восстанавливает эти регистры, как передает параметры, как возвращает значение, как используется стек, сколько используется стеков и т.п. может передача параметров через системные переменные, программная реализация стека и т.п.
4. Сформулировать необходимость принятия единых правил написания функций и их вызова для асм.
5. Задание - сформулировать эти правила.
6. Задание - на основе этих правил написать по две функции - одну, использующую ф-ю товарища, другую, используемую ф-ей другого товарища.
7. Сформулировать необходимость принятия единых правил использования глобальных переменных в ОЗУ для асм.
8. Задание - сформулировать эти правила.
9. Задание - написать функции, использующие глобальные переменные, можно тоже по две.
10. Показать, что эти правила, в общем, не оптимальны.
11. Теперь можно и к С переходить

или к Forth
хорошая книжка: "How the Pros Develop Embedded Software", из нее ясно, на что действительно стоит обращать внимание при обучении студентов