{"id":270,"date":"2016-05-28T22:45:16","date_gmt":"2016-05-28T20:45:16","guid":{"rendered":"http:\/\/tos.acoustic-velocity.com\/?p=270"},"modified":"2021-10-24T12:05:27","modified_gmt":"2021-10-24T10:05:27","slug":"tasks-schedules-and-priorities-2","status":"publish","type":"post","link":"https:\/\/acoustic-velocity.com\/?p=270","title":{"rendered":"Tasks, schedules and priorities (2)"},"content":{"rendered":"<p>Apparently I was creating more questions than answering them in my last post. I will try to provide a bit more detail about the implementation of the task scheduler.<\/p>\n<p><a href=\"https:\/\/acoustic-velocity.com\/wp-content\/uploads\/2016\/05\/interrupt-3.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-273 aligncenter\" src=\"https:\/\/acoustic-velocity.com\/wp-content\/uploads\/2016\/05\/interrupt-3.jpg\" alt=\"interrupt-3\" width=\"225\" height=\"225\" srcset=\"https:\/\/acoustic-velocity.com\/wp-content\/uploads\/2016\/05\/interrupt-3.jpg 225w, https:\/\/acoustic-velocity.com\/wp-content\/uploads\/2016\/05\/interrupt-3-150x150.jpg 150w\" sizes=\"(max-width: 225px) 100vw, 225px\" \/><\/a><\/p>\n<p>The interrupt routine decides, if it wants to schedule a new task. If the new task has a higher priority than the one that is currently running, we want to switch to the new task. So what we need to make sure is, that when we return from the interrupt we do not return to the old task, but to the new one. In order to achieve this, we need to manipulate the stack accordingly, as the return address (and some more information) is stored there. Let&#8217;s look at what happens when an interrupt is triggered:<\/p>\n<ol>\n<li>the currently executed instruction is finished<\/li>\n<li>the current program counter is pushed onto the stack, high byte first<\/li>\n<li>the status register is pushed onto the stack<\/li>\n<li>the interrupt flag is set (because you usually would not want your interrupt routine to be interrupted itself)<\/li>\n<\/ol>\n<p>All these steps are performed by the\u00a0hardware itself. An interrupt service routine normally\u00a0saves the registers\u00a0in software in addition:<\/p>\n<ol start=\"5\">\n<li>A is pushed onto the stack<\/li>\n<li>X is pushed onto the stack<\/li>\n<li>Y is pushed onto the stack<\/li>\n<\/ol>\n<p>How can we manipulate the stack now to return to a different task? We simply push respective data onto the stack!<\/p>\n<pre>\u00a0\u00a0\u00a0 lda #&gt;task\r\n\u00a0\u00a0\u00a0 pha\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ; push &gt;task\r\n\u00a0\u00a0\u00a0 lda #&lt;task\r\n\u00a0\u00a0\u00a0 pha\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ; push &lt;task\r\n\u00a0\u00a0\u00a0 lda #0\r\n\u00a0\u00a0\u00a0 pha\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ; push arbitrary states, IRQ flag must be clear\r\n\u00a0\u00a0\u00a0 pha\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ; push arbitrary A\r\n\u00a0\u00a0\u00a0 pha\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ; push arbitrary X\r\n\u00a0\u00a0\u00a0 pha\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ; push arbitrary Y<\/pre>\n<p>When the end of the interrupt service routine is reached, it will restore Y, X and A and the final\u00a0<em>rti<\/em> will pop the state register and jump to the new address we just pushed onto the stack! We do not have reasonable values for the registers (as the task was never executed before), so we simply chose 0. There is one caveat regarding the state register: we need to make sure that the interrupt flag is clear, or the interrupt will never be called again. It took me a while to realize this, as I was happily using <em>php<\/em> to push the current state register instead and as can be seen from step 4 above, that had the interrupt flag set.<\/p>\n<p>Now let&#8217;s have a look at the <em>task_done <\/em>routine that is jumped to by a task when it is finished. In theory, all we need to do is to jump to the task with the next highest priority. Let&#8217;s for now assume, that the old task has the next highest priority. Things get slightly messy now: we still have the remains of the interrupt on the stack, but tasks\u00a0are\u00a0always executed in the main loop! So\u00a0instead of jumping to the new task with <em>jmp<\/em>, we will misuse\u00a0<em>rti<\/em> and simply do the same as we would do at the end of an interrupt routine:<\/p>\n<pre>\u00a0\u00a0\u00a0 pla\r\n\u00a0\u00a0\u00a0 tay\r\n\u00a0\u00a0\u00a0 pla\r\n\u00a0\u00a0\u00a0 tax\r\n\u00a0\u00a0\u00a0 pla\r\n\u00a0\u00a0\u00a0 rti<\/pre>\n<p>Et voil\u00e1: we\u00a0restore\u00a0all registers and are back in the old task!<\/p>\n<p>There is one additional thing that needs to be organized: when a task is added with a lower priority than the currently executed one, it needs to be stored for later. To do this, a list with upcoming tasks needs to be kept. Every time a task is done, the list needs to be searched for the next highest priority and the task needs to be pushed onto the stack as described above. But I am going to leave this little detail to be worked out by the reader (ha, I always wanted to say this cruel sentence at some point in my life)!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Apparently I was creating more questions than answering them in my last post. I will try to provide a bit more detail about the implementation of the task scheduler. The interrupt routine decides, if it wants to schedule a new task. If the new task has a higher priority than the one that is currently &hellip; <a href=\"https:\/\/acoustic-velocity.com\/?p=270\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Tasks, schedules and priorities (2)<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/acoustic-velocity.com\/index.php?rest_route=\/wp\/v2\/posts\/270"}],"collection":[{"href":"https:\/\/acoustic-velocity.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/acoustic-velocity.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/acoustic-velocity.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/acoustic-velocity.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=270"}],"version-history":[{"count":9,"href":"https:\/\/acoustic-velocity.com\/index.php?rest_route=\/wp\/v2\/posts\/270\/revisions"}],"predecessor-version":[{"id":321,"href":"https:\/\/acoustic-velocity.com\/index.php?rest_route=\/wp\/v2\/posts\/270\/revisions\/321"}],"wp:attachment":[{"href":"https:\/\/acoustic-velocity.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=270"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/acoustic-velocity.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=270"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/acoustic-velocity.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=270"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}