{"id":3447,"date":"2020-05-24T15:50:26","date_gmt":"2020-05-24T13:50:26","guid":{"rendered":"https:\/\/ceo.oric.org\/?p=3447"},"modified":"2020-05-24T15:50:26","modified_gmt":"2020-05-24T13:50:26","slug":"temporisation-en-langage-machine","status":"publish","type":"post","link":"https:\/\/oric.bqtt.net\/index.php\/2020\/05\/24\/temporisation-en-langage-machine\/","title":{"rendered":"Temporisation en langage machine"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>par Andr\u00e9 C.<\/em> avec l\u2019aide de Christian L.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Cet article a \u00e9t\u00e9 corrig\u00e9 gr\u00e2ce aux indications pr\u00e9cieuses de Christian L. &#8220;Assinie&#8221; concernant l&#8217;interf\u00e9rence des interruptions dans la routine TEMPOB. Qu&#8217;il en soit cordialement remerci\u00e9.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La commande Basic WAIT est une petite merveille de simplicit\u00e9 d&#8217;utilisation. Il suffit d&#8217;indiquer une valeur de temporisation en centi\u00e8mes de secondes et pourvu que les IRQ ne soient pas inhib\u00e9es, l&#8217;ex\u00e9cution du programme se fige pendant le d\u00e9lai indiqu\u00e9. La valeur du d\u00e9lai indiqu\u00e9 doit \u00eatre comprise entre 0 et 65535 (en pratique entre 1 \u00e0 65535), ce qui permet des temporisations de 0,1 \u00e0 655 secondes (presque 11 min donc). C&#8217;est plus qu&#8217;il n&#8217;est utile.<br>La pr\u00e9cision de WAIT est \u00e9tonnamment bonne. Mais il n&#8217;est pas possible d&#8217;obtenir des d\u00e9lais tr\u00e8s brefs, car la valeur minimale est de 100 \u00b5s, ce qui est beaucoup compar\u00e9 aux 2 \u00b5s qu&#8217;il est possible d&#8217;obtenir en langage machine, en ins\u00e9rant un NOP dans un programme. En effet NOP (No OPeration) ne fait rien, mais \u00e7a prend 2 cycles de microprocesseur, soit 2 \u00b5s dans le cas de l&#8217;Oric. Moi quand je ne fais rien, \u00e7a prend quand m\u00eame plus de temps, heureusement \u00e7a n&#8217;arrive pas souvent !<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Temporisation \u00e0 boucle<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dans la plupart des programmes LM ainsi que dans les routines de la Rom, les temporisations sont bas\u00e9es sur des boucles. Mais vous verrez qu&#8217;il y a peut-\u00eatre mieux \u00e0 faire.<br>Voici le principe d&#8217;une temporisation \u00e0 boucle :<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/oric.bqtt.net\/wp-content\/uploads\/2020\/05\/Fig1-1024x150.gif\" alt=\"\" class=\"wp-image-3449\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">C&#8217;est simple non ? Mais si on veut calculer le d\u00e9lai correspondant \u00e0 cette valeur 5, ce n&#8217;est pas si facile. En effet, il faut tenir compte du nombre de cycles de microprocesseur que prend chaque instruction.<br>De plus, tr\u00e8s souvent ce nombre de cycle n&#8217;est pas une valeur fixe, mais d\u00e9pend du contexte. Ainsi si LDX et DEX sont des cas simples car n\u00e9cessitant toujours 2 cycles chacun, c&#8217;est plus compliqu\u00e9 avec BNE o\u00f9 trois cas peuvent se pr\u00e9senter :<br>\u25cf 2 cycles si le branchement n&#8217;a pas lieu.<br>\u25cf 3 cycles si le branchement a lieu (cas normal pendant la boucle).<br>\u25cf 4 cycles si le branchement a lieu avec un changement de page m\u00e9moire.<br>Si on fait un bilan pour l&#8217;exemple ci-dessus, on a 2 cycles pour LDX, plus 4 fois 2+3 cycles (2 pour DEY + 3 pour BNE avec branchement) plus 1 fois 2+2 cycles (2 pour DEY + 2 pour BNE sans branchement) on arrive \u00e0 un total de 26 cycles, soit 26 \u00b5s pour l&#8217;Oric, qui tourne \u00e0 1 MHz. Le calcul pr\u00e9c\u00e9dent peut se formuler ainsi : Le d\u00e9lai vaut D = 5Y -1 + 2 ( 1 pour le dernier BNE sans branchement et +2 pour le LDX initial).<br><strong>Le d\u00e9lai maximum pour une simple boucle est obtenu avec Y=#FF et se limite \u00e0 environ 1,3 ms<\/strong>. (D&nbsp;=&nbsp;256&#215;5 -1 + 2 = 1281 \u00b5s). C&#8217;est exactement ce code que l&#8217;on trouve dans la Rom en #FAE1 pour la dur\u00e9e de ZAP.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Temporisation \u00e0 2 boucles<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Si on veut obtenir un d\u00e9lai plus long, il faut imbriquer deux boucles. C&#8217;est ce qu&#8217;a fait Fabrice Broche pour OUPS, le nouveau son pr\u00e9programm\u00e9 du Telestrat :<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/oric.bqtt.net\/wp-content\/uploads\/2020\/05\/Fig2-1024x241.gif\" alt=\"\" class=\"wp-image-3450\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Essayons de calculer le d\u00e9lai produit par l&#8217;ex\u00e9cution de cette routine :<br>\u25cf Pour les LDY et LDX d&#8217;initialisation : 4 cycles.<br>\u25cf Pour la 1e boucle : D = 256&#215;5 &#8211; 1 = 1279 cycles (-1 pour le dernier BNE de la 1e boucle). Notez que cette 1e boucle se termine avec X=0 donc X est r\u00e9g\u00e9n\u00e9r\u00e9 \u00e0 sa valeur initiale pour la suite.<br>\u25cf Pour la 2e boucle : D= 96(1279+5) -1 = 123263 cycles (avec 1279 pour l&#8217;ex\u00e9cution de la 1e boucle et -1 pour le dernier BNE sans branchement de la 2e boucle). Notez que le temps d&#8217;ex\u00e9cution d&#8217;un tour de la 2e boucle inclus celui de l&#8217;ex\u00e9cution totale de la 1e boucle.<br>Au total on a donc 123267 cycles, soit 123267 \u00b5s pour l&#8217;Oric. La dur\u00e9e th\u00e9orique finale de OUPS est donc de 0,123 s (sauf si je me suis plant\u00e9). On voit aussi que l&#8217;on peut simplifier ce calcul car il y a quelques cycles n\u00e9gligeables, par exemple les LDY et LDX initiaux et la r\u00e9duction de 1 cycle pour BNE sans branchement. On arrive alors \u00e0 un d\u00e9lai D = 96(1280+5) = 123360 \u00b5s, soit une approximation de +0,08%.<br><strong>Le d\u00e9lai maximum que l&#8217;on puisse obtenir avec 2 boucles avec X=#FF et Y=#FF est d&#8217;environ 0,33 ms.<\/strong><br>Le <em>calcul simplifi\u00e9<\/em> donne D = 256(1280+5)=328960 \u00b5s. \u00c7a risque de n&#8217;\u00eatre pas assez dans certains cas. On pourrait ajouter quelques NOPs (2 cycles \u00e0 chaque fois) dans la 1e boucle qui est ex\u00e9cut\u00e9e 256&#215;256=65536 fois. L&#8217;ex\u00e9cution s&#8217;allongerait alors de 65536&#215;2=131072 cycles par NOP ajout\u00e9, c&#8217;est-\u00e0-dire 0,13 s, ce qui n&#8217;est pas n\u00e9gligeable. Mais, par exemple, pour atteindre un d\u00e9lai total de 1 s, il faudrait 5 NOP (131072 x 5 = 655360 cycles suppl\u00e9mentaires plus 328960 cycles initiaux = 984320 cycles, soit environ 0,98 s). Mais il ne serait pas raisonnable d&#8217;aller plus loin en accumulant les NOP.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Temporisation \u00e0 3 boucles<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Si 1 s ne vous suffit pas, il faut ajouter une 3e boucle en utilisant le 3e registre du 6502 : L&#8217;accumulateur A (il n&#8217;existe pas de DEA pour d\u00e9cr\u00e9menter A, mais c&#8217;est facilement contournable). La temporisation maximale possible est tellement grande que l&#8217;on peut ajuster finement les valeurs respectives de A, Y et X pour obtenir un d\u00e9lai en chiffre rond. En voici un exemple pour obtenir un d\u00e9lai de 40 s. J&#8217;ai choisi cette dur\u00e9e afin de chronom\u00e9trer la dur\u00e9e obtenue.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/oric.bqtt.net\/wp-content\/uploads\/2020\/05\/Fig3-2-1-1024x600.gif\" alt=\"\" class=\"wp-image-3472\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Cette structure des boucles est n\u00e9cessit\u00e9e par la r\u00e9initialisation des compteurs au d\u00e9but de chaque boucle. A l&#8217;issue de la 1e boucle, X est automatiquement r\u00e9initialis\u00e9 \u00e0 z\u00e9ro. Toutefois, cette remise \u00e0 z\u00e9ro est perdue dans le cas de l&#8217;ex\u00e9cution de la 3e boucle et doit \u00eatre repositionn\u00e9e. D&#8217;autre part, l&#8217;ex\u00e9cution de la 2e boucle se termine avec Y = z\u00e9ro. Si l&#8217;on veut utiliser une valeur initiale de Y diff\u00e9rente de z\u00e9ro, il faut \u00e9galement repositionner Y. C&#8217;est pourquoi la 3e boucle ne se termine pas par un BNE BOUCL1, mais avec un BNE BOUCL3.<br>En faisant abstraction de la dur\u00e9e r\u00e9duite des BNE de fin de boucle et de 6 cycles pour les LDA, LDY et LDX initiaux, on peut faire le <em>calcul simplifi\u00e9<\/em> suivant&nbsp;:<br>\u25cf L&#8217;ex\u00e9cution de la boule X dure D = 256&#215;5 = 1280&nbsp;\u00b5s.<br>\u25cf L&#8217;ex\u00e9cution de la boucle Y dure D = 156(1280+5) = 200460 \u00b5s soit environ 0,20 s<br>\u25cf L&#8217;ex\u00e9cution de la boucle A dure D = 200(200460+9) = 40093800 \u00b5s soit environ 40&nbsp;s.<br>En ajustant les valeurs de Y et A il est possible d&#8217;obtenir la dur\u00e9e dont on a besoin. Il suffit de faire quelques calculs avec les formules ci-dessus.<br><strong>Dur\u00e9e maximale possible avec une temporisation \u00e0 3 boucles (hors ajout de NOPs) : 84 s<\/strong><br>\u25cf L&#8217;ex\u00e9cution de la boule X dure D = 256&#215;5 = 1280&nbsp;\u00b5s.<br>\u25cf L&#8217;ex\u00e9cution de la boucle Y dure D = 256(1280+5) = 328960 \u00b5s soit environ 0,32 s<br>\u25cf L&#8217;ex\u00e9cution de la boucle A dure D = 256(328960+9) = 84216064 \u00b5s soit environ 84&nbsp;s.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Temporisation avec le Timer 2<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Vous vous rappelez la simplicit\u00e9 de mise en \u0153uvre de la commande Basic WAIT ? Si on consulte &#8220;L&#8217;Oric \u00e0 nu&#8221; page 186, on peut examiner ce que fait cette routine. Le param\u00e8tre qui suit WAIT (nombre de centi\u00e8mes de secondes de d\u00e9lai) est analys\u00e9, puis plac\u00e9 en page z\u00e9ro aux adresses #33 (LL de a valeur) et #34 (HH de la valeur). Le registre A est initialis\u00e9 avec #02 (n\u00b0 du timer \u00e0 utiliser), puis le registre Y re\u00e7oit le contenu de la m\u00e9moire #33 et X celui de #34. La routine #EEAB \u00e9crit la valeur YX dans le timer 2, lequel est d\u00e9cr\u00e9ment\u00e9 tous les centi\u00e8mes de secondes. On lit l&#8217;\u00e9tat du timer 2 avec la routine #EE9D et on sort quand il tombe \u00e0 z\u00e9ro.<br>Voici un exemple qui permet d&#8217;obtenir un d\u00e9lai de 40 s :<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/oric.bqtt.net\/wp-content\/uploads\/2020\/05\/Fig4-1024x446.gif\" alt=\"\" class=\"wp-image-3452\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Quelques mesures<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Pour v\u00e9rifier les exemples, j&#8217;ai compar\u00e9 ce que donnent WAIT 4000, TEMPOB et TEMPOT avec un Atmos r\u00e9el, un Atmos sous Euphoric+DosBox et un Atmos sous Oricutron 1.2. Voici les r\u00e9sultats :<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><\/td><td>Atmos r\u00e9el<\/td><td>Euphoric<\/td><td>Oricutron<\/td><\/tr><tr><td>WAIT 4000<\/td><td>40s<\/td><td>42s<\/td><td>41s<\/td><\/tr><tr><td>TEMPOB<\/td><td>40s<\/td><td>42s<\/td><td>41s<\/td><\/tr><tr><td>TEMPOT<\/td><td>40s<\/td><td>42s<\/td><td>41s<\/td><\/tr><\/tbody><\/table><figcaption>Temporisations mesur\u00e9es<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">L&#8217;Atmos donne les valeurs escompt\u00e9es pour WAIT 4000, TEMPOB et TEMPOT. Par contre Oricutron donne des valeurs l\u00e9g\u00e8rement plus \u00e9lev\u00e9es, mais qui restent dans la limite de la pr\u00e9cision de mon chronom\u00e9trage. Pour Euphoric, c&#8217;est encore plus marqu\u00e9, mais peut-\u00eatre est-ce d\u00fb \u00e0 DosBox. J&#8217;ai d\u00e9j\u00e0 remarqu\u00e9 ce d\u00e9faut de r\u00e9activit\u00e9 avec DosBox dans d&#8217;autres situations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">La temporisation avec le timer 2 est plus facile \u00e0 mettre en \u0153uvre que les temporisations \u00e0 boucles et devrait \u00eatre utilis\u00e9e pr\u00e9f\u00e9rentiellement pour les d\u00e9lais de 0,1 \u00e0 655 s (comme WAIT). Il n&#8217;en reste pas moins que pour les petits d\u00e9lais une simple boucle permet de temporiser jusqu&#8217;\u00e0 1281 \u00b5s (et \u00e9ventuellement deux boucles pour aller jusqu&#8217;\u00e0 0,33 s). Il n&#8217;y donc que l&#8217;embarras du choix quant \u00e0 la m\u00e9thode \u00e0 utiliser !<\/p>\n\n\n\n<div class=\"wp-block-file\"><a href=\"https:\/\/oric.bqtt.net\/wp-content\/uploads\/2020\/05\/TEMPORIS.zip\">Disquette TEMPORIS.DSK<\/a><a href=\"https:\/\/oric.bqtt.net\/wp-content\/uploads\/2020\/05\/TEMPORIS.zip\" class=\"wp-block-file__button\" download>T\u00e9l\u00e9charger<\/a><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>par Andr\u00e9 C. avec l\u2019aide de Christian L. Cet article a \u00e9t\u00e9 corrig\u00e9 gr\u00e2ce aux indications pr\u00e9cieuses de Christian L. &#8220;Assinie&#8221; concernant l&#8217;interf\u00e9rence des interruptions dans la routine TEMPOB. Qu&#8217;il en soit cordialement remerci\u00e9. La commande Basic WAIT est une petite merveille de simplicit\u00e9 d&#8217;utilisation. Il suffit d&#8217;indiquer une valeur de temporisation en centi\u00e8mes de [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3452,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[338,346,347,22],"tags":[671,867],"class_list":["post-3447","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-338","category-assembleur","category-atmos","category-ceomag","tag-ceomag361","tag-routines-lm"],"_links":{"self":[{"href":"https:\/\/oric.bqtt.net\/index.php\/wp-json\/wp\/v2\/posts\/3447","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/oric.bqtt.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/oric.bqtt.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/oric.bqtt.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/oric.bqtt.net\/index.php\/wp-json\/wp\/v2\/comments?post=3447"}],"version-history":[{"count":0,"href":"https:\/\/oric.bqtt.net\/index.php\/wp-json\/wp\/v2\/posts\/3447\/revisions"}],"wp:attachment":[{"href":"https:\/\/oric.bqtt.net\/index.php\/wp-json\/wp\/v2\/media?parent=3447"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oric.bqtt.net\/index.php\/wp-json\/wp\/v2\/categories?post=3447"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oric.bqtt.net\/index.php\/wp-json\/wp\/v2\/tags?post=3447"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}