Guide explicatif sur le chapitre 8 des notes de cours de
GABRINI, Ph. -- Organisation des ordinateurs et assembleur.

Chapitre 8. Structure, documentation et mise au point des programmes

Page 81.

Les objectifs actuels de la programmation sont la simplicité, la clarté et la fiabilité des programmes
ainsi que l'efficacité.


8.1.1 Clarté des programmes

Un programme sera clair s'il est bien structuré et si sa structure correspond bien à la solution logique
du problème. Le manque de structure correspond à ce qu'on appelle du "spaghetti logique", assez
difficile à démêler.

Pour un programme en assembleur on devrait voir apparaître les éléments
dans l'ordre suivant:

Commentaires explicatifs généraux sur le programme:
Déclarations des constantes (.EQUATE)
Code du programme principal
Réservation de mémoire et variables globales
Code des sous-programmes
.END


8.1.2 Efficacité des programmes

Une fois un programme écrit, on l'examine pour en améliorer l'efficacité. Ceci consiste surtout à
déterminer quelles sont les parties du code les plus exécutées et à essayer d'en améliorer l'écriture en
diminuant le nombre d'instructions.


8.2 Structures de base

On essaie d'utiliser les structures offertes par un langage évolué comme Java.


8.2.1 Boucles

WHILE

La vérification d'une condition s'effctue à l'entrée de la boucle.

WHILEnn: NOP0          ;while
         CPA N2,d      ; (N1 ≥ N2){
         BRLT ENDWHInn ;
         #CODE#        ; commentaires
         BR WHILEnn    ;
ENDWHInn: NOP0 ;}

où nn prendra une valeur numérique 10, 20, 30, 40, ...


REPEAT

L'instruction REPEAT est l'inverse de l'instruction WHILE: la vérification de la condition
est située en fin de boucle.

