Sommaire: dernière maj : le 01/06/2007 1 - Préface Vous allez peut être me dire quel est le rapport entre les opérations réalisées avec le Z80, la gestion de la pile et les registres spéciaux ? Et bien nous allons étudier dans cet article l'utilisation de la pile pour la création de fonctions avec passage de paramètres. Les registres spécialisés quand à eux, seront utilisés dans les fonctions de manière à préserver le contenu des registres courant qui utilisés dans vos programmes. Dans cet article nous allons reprendre des exemples qui ont été étudiés dans l'article précédent «Les multiplications et divisions avec le Z80», je vous conseille de vous reporter à cet article si vous débutez en assembleur. Je vous souhaite une bonne lecture … 2 - Rappel de l'utilisation de la pile 2.1 Rappel sur la pile La pile est une zone mémoire qui permet de stocker, par exemple, la valeur de registres, les adresses de retour lorsque vous faites appel à des procédures ou fonctions, ou d'éventuelles données temporaires. L'emplacement de cette zone mémoire est stocké dans le registre SP, qui est un registre de 16 bits, et qui sert de pointeur de pile. Lorsque vous concevez un programme la première chose à faire dans ce programme, est de définir l'emplacement de la pile et sa taille. La taille de celle-ci va bien sûr dépendre de son utilisation, à savoir : l'utilisation des instructions PUSH et POP, par exemple, ou du nombre d'appels imbriqués dans des procédures. 2.2 Déclaration de la pile en assembleur ;**************************************** ; Exemple de déclaration de la pile ;ORG &4000 ; Emplacement de programme en mémoire RUN &4000 ; Lancement automatique du programme, pour info! (sous WinAPE) START LD SP, STACK + STACK_SIZE – 1 ; SP = [Adresse de STACK] + [Taille de la pile] - 1 . . . STACK_SIZE EQU 40 ; Taille de la pile en octet STACK DEFS STACK_SIZE ; Emplacement de la pile de 40 octets END Remarque : Si vous utilisez WinAPE pour vos développements, je vous conseille de ne modifier la pile qu'à partir du moment où votre programme est achevé. WinAPE gère par défaut la pile à l'adresse &BFEx. 2.3 Exemple d'utilisation de la pile Nous allons voir rapidement comment travailler avec la pile. Prenons un exemple, imaginons que l'adresse de la pile soit fixée à l'adresse &3010 et d'une taille de 16 octets. ;*************************************** ; Exemple A : Utilisation de la pile ; ; Adresse de la pile fixée en &3010 ; LD SP, &3010 ; On fixe l'adresse de la pile ; L'adresse de la pile ? &3010LD DE, 5 ; On va stocker 5 sur la pile PUSH DE ; La valeur contenu dans DE se trouve maintenant sur la pile ; à l'adresse (SP – 2) => SP = &3008 LD DE, 3 ; On stocke maintenant 2 sur la pile PUSH DE ; Le contenu de DE se trouve maintenant sur la pile ; à l'adresse (SP – 4) => SP = &3006 POP BC ; Récupération de 3 dans BC ; SP a augmenté de 2 car la valeur 3 a été dépilée ; => SP = &3008 POP BC ; Récupération de la valeur 5 de la pile ; SP a été incrémenté de 2 ; => SP = &3010 (adresse initiale) « Je pense que vous avez compris facilement comment on utilise la pile ! » Dans certain cas, dans vos programmes, vous aurez à consulter des données sans toucher à la pile. Le moyen le plus simple est d'accéder directement à l'adresse de ces données via un adressage indexé sans modifier SP, exemple : ;*************************************** ; Exemple 2 : Utilisation de la pile ; ; Adresse de la pile fixée en &3010 ;LD SP, &3010 ; On fixe l'adresse de la pile ; L'adresse de la pile <=> &3010 LD IX, 0 ; Copie de l'adresse de SP dans le registre d'indexe IX ADD IX, SP ; LD DE, 5 ; On va stocker 5 sur la pile PUSH DE ; La valeur contenu dans DE se trouve maintenant sur la pile ; à l'adresse (SP – 2) => SP = &3008 LD DE, 3 ; On stocke maintenant 2 sur la pile PUSH DE ; Le contenu de DE se trouve maintenant sur la pile ; à l'adresse (SP – 4) => SP = &3006 LD B, (IX – 1) ; On transfert le contenu de (SP – 1) dans B LD C, (IX – 2) ; On transfert le contenu de (SP – 2) dans C ; => BC = 5 LD H, (IX – 3) ; On transfert le contenu de (SP – 3) dans H LD L, (IX – 4) ; On transfert le contenu de (SP – 4) dans L ; => HL = 3 Ce qu'il faut ne faut pas perdre de vu c'est qu'à chaque lecture de la pile via un adressage indéxé la pile reste intacte, au contraire lorsque l'on récupère une valeur empilée via l'instruction POP la pile est dépilée, SP est ré-incrémenté de 2.
3 – Création de fonctions Nous allons maintenant voir comment exploiter la pile pour créer des fonctions avec passage de paramètres. Certains préfèreront l'utilisation des registres pour passer directement des paramètres, cependant dans certains cas il est plus facile d'utiliser la pile. 3.1 Exemple d'utilisation de la pile Voyons un exemple concret … Exemple : Création d'une fonction d'addition avec 2 paramètres sur 16 bits, le résultat est renvoyé via le registre de données DE. ;************************************************* ; Addition de 2 valeurs sur 16 bits ; ; LD HL, 4 PUSH HL LD BC, 10 PUSH BC CALL MATH_ADD_SP_16x16 ; En retour DE va contenir HL + BC . . . ;************************************************* ; Fonction d'addition de 2 entiers sur 16 bits ; ; En entrée ; [SP – 2] <=> Paramètre 1 ; [SP – 4] <=> Paramètre 2 ; En sortie ; DE <=> [SP – 2] + [SP – 4] ; Registres affectés ; F, DEMATH_ADD_SP_16x16 : EXX ; Sauvegarde des registres BC, DE, HL POP BC ; Récupération de l'adresse de retour POP DE ; Récupération du premier paramètre POP HL ; Récupération du second paramètre PUSH BC ; Sauvegarde de l'adresse de retour sur la pile ADD HL, DE PUSH HL EXX ; Restauration des registres BC, DE, HL POP DE ; Récupération du résultat de l'opération RET Si vous regardez bien cet exemple qui ne peut pas être plus simple, vous constatez que l'on a utilisé les registres spécialisés avant l'opération. En effet pour préserver la valeurs des registres courant vous pouvez prendre pour habitude d'utiliser les registres spécialisés dans vos fonctions cela évite en plus une gestion plus complexe de la pile. Seulement c'est à vous de faire attention aux imbrications de fonctions !! 3.2 Fonctions avec passage de 2 paramètres de 8 bits Voyons maintenant comment créer une fonction avec 2 paramètres de 8 bits. Comme vous vous en doutez certainement nous allons empiler sur la pile un seul registre de 16 bits : Exemple : ;************************************************* ; Soustraction de 2 valeurs de sur 8 bits ; ; LD D, 10 LD E, 4 PUSH DE CALL MATH_SUB_SP_8x8 ; En retour A va contenir D - E . . .;************************************************* ; Fonction de soustraction de 2 entiers de 8 bits ; ; En entrée ; [SP – 1] <=> Paramètre 1 ; [SP – 2] <=> Paramètre 2 ; En sortie ; A <=> [SP – 1] - [SP – 2] ; Registres affectés ; AF MATH_SUB_SP_8x8 : EXX ; Sauvegarde des registres BC, DE, HL POP HL ; Récupération de l'adresse de retour POP DE ; Paramètres 1 et 2 PUSH HL ; Préservation de l'adresse de retour LD A, D ; 10 dans A SUB E ; On enlève 4 => reste dans A EXX ; Restauration des registres BC, DE, HL RET Ici, l'utilisation d'une telle fonction n'est pas nécessaire, elle sert juste d'exemple, il en va de même pour l'utilisation des registres spécialisés. Je vous conseille de faire attention à l'utilisation de fonctions dans vos programmes, une démo par exemple, car une utilisation trop fréquente de la pile peut s'avérer très couteuse en cycles processeur. C'est à vous de juger si les paramètres d'une fonction doivent être passés par registre ou par la pile ! Je pense que vous avez compris maintenant le rapport entre les opérations du Z80, la pile et les registres spécialisés. Pour conclure … le dernier chapitre reprend les fonctions de multiplication et division de l'article précédent « Les multiplications et divisions entières avec le Z80 », si vous ne l'avez pas lu je vous conseille vivement d'y consacrer un peu de temps. Les passages de paramètres par registres ont été remplacés par une gestion de la pile. Le mois prochain nous allons étudier le tracé de ligne, @très bientôt … 4 – Bibliothèque de méthodes 4.1 Les multiplications par retenues 4.1.1 Multiplication de deux entiers non signés de 8 bits, résultat sur 16 bits ;*************************************************************** ; Routine de multiplication de 2 entiers non signés, de 8 bits ; avec un résultat sur 16 bits ; ; En entrée : ; [SP – 1] <=> Multiplicande (A) ; [SP – 2] <=> Multiplicateur (B) ; [SP – 4] <=> Adresse de retour ; En sortie : ; DE <=> Produit de A par B ; Registres affectés ; DE, les registres spécialisés ; Remarques ; - Si A ou B = 0 => DE = 0 en sortie ; - [SP – 1] et [SP – 2] sont dépilés ; - Vous pouvez utiliser n'importe quel registre 16 bits en sortie ; ; - Exemple de méthode d'appel ; ; LD B, 5 ; LD C, 2 ; PUSH BC ; CALL MATH_MUL_SP_8x8 ; . ;MATH_MUL_SP_8x8: EXX ; Sauvegarde des registres BC, DE, HL EX AF, AF' ; Sauvegarde de AF ; Récupération des paramètres POP HL ; Sauvegarde l'adresse de retour POP BC ; Récupération des deux paramètres PUSH HL ; Sauvegarde l'adresse de retour ; Initialisation des registres pour le calcul LD HL, 0 ; Valeur de retour LD A, B ; Transfert du multiplicande dans A ; Le multiplicateur se trouve dans C ; Vérification des valeurs en entrée OR A ; Test de A ; Si A = 0 on sort JP z, _MATH_MUL_SP_8x8_END LD L, A ; Sauvegarde de A pour le test de C LD A, C ; C est transféré dans A pour le test OR A ; Test de C via A LD A, L ; On restore A LD L, H ; On efface L via H ; Si A(C) = 0 on sort JP z, _MATH_MUL_SP_8x8_END ; Début de code lié à la multiplication LD D, L ; D est effacer par L LD E, A ; Le multiplicande va dans E LD H, C ; Le multiplicateur va dans H LD B, 8 ; Traitement des 8 bits _MATH_MUL_SP_8x8_1 ; Multiplication par 2 de HL (décalage d'1 bit vers le haut) ADD HL, HL JR nc, _MATH_MUL_SP_8x8_2 ; Remplacement du bit de débordement par le multiplicateur ADD HL, DE _MATH_MUL_SP_8x8_2 DJNZ _MATH_MUL_SP_8x8_1 _MATH_MUL_SP_8x8_END PUSH HL ; Sauvegarde du résultat EXX ; Restauration des registres courants EX AF, AF' ; Restauration de AF POP DE ; Récupération du résultat dans DE (à changer éventuellement) RET 4.1.2 Multiplication de deux entiers non signés de 16 par 8 bits, résultat sur 16 bits ;*************************************************************** ; Routine de multiplication de 2 entiers non signés, de 16 par ; 8 bits, avec un résultat sur 16 bits ; ; En entrée ; [SP - 2] <=> Multiplicande, transféré dans DE ; [SP - 4] <=> Multiplicateur, transféré dans A (partie haute de [SP - 4]) ; [SP – 6] <=> Adresse de retour ; En sortie : ; DE <=> Produit de DE par A (résultat sur 16 bits !) ; Registres affectés ; DE et les registres spécialisés ; Remarques ; - Si A ou DE = 0 => DE = 0 en sortie ; - [SP - 2] et [SP - 4] sont dépilés ; - Vous pouvez utiliser n'importe quel registre 16 bits en sortie ; ; - Méthode d'appel de la fonction ; ; LD DE, 6 ; PUSH DE ; LD D, 2 ; D contient le multiplicateur ; PUSH DE ; CALL MATH_MUL_SP_16x8 ; . ;MATH_MUL_SP_16x8: EXX ; Sauvegarde des registres BC, DE, HL EX AF, AF' ; Sauvegarde de AF ; Récupération des paramètres POP HL ; Récupération de l'adresse de retour POP AF ; Récupération du multiplicateur ; Le multiplicateur va se retrouvé dans A POP DE ; Récupération du multiplicande PUSH HL ; Préservation de l'adresse de retour ; Initialisation des registres LD HL, 0 ; Valeur de retour ; Vérification des valeurs d'entrée OR A ; Si A = 0 on sort JP z, _MATH_MUL_SP_16x8_END ADC HL, DE ; Ajout de DE à HL de manière à vérifier si DE = 0 ; Si DE = 0 => z = 1 JP z, _MATH_MUL_SP_16x8_END ; Début du code lié à la multiplication LD HL, 0 ; Valeur de retour LD B, 8 ; Traitement des 8 bits _MATH_MUL_SP_16x8_1 ; Multiplication par 2 de HL (décalage d'1 bit vers le haut) ADD HL, HL ADD A, A JR nc, _MATH_MUL_SP_16x8_2 ; Remplacement du bit de débordement par le multiplicateur ADD HL, DE _MATH_MUL_SP_16x8_2 DJNZ _MATH_MUL_SP_16x8_1 _MATH_MUL_SP_16x8_END PUSH HL ; Sauvegarde du résultat EXX ; Restauration des registres courants BC, DE, HL EX AF, AF' ; Restauration de AF POP DE ; Récupération du résultat dans DE ; (Au tout autre registre 16 bits) RET 4.1.3 Multiplication de deux entiers non signés de 16 bits, résultat sur 16 bits ;*************************************************************** ; Routine de multiplication de 2 entiers non signés de 16 bits, ; avec un résultat sur 16 bits ; ; En entrée ; [SP - 2] <=> Multiplicande, transféré dans BC ; [SP - 4] <=> Multiplicateur, transféré dans DE ; [SP - 6] <=> Adresse de retour ; En sortie ; DE <=> Produit de BC par DE (résultat sur 16 bits !) ; Registres affectés ; DE et les registres spécialisés ; Remarques ; - Si BC ou DE = 0 => DE = 0 en sortie ; -[SP - 2] et [SP - 4] sont dépilés ; - Vous pouvez utiliser n'importe quel registre 16 bits en sortie ; ; - Modèle d'appel de la fonction ; ; LD DE, 5 ; PUSH DE ; LD DE, 3 ; PUSH DE; CALL MATH_MUL_SP_16x16 ; . ; MATH_MUL_SP_16x16 : EXX ; Sauvegarde des registres BC', DE', HL' EX AF, AF' ; Sauvegarde de AF ; Récupération des paramètres POP HL ; Récupération de l'adresse de retour POP DE ; Récupération du multiplicateur POP BC ; Récupération du multiplicande PUSH HL ; Sauvegarde l'adresse de retour ; Initialisation des registres LD HL, 0 ; Valeur par défaut pour le retour ; Vérification des valeurs en entrée RL L ; Permet d'effacer Carry ADC HL, BC ; Vérification que BC' = 0 JP z, _MATH_MUL_SP_16x16_END LD HL, 0 ADC HL, DE ; Vérification que DE' = 0 JP z, _MATH_MUL_SP_16x16_END ; Début du code lié à la multiplication LD HL, 0 ; Valeur de retour LD A, B LD B, 16 _MATH_MUL_SP_16x16_1 ; Multiplication par 2 de HL' (décalage d'1 bit vers le haut) ADD HL, HL SLA C ; SLA suivi d'un RLA permet de réaffecter les bits de C' RLA ; dans A' en cas de débordement de C', donc de traiter ; les 16 bits du multiplicateur JR nc, _MATH_MUL_SP_16x16_2 ; Remplacement du bit de débordement par le multiplicateur ADD HL, DE _MATH_MUL_SP_16x16_2 DJNZ _MATH_MUL_SP_16x16_1 _MATH_MUL_SP_16x16_END PUSH HL ; Stockage du résultat sur la pile EXX ; Restauration des registres courants EX AF, AF' ; Restauration de AF POP DE ; Récupération du résultat pour le retour via DE ; (Ou tout autre registre sur 16 bit) RET 4.2 Les Multiplications par sous-produits 4.2.1 Multiplication de deux entiers non signés de 8 bits, résultat sur 16 bits ;*************************************************************** ; Routine de multiplication de 2 entiers non signés de 8 bits ; avec un résultat sur 16 bits ; ; En entrée ; [SP - 1] <=> Multiplicateur, transféré dans A ; [SP - 2] <=> Multiplicande, transféré dans C ; [SP – 4] <=> Adresse de retour ; En sortie ; DE <=> Produit de C par A ; Registres affectés ; DE et les registres spécialisés ; Remarques ; - Si A ou C = 0 => DE = 0 en retour ; - [SP - 1] et [SP - 2] sont dépilés ; - Pour optimiser les calculs, vous pouvez vérifier que le ; multiplicateur est bien inférieur au multiplicande ; Plus le multiplicateur est petit plus l'opération est rapide ; ; - Méthode d'appel de la fonction ; ; LD D, 6 ; LD E, 2 ; PUSH DE ; CALL MATH_MUL_SP_8x8 ; . ;MATH_MUL_SP_8x8: EXX ; Sauvegarde des registres BC, DE, HL EX AF, AF' ; Sauvegarde de AF POP HL ; Récupération de l'adresse de retour POP BC ; Récupération des deux paramètres PUSH HL ; Sauvegarde de l'adresse de retour ; Initialisation des registres pour le test en entrée LD HL, 0 ; Valeur pour le retour LD A, B ; Le multiplicateur dans A ; Vérification des valeurs en entrée OR A ; Test de A ; Retour si A = 0 JP z, _MATH_MUL_SP_8x8_END LD L, A ; Sauvegarde de A pour le test de C (le multiplicande) LD A, C ; Transfert de B dans A pour le test OR A ; Test de C via A LD A, L ; On restore A LD L, H ; On efface L via H ; Retour si A = 0 JP z, _MATH_MUL_SP_8x8_END _MATH_MUL_SP_8x8_1 ; Début du code lié à la multiplication LD D, L ; Le multiplicande est dans DE pour calculer les sous-produits LD E, C ; _MATH_MUL_SP_8x8_2 BIT 0, A ; Vérification si le bit 0 = 0, si oui ; on ne cumule pas le sous-produit courant JP Z, _MATH_MUL_SP_8x8_3 ADD HL, DE ; Cumule du sous-produit courant _MATH_MUL_SP_8x8_3 EX HL, DE ; Décalage du sous-produit courant ADD HL, HL ; de 1 bit vers le haut EX HL, DE ; SRL A ; Décalage du multiplicateur de 1 bit ; vers le bas OR A ; Vérification que tous les bits du ; multiplicateur ont été traités JP nz, _MATH_MUL_SP_8x8_2 _MATH_MUL_SP_8x8_END PUSH HL ; Sauvegarde du résultat EXX ; Restauration des registres BC, DE, HL EX AF, AF' ; Restauration de AF POP DE ; Récupération du résultat dans DE ; (Ou tout autre registre 16 bits) RET 4.2.2 Multiplication de deux entiers non signés de 16 par 8 bits, résultat sur 16 bits ;*************************************************************** ; Routine de multiplication de 2 entiers non signés, de 16 par ; 8 bits, avec un résultat sur 16 bits ; ; En entrée ; [SP - 2] <=> Multiplicande, transféré dans DE ; [SP - 4] <=> Multiplicateur, transféré dans A (partie haute de [SP – 4] ; [SP – 6] <=> Adresse de retour ; En sortie ; DE <=> Produit de DE par A (résultat sur 16 bits !) ; Registres affectés ; DE et les registres spécialisés ; Remarques ; - Si le multiplicande ou le diviseur = 0 => DE = 0 en retour ; - [SP - 2], [SP - 4] et [SP - 6] sont dépilés ; - Pour optimiser les calculs, vous pouvez vérifier que le ; multiplicateur est bien inférieur au multiplicande ; ; - Méthode d'appel de la fonction ; ; LD DE, 6 ; PUSH DE ; LD D, 2 ; D contient le multiplicateur ; PUSH DE ; CALL MATH_MUL_SP_16x8 ; . ;MATH_MUL_SP_16x8: EXX ; Sauvegarde des registres BC, DE, HL EX AF, AF' ; Sauvegarde du registre AF POP HL ; Récupération de l'adresse de retour POP AF ; Récupération du multiplicateur 8 bits dans A POP DE ; Récupération du multiplicande sur 16 bits PUSH HL ; Sauvegarde de l'adresse de retour LD HL, 0 ; Mise à blanc de HL pour le retour ; Vérification des valeurs en entrée OR A ; Si A = 0 on sort JP z, _MATH_MUL_SP_16x8_END ADC HL, DE ; Si DE = 0 on sort JP z, _MATH_MUL_SP_16x8_END ; Début du code lié à la multiplication LD HL, 0 _MATH_MUL_SP_16x8_1 BIT 0, A ; Vérification si le bit 0 = 0, si oui ; on ne cumule pas le sous-produit courant JP z, _MATH_MUL_SP_16x8_3 ADD HL, DE ; Cumule du sous-produit courant _MATH_MUL_SP_16x8_3 EX HL, DE ; Décalage du sous-produit courant ADD HL, HL ; de 1 bit vers le haut EX HL, DE ; SRL A ; Décalage du multiplicateur de 1 bit ; vers le bas OR A ; Vérification que tous les bits du ; multiplicateur ont été traités JP nz, _MATH_MUL_SP_16x8_1 _MATH_MUL_SP_16x8_END PUSH HL ; Préservation du résultat EXX ; Restauration des registres courants BC, DE, HL EX AF, AF' ; Restauration de AF POP DE ; Récupération du résultat dans DE ; (ou tout autre registre sur 16 bits) RET 4.3 La division par utilisation de sous-dividendes 4.3.1 Division de deux entiers non signés de 8 bits, résultat sur 8 bits ;*************************************************************** ; Routine de division de 2 entiers non signés de 8 bits, avec un ; résultat sur 8 bits pour le quotient et 8 bits pour le reste ; ; En entrée ; [SP - 2] <=> Les deux paramètres le dividende (transféré dans B) ; et le diviseur (transféré dans C) ; En sortie ; D <=> Quotient ; E <=> Reste de la division entière ; Registres affectés ; DE et les registres spécialisés ; Remarques ; - Si le dividende ou le diviseur = 0 en entrée => D = 0 et E = 0 en retour ; - D et E peuvent être remplacés par n'importe quels registres 8 bits en retour ;MATH_DIV_SP_8x8: EXX ; Sauvegarde des registres BC, DE, HL EX AF, AF' ; Sauvegarde de registre AF POP HL ; Récupération des l'adresse de retour POP DE ; Récupération des deux paramètres D et E PUSH HL ; Préservation de l'adresse de retour ; Initialisation des registres LD B, 8 ; Les 8 bits du dividende sont traités XOR A, A _MATH_DIV_SP_8x8_1 SLA D ; Décalage du bit le plus haut, du dividende dans Carry RLA ; Récupération de Carry dans le sous-dividende (A) ; (création du sous dividende) CP E ; Vérification si le sous-dividende est >= au diviseur. ; Si oui le quotient = + 1, sinon on traite le bit suivant, du dividende JR c, _MATH_DIV_SP_8x8_2 INC D ; Incrémentation du quotient. Sachant que les bits de D ; sortent vers le haut (la gauche), les bits du bas vont ; servir pour stocker le quotient SUB E ; On soustrait le diviseur du dividende _MATH_DIV_SP_8x8_2 DJNZ _MATH_DIV_SP_8x8_1 ; Décrémentation de B de manière à ; traiter les 8 bits du dividende LD E, A ; Récupération du reste dans E ; Le quotient se trouve dans D PUSH DE ; Préservation du résultat dans DE EXX ; Restauration des registres BC, DE, HL EX AF, AF' ; Restauration du registre AF POP DE ; Récupération du quotient et du reste dans DE ; (ou tout autre registre 16 bits) RET 4.3.2 Division de deux entiers non signé de 16 par 8 bits, résultat sur 16 bits ;*************************************************************** ; Routine de division d'un entier non signé de 16 bits par un ; entier non signé de 8 bits, avec un résultat sur 16 bits ; ; En entrée ; [SP - 2] <=> Dividende, transféré dans HL ; [SP - 4] <=> Diviseur, transféré dans B (8 bits de la partie haute de [SP - 4]) ; [SP – 6] <=> Adresse de retour ; En sortie ; DE <=> Quotient (sur 16 bits) ; A <=> Reste de la division entière (8 bits) ; Registres affectés ; A, DE et les registres spécialisés ; Remarques ; - [SP - 2], [SP - 4] et [SP – 6] sont dépilés ; - Si le dividende ou le diviseur = 0 => DE = 0 et A = 0 en retour ; - DE peut être remplacé par n'importe quel registre 16 bits pour le retour ;MATH_DIV_SP_16x8: EXX ; Préservation des registre BC, DE, HL EX AF, AF' ; Préservation de AF POP DE ; Récupération de l'adresse de retour POP BC ; Récupération du diviseur dans B ; (partie haute à l'adresse [SP - 3]) POP HL ; Récupération du dividende PUSH DE ; Sauvegarde de l'adresse de retour ; Test des valeurs en entrée LD A, B OR A JP nz, _MATH_DIV_SP_16x8_1 LD H, A LD L, A JP _MATH_DIV_SP_16x8_END _MATH_DIV_SP_16x8_1 LD C, B ; Stockage du diviseur dans C LD B, 16 ; Les 16 bits de HL (le dividende) vont être traités XOR A, A ; On efface A pour initialiser le sous-dividende _MATH_DIV_SP_16x8_2 SLA L ; Décalage du bit le plus haut, du dividende dans Carry RL H RLA ; Récupération de Carry dans le sous-dividende (A) ; (création du sous dividende) CP C ; Vérification si le sous-dividende est supérieur ou ; égal au diviseur. Si oui le quotient = + 1, sinon on ; traite le bit suivant, du dividende JR c, _MATH_DIV_SP_16x8_3 INC HL ; Incrémentation du quotient (sachant que tous les bits ; de HL sont décalé vers le haut (la gauche), les bits ; venant du bas vont servir pour stocker le quotient SUB C ; On soustrait le diviseur du dividende _MATH_DIV_SP_16x8_3 DJNZ _MATH_DIV_SP_16x8_2 ; Décrémentation de B de manière à ; traiter les 16 bits du dividende _MATH_DIV_SP_16x8_END PUSH HL ; Sauvegarde du quotient sur la pile, le reste se trouve dans A EXX ; Restauration des registres BC, DE, HL LD D, A ; Sauvegarde du reste de l'opération dans A EX AF, AF' ; Restauration de F LD A, D ; A contient le reste de la division entière POP DE ; Récupération du résultat dans DE ; (ou tout autre registre 16 bits) RET 4.3.3 Division de deux entiers non signé de 16 bits, résultat sur 16 bits ;*************************************************************** ; Routine de division d'un entier non signé de 16 bits par un ; entier non signé de 16 bits, avec un résultat sur 16 bits ; ;En entrée ; [SP - 2] <=> Dividende, transféré dans BC ; [SP - 4] <=> Diviseur, transféré dans DE ;En sortie ; DE <=> Quotient ; BC <=> Reste de la division entière (sur 16 bits) ;Registres affectés ; BC, DE et les registres spécialisés ; Remarques ; - [SP - 2] et [SP - 4] sont dépilés ; - Les registres DE et BC en sortie peuvent être remplacés par ; d'autres registres 16 bits ; - Si le diviseur ou le dividende = 0 => DE = 0 et BC = 0 en retour ; - DE peut être remplacé par n'importe quel registre 16 bits pour le retour ;MATH_DIV_SP_16x16: EXX ; Sauvegarde des registres BC, DE, HL EX AF, AF' ; Sauvegarde de AF POP HL ; Récupération de l'adresse de retour POP DE ; Récupération du diviseur POP BC ; Récupération du dividende PUSH HL ; Préservation de l'adresse de retour ; Vérification des valeurs en entrée LD HL, 0 ADC HL, DE ; Si DE = 0 on sort JP nz, _MATH_DIV_SP_16x16_1 XOR A, A ; On efface A pour le retour LD C, A ; On efface C pour le retour JP _MATH_DIV_SP_16x16_END _MATH_DIV_SP_16x16_1 LD HL, 0 ; Servira de sous-dividende LD A, B ; Stockage de la partie haute du dividende dans B LD B, 16 ; Les 16 bits de HL (le dividende) vont être traités _MATH_DIV_SP_16x16_2 SLA C ; Décalage du bit le plus haut de la partie basse du ; dividende, dans Carry RLA ; Récupération de Carry dans la partie haute du dividende ; Et décalage de la partie haute du dividende dans Carry ADC HL, HL ; Récupération éventuelle de Carry et création par la ; même occasion du sous-dividende SBC HL, DE ; On enlève le diviseur du sous-dividende, si Carry est ; allumé c'est que le sous-dividende est inférieur JR c, _MATH_DIV_SP_16x16_3 INC C ; Incrémentation de C de manière à crée le quotient ; (Remplacement des bits du dividende par le quotient) DJNZ _MATH_DIV_SP_16x16_2 ; Décrémentation de B de manière à ; traiter les 16 bits du dividende JP _MATH_DIV_SP_16x16_END _MATH_DIV_SP_16x16_3 ADD HL, DE ; Restauration du sous-dividende car celui-ci est ; inférieur au diviseur DJNZ _MATH_DIV_SP_16x16_2 ; Décrémentation de B de manière à ; traiter les 16 bits du dividende _MATH_DIV_SP_16x16_END LD B, A ; Partie haute du quotient dans B ; La partie basse se trouve déjà dans C PUSH BC ; Préservation du quotient PUSH HL ; Préservation du reste de la division EXX ; Restauration des registres BC, DE, HL EX AF, AF' ; Restauration de AF POP BC ; Récupération du reste dans BC POP DE ; Récupération du quotient dans DE RET
par –( ANTIBUG )- le 01/06/2007 dernière maj : le 01/06/2007 contact : vincentbouffigny.cpc@free.fr web : http://ovi.org.free.fr - http://projets.infos.free.fr ★ AMSTRAD CPC ★ A voir aussi sur CPCrulez , les sujets suivants pourront vous intéresser... |
CPCrulez[Content Management System] v8.7-desktop/c Page créée en 448 millisecondes et consultée 2236 foisL'Amstrad CPC est une machine 8 bits à base d'un Z80 à 4MHz. Le premier de la gamme fut le CPC 464 en 1984, équipé d'un lecteur de cassettes intégré il se plaçait en concurrent du Commodore C64 beaucoup plus compliqué à utiliser et plus cher. Ce fut un réel succès et sorti cette même années le CPC 664 équipé d'un lecteur de disquettes trois pouces intégré. Sa vie fut de courte durée puisqu'en 1985 il fut remplacé par le CPC 6128 qui était plus compact, plus soigné et surtout qui avait 128Ko de RAM au lieu de 64Ko. |
|
|