REPEATnn: NOP0          ;do{
          #CODE#        ; commentaires
          ADDX 4,i      ; augmenter indice
          CPA Table,x   ; vérifier élément suivant
UNTILnn:  BRLT REPEATnn ;}while(Table[i] > element


Boucles généralisées

Dans certains cas la condition déterminant la fin de la boucle peut être détectée au milieu des
instructions à répéter et les instructions WHILE et REPEAT s'avèrent malcommodes.

LOOPnn: NOP0         ;while(true){ [1 sortie]
        #CODE#       ; commentaires
        SUBX pas,d   ;
        BRGT ENDLPnn ;:>if(A > B) break;
        #CODE#       ; commentaires
        BR LOOPnn    ;
ENDLPnn: NOP0 ;}


Boucles FOR

De telles boucles se traduisent facilement par utilisation d’un compteur:

FORnn:    NOP0          ;for(Ctr = Inf; Ctr <= Sup; Ctr++){
          LDX Ctr,d     ; <Ctr == Sup>
FTESTnn:  NOP0          ;
          CPX Sup,d     ;
          BRGT ENDFORnn ;
          #CODE#        ; commentaires
          ADDX 2,i      ; le pas égal est égal à 2
          BR FTESTnn    ;
ENDFORnn: NOP0 ;}
 

8.2.2 Instructions conditionnelles

Instruction IF simple

IFnn:    NOP0                    ;if
         <évaluation expression> ; (condition)
         CPr opérandes           ;
         BR-- ENDIFnn            ;
THENnn:  NOP0                    ;{
         #CODE#                  ; commentaires
ENDIFnn: NOP0                    ;}
 

Instruction IF complète.

IFnn:    NOP0                 ;if
         <évaluer expression> ; (condition)
         CPr opérandes        ;
         BR-- ELSEnn          ;
THENnn:  NOP0                 ;{
         #CODE#               ; commentaires
         BR ENDIFnn           ;}
ELSEnn:  NOP0                 ;else {
         #CODE#               ; commentaires
ENDIFnn: NOP0                 ;}
 

Instructions IF imbriquées

En langage évolué, un énoncé IF peut être complexe du genre:

if(condition_1) {
   /*if condition_1 is true execute this*/
   statement(s);
}
else if(condition_2) {
   /* execute this if condition_1 is not met and
    * condition_2 is met
    */
   statement(s);
}
else if(condition_3) {
   /* execute this if condition_1 & condition_2 are
    * not met and condition_3 is met
    */
   statement(s);
}
.
.
.
else {
   /* if none of the condition is true
    * then these statements gets executed
    */
   statement(s);
}

Soit la structure suivante:

IF04:   NOP0        ;if(X > Z)
        LDA X,d     ;
        CPA Z,d     ;
        BRLE ELSE04 ;
THEN04: NOP0        ;{
        #CODE#      ; commentaires 25$
        BR ENDIF04  ;}
ELSE04: NOP0        ;else {
        #CODE#      ; commentaires 50$
IF05:   NOP0        ; if(Y > X)
        LDA Y,d     ;
        CPA X,d     ;
        BRLE ELSE05 ;
THEN05: NOP0        ; {
        #CODE#      ; commentaires +50$
        BR ENDIF05  ; }
ELSE05: NOP0        ; else {
IF06:   NOP0        ; if(Z < W)
        LDA Z,d     ;
        CPA W,d     ;
        BRGE ELSE06 ;
THEN06: NOP0        ; {
IF07:   NOP0        ; if(V == W)
        LDA V,d     ;
        CPA W,d     ;
        BRNE ENFIF07;
THEN07: NOP0        ; {
        #CODE#      ; commentaires +2000$
ENDIF07: NOP0       ; }
        BR ENDIF06  ; }
ELSE06: NOP0        ; else {
       #CODE#       ; commentaires +250$
ENDIF06: NOP0       ; }
       #CODE#       ; commentaires +150$
ENDIF05: NOP0       ; }
ENDIF04: NOP0       ;}

Supposons que ce programme calcule un montant accordé pour le bonus des Fêtes.

Dépendant des variables V, W, X, Y et Z, l'employé recevra un montant variant entre 25$ et 2050$.

Si l'employé devant recevoir 25$, reçoit un montant supérieur, il n'ira pas se plaindre de l'erreur.
Mais si l'employé devant recevoir 2050$, reçoit plutôt 100$, il se plaindra rapidement.

En tant qu'informaticien, on vous demande de rectifier "rapidement" cette anomalie.
Il devient compliqué d'avoir à effectuer une modification surtout si le programme n'est pas documenté adéquatement.
Vous devrez alors contacter le département de la paie pour essayer dans un premier temps de comprendre
le fonctionnement du calcul du bonus et de voir que représentent les variables V, W, X, Y et Z.

On vous expliquera la représentation de V(département), W(section), X(localisation), Y(niveau), Z(classe).
Ensuite, vous aurez à effectuer des tests avec des valeurs de test.

Finalement, vous allez découvrir que V=W est une condition essentielle et que le problème
est dû à un changement de section de certains employés. 
Vous allez modifier le programme afin que si V >= W, alors le bonus de 2050$ sera attribué.

IF07:   NOP0        ; if(V >= W)
        LDA V,d     ;
        CPA W,d     ;
        BRLT ENFIF07; <-----------------------------------------
THEN07: NOP0        ; {
        #CODE#      ; commentaires +2000$
ENDIF07: NOP0       ; }


8.2.3 Instruction SWITCH

Exemple de l'instruction SWITCH: prog823.txt

;
; SWITCH section 8.2.3 page 86
;
SWITCH10:NOP0                ;switch(Ind) {
         DECI    Ind,d      
         LDX     Ind,d       ;
         CPX     1,i         ;
         BRLT    OTHER10     ; Vérifier si Ind est
         CPX     5,i         ; dans les limites [1..5]
         BRGT    OTHER10     ;
         SUBX    1,i         ; ramener à [0..4]
         ASLX                ; * 2 => indice réel dans
         BR      TAB10,x     ; table (adresse = 2 octets)
TAB10:   .ADDRSS CAS10_1     ;
         .ADDRSS CAS10_2     ;
         .ADDRSS CAS10_3     ;
         .ADDRSS CAS10_4     ;
         .ADDRSS CAS10_5     ;
CAS10_1: NOP0                ; case 1:
         LDA     1,i        
         STA     code,d      ; commentaires
         BR      ENDCAS10    ; break;
CAS10_2: NOP0                ; case 2:
         LDA     2,i        
         STA     code,d      ; commentaires
         BR      ENDCAS10    ; break;
CAS10_3: NOP0                ; case 3:
         LDA     3,i        
         STA     code,d      ; commentaires
         BR      ENDCAS10    ; break;
CAS10_4: NOP0                ; case 4:
         LDA     4,i        
         STA     code,d      ; commentaires
         BR      ENDCAS10    ; break;
CAS10_5: NOP0                ; case 5:
         LDA     5,i        
         STA     code,d      ; commentaires
         BR      ENDCAS10    ; break;
OTHER10: NOP0                ; default:
         LDA     0,i        
         STA     code,d      ; commentaires
ENDCAS10:NOP0                ;}
         DECO    code,d     
         STOP               
code:    .BLOCK  2           ; #2d
Ind:     .BLOCK  2           ; #2d
         .END           
     

Supposons que l'Indice lu est 4.
La première étape consiste à vérifier si l'indice se trouve entre 1 et 5.
Sinon le branchement se fait à OTHER10 pour toutes les autres valeurs
et le "code" est mis à 0.
Pour s'aiguiller au bon endroit dans notre TAB10,
il faut effectuer un petit calcul:
l'indice 4 indique que nous devons passer par dessus 3 autres indices (SUBX 1,i => 4-1 => 3)
comme chaque adresse du tableau TAB10 occupe 2 octets, nous devons multiplier par 2 octets
le résultat de la soustraction précédente (ASLX => 3*2 => 6).
Finalement l'instruction BR TAB10,X (avec X=6) va nous permettre de faire un branchement à TAB10+x (TAB10+6 octets)
qui va nous amener à l'adresse de CAS10_4.
Cette section CAS10_4 assigne la valeur 4 à "code".
A la toute fin, un branchement s'effectue à ENDCAS10 pour afficher "code".

Exemple de l'instruction SWITCH modifié pour les 12 mois de l'année: prog823m.txt

;
; SWITCH section 8.2.3 page 86
;
; modifié pour les 12 mois de l'année
;
SWITCH12:NOP0                ;switch(Ind) {
         DECI    mois,d     
         LDX     mois,d      ;
         CPX     1,i         ;
         BRLT    OTHER12     ; Vérifier si Ind est
         CPX     12,i        ; dans les limites [1..12]
         BRGT    OTHER12     ;
         SUBX    1,i         ; ramener à [0..11]
         ASLX                ; * 2 => indice réel dans
         BR      TAB12,x     ; table (adresse = 2 octets)
TAB12:   .ADDRSS janvier     
         .ADDRSS février     
         .ADDRSS mars       
         .ADDRSS avril      
         .ADDRSS mai        
         .ADDRSS juin       
         .ADDRSS juillet    
         .ADDRSS août       
         .ADDRSS septembr   
         .ADDRSS octobre    
         .ADDRSS novembre   
         .ADDRSS décembre   
janvier: NOP0                ;
         CHARO   "J",i      
         BR      ENDCAS12    ; break;
février: NOP0                ;
         CHARO   "F",i      
         BR      ENDCAS12    ; break;
mars:    NOP0                ;
         CHARO   "M",i      
         BR      ENDCAS12    ; break;
avril:   NOP0                ;
         CHARO   "A",i      
         BR      ENDCAS12    ; break;
mai:     NOP0                ;
         CHARO   "M",i      
         BR      ENDCAS12    ; break;
juin:    NOP0                ;
         CHARO   "J",i      
         BR      ENDCAS12    ; break;
juillet: NOP0                ;
         CHARO   "J",i      
         BR      ENDCAS12    ; break;
août:    NOP0                ;
         CHARO   "A",i      
         BR      ENDCAS12    ; break;
septembr:NOP0                ;
         CHARO   "S",i      
         BR      ENDCAS12    ; break;
octobre: NOP0                ;
         CHARO   "O",i      
         BR      ENDCAS12    ; break;
novembre:NOP0                ;
         CHARO   "N",i      
         BR      ENDCAS12    ; break;
décembre:NOP0                ;
         CHARO   "D",i      
         BR      ENDCAS12    ; break;
OTHER12: NOP0                ; default:
         CHARO   "X",i      
ENDCAS12:NOP0                ;}
         STOP               
mois:    .BLOCK  2           ; #2d
         .END 
               

Un autre exemple de l'instruction SWITCH modifié pour les 12 mois de l'année
affichant le nombre de jours pour un mois sélectionné: prog823m31.txt

;
; SWITCH section 8.2.3 page 86
;
; modifié pour les 12 mois de l'année
; affiche le nombre de jours pour le mois désiré
;
SWITCH31:NOP0                ;switch(Ind) {
         DECI    mois,d     
         LDX     mois,d      ;
         CPX     1,i         ;
         BRLT    OTHER31     ; Vérifier si Ind est
         CPX     12,i        ; dans les limites [1..12]
         BRGT    OTHER31     ;
         SUBX    1,i         ; ramener à [0..11]
         ASLX                ; * 2 => indice réel dans
         BR      TAB31,x     ; table (adresse = 2 octets)
TAB31:   .ADDRSS jours31    
         .ADDRSS jours28    
         .ADDRSS jours31    
         .ADDRSS jours30    
         .ADDRSS jours31    
         .ADDRSS jours30    
         .ADDRSS jours31    
         .ADDRSS jours31    
         .ADDRSS jours30    
         .ADDRSS jours31    
         .ADDRSS jours30    
         .ADDRSS jours31    
jours28: NOP0                ; à modifier pour année bissextile
         LDA     28,i       
         STA     jours,d    
         BR      ENDCAS31    ; break;
jours30: NOP0                ;
         LDA     30,i       
         STA     jours,d    
         BR      ENDCAS31    ; break;
jours31: NOP0                ;
         LDA     31,i       
         STA     jours,d    
         BR      ENDCAS31    ; break;
OTHER31: NOP0                ; default:
         LDA     0,i        
         STA     jours,d    
ENDCAS31:NOP0                ;}
         DECO    jours,d    
         STOP               
mois:    .BLOCK  2           ; #2d
jours:   .BLOCK  2           ; #2d
         .END
                


8.3 Normes de programmation

On devra prévoir un guide d'utilisation ainsi que des documentations externes et internes pour faciliter
la compréhension d'un programme.
Le guide d'utilisation est simplement une description indiquant à un non programmeur comment utiliser
le système.
La documentation externe d'un système comprend la description de chaque module principal ainsi qu'une
explication plus générale de la façon dont ces diverses composantes communiquent pour former un système complet.


8.4 Exemples de programmes complets
 

8.4.1 Programme de multiplication rapide

par la méthode des moujiks (page 88)


8.4.2 Programme de tri

Ce programme effectue un tri de 20 valeurs.

for(int i = 19; i >=0; i--)
for(int j = i-1; j >= 0; j--)
if(Vec[j] > Vec[i])
Echanger(Vec[j],Vec[i]);

Exemple avec un tri simple de 5 valeurs:

3 8 4 5 2
3 8 4 2 5    échange
3 8 4 2 5
3 5 4 2 8    échange
3 5 4 2 8

3 5 4 2
3 5 2 4    échange
3 4 2 5    échange
3 4 2 5

3 4 2
3
2 4    échange
3 2 4

3 2
2 3    échange

2

Après les échanges: 2 3 4 5 8

Cela a requis: (4+3+2+1): 10 comparaisons

Pour un tableau de 20 éléments, cela demandera
(19+18+17+16+15+14+13+12+11+10+9+8+7+6+5+4+3+2+1): 190 comparaisons 

Cette méthode de tri est facile à comprendre mais inefficace.

Exemple du programme TRI: prog83.txt

8.4.3 Programme de recherche binaire

Il arrive très souvent que l'on ait à fouiller une table à la recherche d'un élément donné. La méthode
de fouille la plus intuitive est une fouille séquentielle du début de la table jusqu'à ce qu'on trouve
l'objet cherché ou que l'on atteigne la fin de la table. Ceci est acceptable lorsque la table fouillée est
courte. Il est parfois possible de conserver les éléments d'une table en ordre croissant ou décroissant.


8.5 Mise au point des programmes

Le premier principe que l'on peut indiquer est qu'avant qu'un programme ne soit soumis à sa première
vérification, il doit être listé et relu attentivement.

Le second principe indique qu'un programme doit être découpé en parties qui remplissent chacune une
fonction spécifique.

Le troisième principe est de déterminer exactement la raison d'une fin anormale de l'exécution du
programme.

Un quatrième principe sera d'isoler l'erreur.

Un dernier principe de mise au point est celui de retracer ce qui s'est passé à partir de l'erreur et
de tracer le programme avec  "Le débogueur" et "le vidange mémoire (memory dump)".