Category: Programmation

  • Pipe-Mania

    Un idée simple, une programmation des plus légères. Et nous voilà à jouer sur un jeu très addictif.

    Informations générales

    En pensant à son prochain projet 1K pour le ZX-81, DanCresp ne le savait pas, mais ce jeu était en tête et de la recherche d’informations sur Internet. Il décida de créer une première version pour MSX. Les problèmes de temps et la facilité d’utilisation de MS-DOS EUPHORIC sur son ordinateur portable l’ont incité à l’adapter à l’ORIC, en vérifiant auparavant qu’il n’y avait pas de version connue.

    Au bout de cinq jours, l’auteur a laissé cette version utilisable, quels que soient les niveaux et les blocs qui apparaissent dans la version d’origine, graphiquement sur la base de la version Spectrum.

    Heureusement, l’émulateur EUPHORIC permet, en appuyant sur “F3”, de mapper le clavier ORIC avec les touches du PC, ce qui facilite beaucoup l’introduction du programme. De l’émulateur, il a généré le TAP et de «l’Oricutron» de Windows, il a généré le listing dans un fichier texte utilisé pour déboguer le code source.

    La vérité est que Dan est très satisfait car le fonctionnement et la performance sont parfaits ; avec un très bon game design. Le système pour augmenter la difficulté a donné plus mal à la tête parce que le jeu devait être difficile mais réalisable. (NDLR : Dan est décidément un pro du game design)

    Les petites choses de la version ORIC
    Comme toujours lors de la programmation dans l’ORIC, les graphismes ont dû être repensés car au lieu de la matrice habituelle de 8×8 pixels, nous passons à une matrice de 8×6.
    Étant donné que l’ORIC dispose d’un mode texte de 26 lignes et de 40 colonnes, la zone de jeu a les dimensions de la version Amiga 500 d’origine de 7 lignes x 10 colonnes au lieu de la version 7×9 de la version Spectrum.

    Dan aurais aimé regarder plus avec la couleur de l’écran, mais le système “particulier” de l’ORIC ne m’a pas permis. Après consultation d’amis graphistes, c’était la meilleure solution. Très semblable à MSX … bleu et jaune.

    Pour contrôler le curseur, le clavier est lu à l’aide d’un «PEEK 520» (NDLR : le fameux PEEK #208 si connu des développeurs), ce qui évite les problèmes de lettres majuscules / minuscules et vous garantit de toujours lire la dernière touche enfoncée, en accédant à la mémoire tampon du clavier.

    Et pour le son, une fois encore, Dan a utilisé les effets prédéfinis de l’ORIC, démontrant ainsi une faiblesse dans ce domaine …

    Et l’eau coule …
    La difficulté principale et presque unique du projet a été de faire circuler l’eau à travers le tuyau, et même si au début cela semblait une question assez complexe, après une promenade avec brain-storming inclus, j’ai trouvé une solution très simple et rapide .

    Nous avons 7 types de blocs différents pour assembler notre circuit.

    Chacun de ces 7 blocs utilise certains des 11 graphiques de tubes différents, numérotés de 97 à 104.

    L’eau peut avoir 4 directions possibles, numérotées dans le sens des aiguilles d’une montre.

    Nous chargeons la matrice «U $» avec les valeurs DATA des lignes 9600 et 9605. Chacune de ces valeurs contient quatre nombres. La position indique la direction dans laquelle l’eau entre et la valeur de cette position indique la nouvelle direction à prendre par l’eau. Si la valeur est 0, cela signifie que vous avez entré une adresse incorrecte et que le jeu se termine.

    Par exemple, la valeur en U du segment de tuyau 3 (coude inférieur / gauche) est «4300». Ainsi, si l’eau entre, elle monte et entre en dessous (direction 1), elle change à gauche (direction 4). Si vous entrez, il avance vers la droite (direction 2), il passe en bas (direction 3). Mais si l’eau vient du haut ou de la gauche, la valeur est «0» et le jeu se termine.

    Lors du démarrage d’un niveau, la position initiale du bloc de sortie est calculée et dans la variable “W”, nous stockons l’adresse de la mémoire vidéo VRAM où l’eau commencera à circuler et dans la variable “O” son adresse (1, 2, 3 ou 4).

    Lorsque l’eau commence à circuler, nous examinons le graphique correspondant à cette conduite et prenons son code (entre 1 et 11). Sur la base du sens d’entrée de l’eau (1 à 4), nous calculons le changement de direction en fonction de la matrice enregistrée dans «U $» et nous mettons à jour la position dans la VRAM sur laquelle pointe la variable «W» en fonction des valeurs de la matrice «J». Cette matrice contient 4 valeurs: -40, 1, 40 et -1 et est ajoutée à «W» en fonction de la nouvelle adresse pour pointer sur la bonne adresse VRAM afin de calculer le morceau de tuyau suivant.

    Ensuite, nous modifions le CHAR du tuyau pour qu’il corresponde à l’eau, au moyen d’un POKE. Si le graphique de la position indiquée par «W» ne correspond pas à celui d’un tuyau, avec ou sans eau, le jeu se termine. J’ai utilisé un POKE car il est plus rapide et plus facile à contrôler (je n’utilise qu’une variable) qu’en utilisant un PLOT et deux variables.

    Tout cela, qui semble très compliqué, est résolu pendant le jeu avec 3 lignes de code, 170, 175 et 180. C’est aussi simple que cela.

    Une fois le niveau terminé, le même processus est exécuté pour calculer la section construite correcte, avec une copie du code précédent et quelques modifications simples, aux lignes 205, 210 et 215.

    Eh bien, rien de plus, je ne peux qu’espérer que cela vous plaise, et oeil… qui accroche le sien…

    Je vous invite à l’essayer.

    L’écran titre et toutes les touches du jeu !

    Quelques éléments concernant le programme

    Le programme est divisé en 9 blocs:

    • Déclaration de la matrice et début du jeu.
    • Boucle principale de développement.
    • Passer le niveau
    • Plusieurs routines.
    • Fin du jeu.
    • Présentation et début du jeu.
    • Préparez et montrez l’aire de jeu.
    • Charger UDG.
    • DATA avec les UDG du jeu et les valeurs de la matrice.

    L’ensemble du programme occupe 91 lignes.

    • 10 – Nous définissons les matrices du jeu.
    • 30 – Aller au sous-programme redéfinit les caractères et définit les variables globales.
    • 100 – Début de la boucle principale. Si le curseur est verrouillé, il passera à la ligne 160.
    • 105 – Prenez la touche appuyée. Si aucune touche n’a été enfoncée, le nombre passe à 160.
    • 115 – Bloc qui déplace le curseur en fonction de la touche appuyée.
    • 135 – Si vous n’appuyez pas sur “Space”, passez à 160.
    • 140 – Vérifiez si la case est occupée par un bloc spécial, sautez à 160 si c’est le cas.
    • 145 – S’il est occupé par un tuyau, réglez le compteur «N» sur 10 et il nous bloquera.
    • 150 – Bloc qui vérifie qu’aucune eau ne passe à travers le bloc.
    • 155 – Placez le bloc à la position indiquée en mettant à jour le curseur et la ligne des blocs suivants.
    • 160 – Réduit l’heure de l’indicateur “T”. S’il reste encore sauter à 185.
    • 165 – Compteur contrôlant le débit d’eau dans les tuyaux. Si vous ne devez pas avancer, passez à 185.
    • 170 – Bloc qui contrôle la progression de l’eau dans les tuyaux. Si vous ne pouvez pas, mettez “D = -9” pour interrompre la répétition.
    • 175 – Réduit le compteur “D” et vérifie le type de tuyau dans lequel se trouve l’eau.
    • 180 – Modifiez le graphique du tuyau et affectez l’adresse correspondante.
    • 185 – S’il reste encore du temps dans le compteur «D» et que l’eau circule bien dans la canalisation, retournez à REPEAT de la ligne 100.
    • 200 – Si “D = -9” saute à 1900 et met fin à la partie, sinon le niveau est dépassé.
    • 205 – Bloc qui vient de remplir d’eau l’ensemble du circuit de tuyaux assemblé.
    • 220 – Mettez à jour les points, augmentez le niveau et passez à 2100 pour monter un nouveau niveau.
    • 1000 – Routine qui place un bloc dans une certaine position.
    • 1100 – Routine qui sélectionne de manière aléatoire un nouveau bloc à quitter lors des prochaines exécutions.
    • 1200 – Routine qui montre les cinq blocs suivants à partir.
    • 1300 – Place le curseur dans la case sélectionnée.
    • 1350 – Supprimer le curseur de la case sélectionnée.
    • 1400 – Affiche le marqueur à 8 chiffres avec les points ou l’enregistrement.
    • 1900 – Le jeu se termine, met à jour le tableau de bord et marque une pause avant de passer à la présentation.
    • 2000 – Écran initial du jeu.
    • 2065 – Une touche doit être enfoncée. S’il se situe entre “1” et “9”, il est pris comme niveau initial et le jeu commence.
    • 2100 – Initialise les variables et détermine la difficulté du niveau.
    • 2105 – Dessine l’aire de jeu.
    • 2115 – Calcule les cinq prochains blocs qui vont sortir.
    • 2120 – Initialise la position des différentes cases.
    • 2130 – Calculez la position de la boîte initiale et enregistrez dans «W» l’adresse VRAM de la première position de l’eau.
    • 2135 – Calculer et positionner le curseur.
    • 2140 – À partir d’un certain niveau, les blocs avec la clé sont placés à 3 positions de la boîte initiale.
    • 3000 – Sélection des couleurs, effacement de l’écran, masquage du curseur et masquage des «CAPS» supérieurs.
    • 3005 – Lecture de données UDG.
    • 3010 – Assemblez les matrices avec des bandes de caractères.
    • 3020 – Lire les DONNÉES avec les directions de l’eau et leurs incréments.
    • 3025 – Chargez un tableau avec des chaînes numérotées de «01» à 25» et utilisées par les marqueurs. C’est plus rapide comme ça.
    • 9000 – DONNÉES avec graphiques UDG.
    • 9500 – DATA avec les différents types de blocs.
    • 9600 – DATA avec les directions de l’eau en fonction du type de tuyau.
    • 9700 – DATA permet de modifier la valeur «W» lorsque vous pointez vers la VRAM.

    Le jeu

    Pipe Mania est un jeu vidéo de type “casse-tête” développé en 1989 par The Assembly Line pour Amiga. Plus tard, LucasFilm Games a créé des versions pour d’autres machines sous le nom de Pipe Dream.

    Le pipe est confié à un plombier, Alfonzo, qui a pour objectif d’ajuster des tuyaux apparaissant au hasard dans une matrice contenant un robinet d’où sort de l’eau.

    Vous devez savoir que deux marqueurs effectuent un compte à rebours:

    • “T”: indique le temps restant avant que l’eau commence à traverser les tuyaux.
    • “D”: indique le temps restant pour terminer le niveau. Chaque unité correspond à un bloc.

    Mettre un bloc de tuyaux dans une boîte occupée nous bloquera quelques instants.

    À partir de certains niveaux, des blocs avec une clé ou des segments de tuyau apparaissent et ne peuvent être occupés.

    Sur l’écran de présentation, nous pouvons indiquer le niveau, entre 1 et 9, auquel commencer le jeu.

    Cette version suit exactement les mêmes mécanismes, mais les phases de bonus et certains types de blocs ont été éliminés.

    Contrôles:
    Contrôlez le curseur avec “Q” – “A” – “O” – “P” et appuyez sur “Espace” pour placer un segment de tuyaux.

    Quelle est votre préférée : la version Oric ou Amiga ?

    Quelques autres versions

    Pipe-Mania, au plus simple sur ZX-81
  • Blasto

    Mars 2014, l’acte 4 des développements retrouvés de Dan Cresp sur Oric. Cette fois-ci, le développement a été limité au Commodore, au MSX et la notre petite machine en rouge et noir

    Infos générales

    Le développement de la version ORIC est issu de la version MSX.

    Le MSX, armé de son Z80 et son Basic Microsoft

    En l’absence de temps libre, l’auteur a profité de trajets en train pour programmer le jeu avec la version MS-DOS de l’émulateur EUPHORIC. En effet, la saisie de programme est très facilitée avec cet émulateur qui accepte l’utilisation du clavier au format du PC (NDLR : j’ai toujours été ennuyé avec le clavier proposé par Oricutron sur la ROM Atmos de base). Le TAP a été crée avec “Oricutron” sous Windows, j’ai généré le listing dans un fichier texte et je l’ai commenté plus tard.

    Le listing de cette version est très similaire, et certaines parties sont identiques, à celle de la version MSX ; tout en prenant en compte les particularités de chaque système. Ainsi, l’adresse de la mémoire vidéo et la méthode de lecture des commandes du navire ont été modifiées. Certaines lignes ont été dépliées pour éviter des problèmes avec le “bogue” de l’instruction ELSE de l’Oric.

    C’est un excellent exemple pour comparer les performances de ces deux systèmes, et bien que les deux versions fonctionnent parfaitement, le MSX a dû “ralentir” pour éviter que ce ne soit trop rapide tandis que pour l’Oric, cela n’a pas été nécessaire.

    À cet égard, je dois reconnaître que même si la vitesse est parfaite, je m’attendais à de meilleures performances coté ORIC. Gardez à l’esprit que, dans les deux versions, POKE se trouve directement dans la mémoire vidéo pour gagner du temps et qu’il n’y a pas beaucoup de lignes de code à exécuter, sauf dans le cas d’une explosion dans une mine ou d’une mine à chaînes, et dans l’ORIC, il en jette juste assez.

    Vous êtes plutôt Oric ou Arcade ?

    Principaux écarts par rapport à l’arcade d’origine:

    • Le jeu original autorise 2 joueurs simultanés et un seul ici.
    • L’écran a 28 lignes et ici il y en a 26 (22 en MSX).
    • Dans le jeu original, il y a 84 mines et ici 78 (64 en MSX), l’aire de jeu étant un peu plus petite.
    • Les graphiques sont légèrement différents de la version originale.
    • Cette version ne permet pas l’utilisation du joystick.

    Pour le reste, la mécanique du jeu a été pleinement respectée.

    Les petites choses de la version ORIC
    Comme toujours lors de la programmation sur l’ORIC, les graphismes ont dû être repensés car au lieu de la matrice habituelle de 8×8 pixels, nous passons à une matrice de 8×6. Ainsi, l’explosion (qui occupe 9 blocs) a dû être coupée à 6 pixels horizontalement, le navire a dû être redessiné pour le laisser dans une taille réelle de 5×5 pixels et les mines ont une apparence différente.

    La magnifique aire de jeu de l’Oric

    Étant donné que l’ORIC dispose d’un mode texte de 26 lignes et de 40 colonnes, l’apparence de la zone de jeu est presque identique à celle de la version arcade.

    Pour le contrôle du navire, le clavier est lu à l’aide d’un “PEEK 520”, ce qui évite les problèmes de boîtier.

    Et pour le son, une fois encore, j’ai tiré les prédéfinis de l’ORIC.

    Réaction en chaîne
    Le jeu est techniquement très simple et la seule difficulté est de savoir comment résoudre le problème des explosions de mines à chaînes, car il peut facilement y avoir 2 mines ou plus ensemble. Pour cela, j’ai créé une matrice de 78 éléments (un par mine) avec deux pointeurs: “M” et “N”.

    Lorsque notre tir détecte que nous avons touché une mine, il augmente le pointeur «N», enregistre sa position dans la matrice et l’efface de l’écran. Les positions autour sont examinées ci-dessous. Chaque fois qu’une autre mine est trouvée, la même action est effectuée.

    A la ligne 140, nous voyons si le pointeur “M” est inférieur à “N” et si c’est vrai, nous allons à la routine qui les fait exploser, en augmentant le pointeur “M”.

    Eh bien, rien de plus, je ne peux qu’espérer que cela vous plaise.

    Je vous invite à l’essayer.

    Quelques éléments concernant le programme

    Le programme a été divisé en 10 blocs:

    • Déclaration de matrice. Appel aux routines principales.
    • Contrôle des navires.
    • Contrôle du tir.
    • Fin du jeu.
    • Le coup a touché quelque chose.
    • Détruire une mine.
    • Perdre une vie.
    • Présentation et début du jeu.
    • Plusieurs routines.
    • DATA avec les UDG du jeu et la conception de la zone de jeu.

    L’ensemble du programme occupe 73 lignes.

    Ensuite, j’indique le principe des différentes parties du programme:

    • 10 – Nous définissons les matrices du jeu.
    • 50 – Accédez au sous-programme qui lit les graphiques, redéfinit les caractères et définit les variables globales et le mode vidéo.
    • 100 – Début de la boucle principale.
    • 102 – Le clavier est consulté et si aucune touche n’est enfoncée, il passe à 140.
    • 110 – Le navire se déplace en fonction de la touche enfoncée.
    • 140 – S’il y a une mine en attente d’exploitation, reportez-vous à la routine 350 correspondante.
    • 150 – Contrôle de tir. S’il est actif, passez à 160 et si ce n’est pas le cas, vérifiez si le déclencheur a été enfoncé
    • 160 – Déplacez le coup en fonction de sa direction.
    • 190 – Le temps disponible pour terminer le jeu est réduit.
    • 200 – Si le temps le permet, passez à 100.
    • 205 – Fin de partie où il est contrôlé si le record est dépassé et si toutes les mines ont été détruites.
    • 300 – Contrôle que vous avez touché le coup.
    • 350 – Routine qui contrôle la destruction des mines à chaînes.
    • 400 – Une mine a explosé à proximité et notre navire est détruit.
    • 2000 – Écran initial du jeu.
    • 2500 – Initialise les variables pour le jeu et la routine qui place les mines.
    • 2600 – Position initiale de notre navire.
    • 2700 – Routine qui montre le labyrinthe de points à l’écran, sans les mines.
    • 3000 – Sélection des couleurs, effacement de l’écran, masquage du curseur et masquage des «CAPS» supérieurs.
    • 3010 – Lire les données UDG.
    • 3015 – Lecture des DONNEES des segments de l’aire de jeu.
    • 5100 – Routine montrant le marqueur de point.
    • 5400 – Routine indiquant la fin du jeu ou un message de jeu gratuit.
    • 9000 – DONNÉES avec graphiques UDG.
    • 9100 – DATA avec les différentes lignes qui composent l’aire de jeu.

    Le jeu

    Ce jeu vidéo pour systèmes d’arcade a été développé en 1977 par la société Gremlin.

    L’objectif du jeu est de détruire toutes les mines du champ de mines avant la fin du temps imparti. Pour cela, nous avons un navire que nous pouvons déplacer à travers les zones vides de l’écran et qui peut tout détruire. Il faut faire attention, car lorsque les mines explosent, elles détruisent ce qui les entoure.

    Si nous atteignons notre objectif, nous aurons un jeu gratuit.

    Contrôles:

    • Contrôler le navire avec Q – A – O – P.
    • Appuyez sur la “espace” pour tirer.

    Quelques autres versions

    Blasto sur MSX
    et sur Commodore
  • Lights Out

    Après quelques softwares développés sur d’autres plateformes, ce logiciel marque les débuts de Dan Cresp en programmation sur l’Oric. Produit début 2012, la vision de Dan est exceptionnelle sur cette adaptation. Romuald réalisera fin 2018 une nouvelle adaptation de ce jeu de réflexion dans le cadre du concours CEO NY2019.

    Généralités

    Le jeu est une conversion informatique de la machine “LIGHTS OUT” de la maison TIGGER. Très addictif, c’est le premier jeu que Dancresp a réalisé pour l’ORIC, et c’est une adaptation du même jeu programmé pour le THOMSON MO5 fin 2011. Cette version est un peu plus rapide, mais elle a perdu la simplicité d’action liée à l’utilisation du crayon optique.

    Le MO5 de Daniel, équipé de son crayon optique … en pleine action

    L’adaptation à l’Oric est une réussite car son BASIC est assez complet et rapide et le programme de fait est agréable à utiliser. Le développement sur l’Oric m’a exigé de me familiariser avec son éditeur “étrange”, que j’ai finalement utilisé sans problèmes majeurs. D’autre part, j’ai découvert et commencé à maitriser son système de gestion de la couleur “particulier”. En fonction de ce que vous voulez faire, c’est complexe et peut limiter le développement. Enfin, il est curieux de pouvoir utiliser des caractères de double hauteur. Pour résumer, cette “expérience initiatique” se poursuivra.

    Dans le chapitre bizarreries de l’Oric, la fonction CHR a une fonction différente suivant que vous l’utilisez dans des lignes PLOT (pour indiquer une couleur) et PRINT.

    Pour conclure, disons que chaque être humain devrait avoir la possibilité, ne serait-ce qu’une fois, d’utiliser le clavier de l’ORIC ATMOS qui est un des meilleurs que j’ai pu utiliser.

    Je vous invite à l’essayer.

    Lights Out sur l’Oric Atmos de Daniel, une belle adaptation

    Quelques éléments à propos du programme

    Le programme principal est décomposé en 9 parties :

    • Déclaration de variables, initialisation du jeu et calibrage du stylet.
    • Impression des marqueurs de jeu.
    • Sélecteur de carte correspondant.
    • Envoi à l’affichage du tableau actuel et du numéro de mouvement.
    • Contrôle de clé.
    • Contrôle de l’investissement des lumières correspondantes.
    • Inversion d’une lumière.
    • Impression du tableau avec les lumières.
    • Données des 10 premiers et tableaux de bord et données d’aide de la course.

    Les variables suivantes sont utilisées:

    • M$ = Matrice où sont stockés les 10 premiers tableaux.
    • S$ = Chaîne où le tableau actuel est enregistré.
    • A$ = Carte de lumière.
    • W = Matrice où les premières analyses sont enregistrées en cas d’aide.
    • L = plateau de jeu actuel.
    • M = numéro de mouvement.
    • H = compteur d’aide.
    • F = Lumière à inverser, ou boucles
    • X = position horizontale du pointeur.
    • Y = Position verticale du pointeur.
    • P = compteur de lumière.
    • B = Nombre de lumière qui a été pressée.
    • N = Nombre de lumière lors de l’impression du tableau.

    Les mécanismes du jeu original ont été condensés en seulement 42 lignes de code.

    Le code, simple et efficace, résumé par Dancresp
    • 100 – Définition des matrices du jeu.
    • 110 – Début des variables et charge les lignes DATA dans les matrices.
    • 200 – Couleurs du jeu, et nous faisons disparaître le curseur.
    • 210 – Impression de titres double format en utilisant les séquences “d’échappement” correspondantes.
    • 220 – Copyright au bas de l’écran.
    • 230 – Impression des textes “(H) ELP!” et “(R) ESET”.
    • 300 – Impression de marqueur de niveau.
    • 310 – Position du pointeur, nombre de mouvements (M) à 0 et si le niveau est supérieur à 10, sautez à la ligne 330.
    • 320 – Décomposez les 25 voyants de la matrice (M?) Dans la matrice S? Et réglez le compteur d’aides (H) sur 1.
    • 330 – Génère une carte de manière aléatoire et définit le compteur d’aide sur 3, qui est désactivé.
    • 400 – Imprimez le tableau avec les 25 voyants et s’il n’y en a pas, passez à la ligne 300.
    • 410 – Imprimez le numéro de mouvement.
    • 500 – Imprimez le pointeur sous la lumière correspondante.
    • 510 – Lisez le clavier. Si une touche est enfoncée, supprimez le pointeur mais relisez le clavier.
    • 520 – Si vous appuyez sur “O”, déplacez le pointeur vers la droite.
    • 530 – Appuyez sur “P” pour déplacer le pointeur vers la gauche.
    • 540 – Appuyez sur “Q” pour déplacer le pointeur vers le haut.
    • 550 – Appuyez sur “A” pour déplacer le pointeur vers le bas.
    • 560 – Si vous appuyez sur la touche espace, désactivez l’aide, calculez la lumière sélectionnée et passez à 600.
    • 570 – Si vous appuyez sur “H” et que le nombre de mouvements est inférieur à 3, lancez-vous pour nous.
    • 580 – Appuyer sur “R” réinitialise le tableau.
    • 590 – Si vous appuyez sur “S”, le curseur s’affiche et vous quittez le programme, mais passe à 500
    • 600 – Inverser la lumière sur laquelle il a été appuyé.
    • 610 – Si la lumière n’est pas dans la première rangée, inversez la lumière ci-dessus.
    • 620 – Si le feu ne se trouve pas dans la dernière rangée, inversez le feu en dessous.
    • 630 – Si le feu ne se trouve pas dans la dernière colonne, inversez le feu à droite.
    • 640 – Si le feu ne se trouve pas dans la première colonne, inversez le feu à gauche.
    • 650 – Ajoutez un coup (M) et passez à 400.
    • 700 – Si le voyant indiqué par F est allumé (= 1), définissez-le sur 0 mais sur 1.
    • 710 – Le sous-programme se termine.
    • 800 – Réglez le compteur de lumière (P) sur 0 et N = 1.
    • 810 – Début de la double boucle qui imprime le tableau.
    • 820 – Si la lumière à régler est active, la couleur rouge est activée, mais la couleur blanche est activée.
    • 830 – Imprimez le fichier à la position correspondante. Il a une taille de 3×3.
    • 840 – Augmentez le nombre de jetons (N) et fermez les boucles.
    • 850 – Fin du sous-programme.
    • 900 – Données des planches. Chaque tableau consiste en une chaîne de 25 chiffres 1 ou 0 indiquant l’état de l’un des voyants. Ensuite, deux nombres correspondant aux 2 premiers cycles que la machine proposera en cliquant sur “AIDE!” Sont transmis. S’il n’y a pas d’aide, mettez comme 0,0.

    Le Jeu

    L’objectif du jeu est d’éteindre toutes les lumières rouges sur le plateau de jeu. Nous contrôlons le pointeur qui apparaît sous l’un des 25 carrés du tableau. En appuyant sur Espace, vous modifierez l’état de ce carré et des 4 carrés adjacents ; celui du dessus, du dessous, de droite et de gauche.
    En appuyant sur la touche “H” (HELP), l’ordinateur choisit les 2 premiers mouvements, à condition qu’ils soient les premiers. Appuyez sur la touche “R” (RESET) pour revenir à la position initiale du tableau.

    Le jeu a 10 niveaux prédéfinis, et à partir de là, l’ordinateur génère des tableaux aléatoires sans aide disponible

    Quelques autres versions

    La version originale de Lights Out créee par Dan, sur Thomson
    A découvrir sur ZX-80
    En Hires et plus récente, le Lights Out de Romuald
  • Nuclear Invaders

    Un des premiers programmes conçu par Dan Cresp sur Oric, publié en Février 2013 et bien sur disponible sur oric.org dans la rubrique software. Comme vous l’avez deviné, avec son amour de la programmation et sa collection de micros, Dan a adapté son jeu sur plusieurs systèmes. Vous trouverez ci-dessous ses découvertes …

    Nous avons tous un équipement plus ou moins rétro, et la conception de ces programmes sont une bonne excuse pour pouvoir les utiliser et apprendre quelque chose à leur sujet.

    En règle générale, j’invente un jeu, ou adapte quelque chose d’existant, et le transfère sur 3 systèmes différents. Cela m’aide à comparer les performances et certaines fonctionnalités. Pour Nuclear Invaders, il existe une version pour DRAGON 32 (l’original, mais beaucoup plus simple) et pour MSX et ORIC. La meilleure des trois versions est sans aucun doute celle du MSX, mais la version ORIC a été meilleure que prévu.

    Un Dragon 32 en démonstration avec Nuclear Invaders

    En ce qui concerne l’ORIC, voici une partie des choses que j’ai apprises :

    • J’ai enfin appris à utiliser l’éditeur sans problèmes. Cela me rappelle beaucoup celui du ACORN BBC / ELECTRON.
    • Le programme a été entièrement développé sur un véritable ORIC-1 ; avec toutes les difficultés liées aux cassettes. Ainsi, si je l’enregistre en mode rapide avec l’ORIC-1, il ne se charge jamais bien. Si je le charge sur un ATMOS et l’enregistre rapidement avec l’ATMOS, l’ORIC-1 le charge sans problème. Curieux
    • Le programme a parfaitement fonctionné sur l’ORIC-1. Une fois passé à l’émulateur, l’OVNI ne s’est pas déplacé. Je l’ai essayé sous EUPHORIC (MSDOS) et ORICUTRON (Windows), en émulant ORIC-1 et ATMOS et cette partie ne fonctionnait pas bien. Je devais diviser la ligne 180 en deux (180 et 185) pour que cela fonctionne. Lorsque j’ai essayé sur ATMOS, j’ai constaté que cela ne fonctionnait pas non plus. Au final, le jeu garde la ligne divisée pour que le jeu fonctionne dans toutes les configurations.
    • En ce qui concerne le son, j’ai choisi d’utiliser les paramètres prédéfinis car leur modification pénalisait la jouabilité. Il y a de bonnes options sonores, mais en BASIC, il en coûte un max de les travailler.
    • Concernant la jouabilité, j’ai assez respecté la vitesse du jeu SPACE INVADERS, et nous sommes en BASIC !!!
      Le tir avance de 2 en 2 rangées, soit “curieusement” la séparation entre les envahisseurs. Cela permet de ne vérifier qu’une fois par cycle si vous touchez quelque chose. En outre, si le tir était plus rapide, la vitesse des envahisseurs devrait augmenter car le jeu serait trop facile. Presque tout a une explication

    Personnellement, j’aime davantage programmer en assembleur, mais il est clair que faire un jeu en BASIC est un défi particulier, car vous ne pouvez rien programmer. Vous devez très bien savoir si le projet est réalisable et très bien savoir le déboguer pour le lancer.

    Un Alien approche des réacteurs … la fin est proche !

    Notes finales

    La principale difficulté du jeu a été de parvenir à une bonne vitesse d’exécution, en tenant compte du fait que le jeu d’origine n’est pas particulièrement rapide. J’ai utilisé différentes techniques pour y parvenir. Parmi celles-ci, la sauvegarde des différentes animations des envahisseurs en fonction de leur position.

    Les graphismes ORIC sont composés d’une grille 6×8, 6 large par 8 haut. Comme aucun envahisseur n’occupe plus de 12 pixels, ils sont parfaitement positionnés. Pour le tank et l’OVNI je devais utiliser 3 caractères. J’ai redéfini les minuscules pour pouvoir lister le programme sans problèmes.

    L’écran de présentation est un “duplicata” du jeu d’origine, à l’exception du changement de nom du jeu. La forme de présentation est différente puisque tout est montré ici en une fois et non pas lettre par lettre.

    Ecran de présentation de Nuclear Invaders

    La boucle principale du développement va des lignes 100 à 260, soit 19 lignes au total. Ici, vous contrôlez le char, le tir, l’OVNI et un envahisseur. Comme le processus est rapide, tout se passe bien. Cependant, l’impossibilité d’utiliser ELSE en toute quiétude, ainsi que la limitation de la longueur des lignes ont rendu ce bloc plus compliqué que dans la version MSX.

    Pour détecter si le tir touche un ennemi, avant de montrer le tir dans sa nouvelle position, je regarde quelque chose, et si oui, je saute à la ligne 400. Ici, je vérifie verticalement si c’est l’OVNI, et sinon, c’est un envahisseur qui en position verticale, je calcule la ligne et en position horizontale la colonne.

    Dans cette version, je montre un précédent écran d’instructions où j’indique les touches de contrôle. Pendant ce temps, les graphiques redéfinissent et initialisent certaines matrices et variables.

    L’éditeur de l’ORIC est très similaire à ACORN BBC / ELECTRON, mais au lieu de la touche “COPY []”, le “CTRL + A” est utilisé ici. La différence est que dans ACORN, vous voyez ce que vous copiez dans le tampon, mais pas dans l’ORIC. Il est donc conseillé de vérifier avec un LIST de la ligne pour s’assurer que les modifications ont bien été enregistrées.

    Il y a un bogue important avec la commande ELSE du Basic de L’ORIC. Entre autres choses, il n’identifie pas bien à quoi le SI est associé et empêche le programme de le traiter correctement. Cela signifie que certaines lignes doivent être divisées en deux ou trois pour éviter des erreurs.

    En ce qui concerne l’enregistrement sur bande, l’ORIC-1 enregistre généralement mal en mode FAST (par défaut) et vous devez utiliser le paramètre «, S» pour le faire en mode SLOW, qui est beaucoup plus lent mais plus sûr. L’enregistrement rapide n’échoue pas dans l’ORIC ATMOS et l’ORIC-1 lui-même le charge sans problèmes.

    Pour ce qui est du son, domaine pour lequel je suis assez ignare, après plusieurs tests au cours desquels la performance d’ORIC est tombée trop bas, j’ai choisi d’utiliser les sons prédéfinis dont elle dispose. Les performances sonores de l’ORIC sont bonnes, mais leur utilisation est quelque peu fastidieuse.

    Avec la touche “ESC” on quitte le programme et en utilisant POKE # 26A, 3 activer le curseur et le clic de la touche.

    Quelques éléments concernant le programme

    La routine du jeu est divisée en 14 blocs

    • Déclaration de la matrice
    • Contrôle de notre tank
    • Contrôle de notre tir
    • Déplacement de OVNI
    • Déplacement des envahisseurs
    • Destruction d’un ennemi ou de l’OVNI
    • Perte d’une vie
    • Niveau de passage.
    • Présentation
    • Affichage de l’écran de jeu.
    • Chargement des graphiques. SPRITES et UDG.
    • Initialisation des variables et matrices du jeu.
    • Routines
    • Blocs graphiques.

    L’ensemble du programme occupe 79 lignes dont voici les principales :

    • 10 Définition des matrices du jeu.
    • 30 Routines d’appel pour lire les graphiques (3000), Affichage de la présentation (2000).
    • 100 Nous vérifions si notre char doit être déplacé, en le montrant dans la nouvelle position.
    • 130 S’il y a un tir à l’écran, nous le déplacerons, et nous verrons si un nouveau tir est réalisé (RETURN).
    • 170 S’il y a un OVNI sur l’écran, nous le déplaçons, et diminuons le compteur (W).
    • 200 Routine de déplacement des envahisseurs. Un envahisseur différent se déplace à chaque passage. D’abord, nous regardons s’il y a toujours des envahisseurs dans la position correspondante (3 par position), s’il y en a, nous avançons. Si la vitesse est assignée, nous la déplaçons, et si nous n’utilisons pas un nombre aléatoire, nous décidons de commencer à bouger. Pendant le mouvement, nous modifions son animation et voyons si il a atteint la centrale nucléaire.
    • 250 Compteur du prochain envahisseur à déplacer (M).
    • 260 Si tous les envahisseurs n’ont pas été détruits (T), nous retournons à REPEAT à 100, et s’ils ne restent pas, nous continuons jusqu’à 300
    • 300 Code de niveau de passe. Le passe est donné lorsque nous détruisons les 30 envahisseurs. Le contrôle de difficulté (C) indique que la largeur de la centrale nucléaire est augmentée.
    • 400 Cette routine est exécutée lorsque le tir détecte qu’il a touché quelque chose (sur 160). Ici, il est calculé si un envahisseur ou l’ovni a été touché et agit en conséquence. En 410, un envahisseur est détruit et en 450, l’OVNI est détruit.
    • 500 Routine en cours lorsqu’un envahisseur arrive à la centrale nucléaire. Un son est joué et une vie est supprimée. S’il reste des vies, nous recommençons le niveau et sinon, nous revenons à l’écran de présentation après une pause.
    • 2000 Écran de présentation du jeu
      La commande de déclenchement est enregistrée dans la variable (G).
    • 2100 Suppression de l’écran de présentation.
      Message joueur 1 pour être prêt et la centrale nucléaire est dessinée.
    • 2800 Positionnez et montrez les envahisseurs et notre tank à l’écran. Réinitialise la valeur de certaines variables.
    • 3000 Nous chargeons les graphiques des lignes de données dans la VRAM. D’abord, les SPRITES sont chargés, puis l’UDG.
    • 5000 Routine pour imprimer un numéro sur un marqueur.
      Passez la valeur dans (I) et la position dans (N).
    • 5200 Programme de suppression d’une partie de l’écran, en respectant les repères supérieur et inférieur.
    • 9000 Données du graphique UDG du jeu.
    • 9500 Données des envahisseurs selon la position et l’animation.
    Nuclear Invaders possède des sprites classiques et bien définis

    Le Jeu

    Les envahisseurs de l’espace sont de retour !!!

    En 1977, ils ont essayé d’envahir les villes de la Terre et ont échoué !
    Maintenant, ils sont de retour avec un plan diabolique : détruire les centrales nucléaires pour laisser la planète inhabitable.
    Avec l’aide de notre tank, nous devons éviter qu’ils atteignent leur objectif.

    Chaque niveau est composé de 30 envahisseurs (NDLR : je ne suis pas encore arrivé au bout des 30 premiers envahisseurs), après chaque vague il apparaîtra un centre plus grand. De ce fait, les envahisseurs doivent parcourir moins de distance pour atteindre et détruire notre planète.

    Bonus : Détruisez l’OVNI qui apparaît en haut pour obtenir des points supplémentaires.

    L’écran du 1er niveau.

    Quelques autres versions

    MSX, la plus belle version d’après Dan Cresp
    Nuclear Invaders sur ZX Spectrum
    Avec le CPC, le jeu en noir et blanc prend des couleurs !
    La version originale de Nuclear Invaders, sur Dragon 32
  • Galaxy Wars

    Infos générales

    Ce jeu est une adaptation de la version précédemment programmée pour le COMMODORE-16 et le ZX-SPECTRUM. Pour cela, j’ai copié, ligne par ligne, le code de la version C-16, en y apportant de petites modifications car elles ont le même nombre de lignes et peuvent toutes deux utiliser ELSE.

    Galaxy Wars sur C-16, base du développement de la version Oric
    Une des premières versions de Galaxy Wars, sur ZX

    Pour la première fois, j’ai utilisé un émulateur pour développer le jeu : EUPHORIC sous MS-DOS. Le résultat est très satisfaisant puisque cet émulateur permet, en appuyant sur F3, d’avoir un clavier qui correspond à celui du PC, cela permet de taper un programme très rapidement et confortablement. Prendre un listing ou charger / enregistrer un programme est tout aussi simple.

    Les graphismes originaux, basés sur des largeurs de 8 pixels de base

    La seule complication lors du développement de cette version a été le besoin de coder à nouveau tous les graphiques, car l’ORIC utilise des caractères de 6 pixels de large et non de 8 comme le reste des ordinateurs. Ainsi, la conception du missile est légèrement plus étroite et les vaisseaux supérieurs occupent 3 caractères de large au lieu de 2. Quoi qu’il en soit, je l’ai fait et si vous touchez un navire du côté droit, vous ne le détruisez pas.

    Des graphismes “handheld”

    En ce qui concerne le son, j’ai repris les routines standards.

    Et en ce qui concerne l’aspect, grâce au système particulier de mémoire d’écran de ce système, c’est la seule version dans laquelle j’ai vraiment été capable de reproduire l’effet «cellophane», en émulant les rayures colorées.

    Ecarts à la version TAITO d’origine:

    • Cette version est un peu plus large et beaucoup moins haute puisque dans le jeu original l’écran est vertical.
    • Il y a moins de rangées d’astéroïdes, en raison de la taille verticale.
    • Pendant l’ascension du missile, nous pouvons le contrôler horizontalement, mais nous ne contrôlons pas la vitesse.
    • Les envahisseurs n’accélèrent pas lorsqu’il ne reste que quelques-uns d’entre eux.
    • Les astéroïdes ne descendent pas lorsqu’ils atteignent les extrêmes.
    • Les scores sont limités à 50, 100 et 150 points.
    • Il n’y a pas d’animations sur les ennemis.

    Le contrôle des ennemis est très simple. Il y a 6 bandes avec les ennemis dans la position correspondante. Les lignes paires tournent à gauche et les lignes impaires à droite.

    Le bloc principal du programme n’occupe que 18 lignes. À chaque passage, je déplace une rangée d’ennemis et contrôle un tir ennemi. Le compteur est commun dans les deux cas (A).

    Pour gagner de la vitesse, je me suis débarrassé des animations des ennemis. J’ai choisi d’utiliser les graphiques des ennemis dispersés.

    Lors de collisions, je vérifie seulement si la tête du missile touche quelque chose, mais pas la partie inférieure. En ce qui concerne les bombes ennemies, seul celui qui est actif vérifie s’il touche quelque chose. Cela rend le jeu plus rapide, mais il se peut que nous touchions quelque chose et que rien ne nous arrive. Comme le processus est rapide, il y a un risque … et il vaut mieux ne pas y jouer.

    En ce qui concerne les performances, cette version fonctionne aussi bien (sinon autre chose) que les deux précédentes (C-16 et SPECTRUM).
    La vérité est que, malgré ses bugs, ce BASIC tire plus que bien.

    Eh bien, rien de plus, je ne peux qu’espérer que cela vous plaise.

    Je vous invite à l’essayer.

    Quelques éléments concernant le programme

    Le programme principal est divisé en 10 blocs:

    • Déclaration de matrice. Appel aux routines principales.
    • Contrôle notre plateforme ou missile.
    • Contrôle des envahisseurs et des astéroïdes.
    • Contrôle des tirs ennemis.
    • Détection d’impact de missile. Touchez un astéroïde, un envahisseur ou un tir ennemi.
    • Perdre une vie.
    • écran de présentation.
    • Début du jeu.
    • Plusieurs routines.
    • Données avec les graphismes du jeu.

    Comment le programme fonctionne-t’il, avec ses 88 lignes ?

    • 10 – Nous définissons les matrices du jeu.
    • 30 – Initialiser l’enregistrement (R) et aller à la routine qui redéfinit les graphiques.
    • 50 – Allons à la présentation du jeu.
    • 100 – Nous déplaçons la plate-forme ou le missile dans la direction indiquée.
    • 130 – Si le missile a été tiré (T = 2), il passe à 160.
    • 140 – Si la touche RETOUR est enfoncée, nous tirons le missile et supprimons la plate-forme.
    • 160 – Nous supprimons le missile et modifions la position verticale (Y).
    • 170 – Si la pointe du missile touche quelque chose, nous sautons à 400.
    • 180 – Nous mettons le missile dans la nouvelle position.
    • 200 – Nous déplaçons l’une des 6 rangées d’ennemis ou d’astéroïdes dans la direction correspondante. (B = 1: Droite, B = -1: Gauche).
    • 220 – Nous imprimons la ligne de l’ennemi.
    • 250 – Nous voyons si l’une des 6 bombes ennemies est active.
    • 260 – Si ce n’est pas le cas, nous voyons s’il y a un envahisseur en position sur l’écran pour activer le tir.
    • 270 – Si le tir touche la plate-forme ou si le missile passe à 500.
    • 400 – Si la position Y est supérieure à 5, c’est que nous avons frappé avec un astéroïde ou tiré et sauté à 500.
    • 410 – Nous calculons la position de l’envahisseur détruit.
    • 420 – On met l’explosion, un son retentit et on calcule les points (50, 100 ou 150).
    • 430 – Nous supprimons l’envahisseur de la ligne et soustrayons un envahisseur des autres (Q).
    • 450 – Nous montrons les points et faisons une pause (5500) et mettons à jour le marqueur.
    • 470 – En passant le niveau, nous augmentons sa valeur s’il est inférieur à 5.
    • 500 – Nous mettons une explosion à la position du missile et reproduisons un son.
    • 520 – Nous mettons à jour le marqueur de vie.
    • 550 – S’il n’y a plus de vies, le jeu se termine.
    • 2000 – Écran de présentation du jeu.
    • 2050 – Appuyez sur une touche pour effacer l’écran (5400) et faire clignoter le marqueur du lecteur 1.
    • 2500 – Nous avons monté les bandes des envahisseurs.
    • 2510 – Routine générant de manière aléatoire des bandes d’astéroïdes. Fuller dans les niveaux avancés.
    • 2570 – Initialisation des variables principales de l’item.
    • 5000 – Changer les couleurs, masquer le curseur et désactiver les majuscules.
    • 5002 – Instruction Écran pendant la lecture des graphiques.
    • 5010 – La routine commence pour redéfinir le jeu de caractères.
    • 5020 – Nous sauvegardons l’attribut color de chaque ligne de l’écran dans la matrice C $.
    • 5040 – Initialise la matrice de la hauteur des différents ennemis V ().
    • 5050 – Initialise la position des bombes ennemies.
    • 5100 – Affiche le marqueur de score ou l’enregistrement.
    • 5400 – Routine d’effacement de l’écran activant les couleurs correspondantes par ligne.
    • 5600 – Affiche les 6 rangées d’ennemis dans leur position et avec la couleur correspondante.
    • 9000 – Ligne de données avec graphiques du jeu.

    Le jeu

    Ce jeu vidéo d’arcade a été développé par Universal et fabriqué par Taito en 1979.

    L’objectif du jeu est de détruire la flotte d’envahisseurs dans la partie supérieure de l’écran. Pour cela, nous contrôlons une plate-forme avec un missile. Lors du tir du missile, vous devez éviter les astéroïdes et les tirs ennemis et toucher un envahisseur.

    Contrôles:
    Appuyez sur Z ou X pour déplacer la plate-forme et le missile vers la gauche ou la droite. Appuyez sur la touche RETOUR pour tirer le missile.

    Quelques autres versions

    Comparaison de la version C-16 avec la version originale
    Le C-16 en action !
    Comparaison de la version ZX Spectrum avec la version d’arcade originale
    le ZX Spectrum en action
  • Fonctionnement de la pile matérielle du 6502 (4/8)

    PARAMÈTRES ET SOUS-ROUTINES

    L’entrée et la sortie de données nécessaires au fonctionnement d’une sous-routine, peut se dérouler de quatre façons :

    1. En passant par les registres (A, X et / ou Y). C’est la meilleure méthode et la plus performante lorsqu’il s’agit de données constituées d’un ou deux octets
    2. En passant par des variables en RAM. Cette méthode est parfaite pour des données qui doivent rester en RAM et qui doivent servir à d’autres routines. Elle est souvent utilisée de manière conjointe à la première méthode.
    3. Les besoins en entrées de la sous-routine peuvent être intégrés au programme lui-même, juste après l’instruction JSR (comme par exemple une chaîne alphanumérique à afficher). Dans ce cas, la sous-routine utilise l’adresse de retour stockée sur la pile en guise d’adresse de début des données. Elle peut aussi utiliser un octet qui détermine le nombre de données ou un délimiteur afin de savoir de combien d’octets avancer l’adresse de retour avant d’exécuter l’instruction RTS (et d’éviter que le processeur ne traite les données comme une instruction).
    4. En passant par la pile matériel.
      • Pile en page 1 : le programme appelant empile la (ou les) données grâce à PHA, puis récupère le (ou les) résultats grâce à PLA, au retour de la sous-routine.
      • Pile virtuelle en page 0 : Idem précédemment, avec une pile de données différentes de la pile matérielle, ce qui peut faciliter certaines opérations sur la pile et l’adressage indirect. Par exemple, la pile de données peut être utilisée dans un processus en plusieurs étapes pour calculer une adresse, adresse qui à son tour peut être utilisée pour un accès indirect à un octet dans un tableau pour faire un calcul ou une opération logique.
      • Pile en RAM : Idem pile virtuelle en page 0, mais pouvant typiquement servir pour des nombres en virgule flottante autres que des adresses nécessitant les modes d’adressage disponibles en page 0.

    Examinons un exemple dans lequel il s’agit d’utiliser la pile matériel pour une donnée d’entrée. Dans cet exemple, notre sous-routine transforme un demi-octet hexadécimal en octet ASCII, avec un octet en entrée et un octet en sortie, le tout stocké sur la pile matériel.

    <do_stuff>      ; Get the nybble into A.  Allowable value is 0-F.
    PHA             ; Other code can be put between the PHA and the PLA, as long
    JSR  NYB2ASCII ; as it doesn't care if NYB2ASCII overwrites A and X. The
    PLA             ; subroutine's input and output are protected on the stack though.
    <do_stuff>      ; Do something with the ASCII output, like add it to a string, display it, etc..

    La sous-routine NYB2ASCII pourrait ressembler à ceci.

    NYB2ASCII:

    TSX

    LDA  $103,X     ; 103 reaches past the return address, to the input parameter.

    CMP  #$0A       ; Anything below $0A will end up in the $30’s.

    CLC             ; CLC before the BMI so we only have to do it once.

    BMI  n2a1       ; For 9 or less, skip the next instruction.

    ADC  #7         ; $0A becomes $11, $0B becomes $12, etc..  C is still clear.

     n2a1:  ADC  #$30       ; Whether the 7 got added above or not, this gives the ASCII.

    STA  $103,X     ; Put it on the stack, overwriting the input value.  Note

    RTS             ; that we read and overwrote the byte just behind the

    ; return address, leaving the return address undisturbed.

    Voici un autre exemple dans lequel deux nombres de 16 bits non signés en entrée sur la pile, sont multipliés pour donner un nombre de 32 bits non signé, en sortie sur la pile.

    On empile tout d’abord les deux nombres à transformer, chacun avec le poids fort en premier, de sorte que le poids fort prenne l’adresse la plus haute comme dans le fonctionnement habituel du 6502.

    <do_stuff>     ; Get high byte of first input,

    PHA            ; and push it.

    <do_stuff>     ; Get low  byte of first input,

    PHA            ; and push it.

    <do_stuff>     ; Get high byte of second input,

    PHA            ; and push it.

    <do_stuff>     ; Get low byte of second input,

    PHA            ; and push it.

    JSR  UM_STAR   ; Now you can call the subroutine below that does the multiplying.

                   ; If you pull the product off the stack now, the byte order will be:

    PLA            ; 2nd-highest byte

    <do_stuff>

    PLA            ; high byte

    <do_stuff>

    PLA            ; low byte

    <do_stuff>

    PLA            ; 2nd lowest byte

    <do_stuff>

    Selon les opérations que vous souhaitez réaliser ensuite, vous pouvez très bien laisser le premier résultat sur la pile.

    Si l’opération après la multiplication des deux nombres est de calculer la racine carrée pour en obtenir la raison géométrique, alors il convient de ne pas toucher à la pile tant que cette seconde opération n’est pas effectuée (la raison géométrique équivaut à une moyenne logarithmique. La raison géométrique de 1 et 100 est de 10, et non de 50,5).

    Pour accéder aux données stockées sur la pile sans les dépiler et dans l’ordre de votre préférence, vous pouvez continuer d’utiliser X comme index de pile (ainsi que décrit dans l’exemple UM_STAR ci-dessous). En cas de TSX, vous devrez ajuster l’index en tenant compte du fait que l’adresse de retour n’est plus sur la pile, et comme décrit ci-après, qu’un PLA a déplacé les résultats sur la pile de $101,X à $104,X. Par contre, sans TSX l’index est toujours valide et pointe bien sur les bonnes données.

    UM_STAR: LDA #0                 ; Unsigned, mixed-precision (16-bit by 16-bit input, 32-bit output)

             PHA                    ; multiply.  Add a variable byte to the stack, initializing it as 0.

             TSX                    ; Now 101,X holds that new variable, $102,X and $103,X hold the return address

             LSR $107,X             ;and $104,X to $107,X holds the inputs and later the outputs.

             ROR $106,X

             FOR_Y  16, DOWN_TO, 0  ; Loop 16x.  The DEY, BNE in NEXT_Y below will drop through on 0.

                 IF_CARRY_SET

                     CLC

                     PHA            ; Note that the PHA (and PLA below) doesn’t affect the indexing.

                        LDA $101,X

                        ADC $104,X

                        STA $101,X

                     PLA

                     ADC $105,X

                 END_IF

                 ROR

                 ROR $101,X

                 ROR $107,X

                 ROR $106,X

             NEXT_Y

             STA $105,X

             PLA                    ; Retrieve the variable byte we added at the top, cleaning up the stack.

             STA $104,X             ; Again note that the PLA changed S but not X, so the $104 is still $104.

             RTS

    Note : Dans de nombreux cas, il serait plus judicieux de nommer les données présentes sur la pile, en utilisant la macro EQU, plutôt que de se contenter de $101,X ou équivalent.

    Supposons maintenant que vous ayez besoin d’une sous-routine avec quatre octets en entrée et six octets en sortie.

    Afin de servir de la pile matérielle pour deux données supplémentaires en sortie, il faut donc résoudre le problème de l’adresse de retour qui ne sera pas en haut de la pile lors du RTS.

    La meilleure méthode consiste à faire de la place sur la pile avant d’appeler la sous-routine :

    PHA              ; Push two dummy bytes onto the stack to hold the

    PHA              ; positions open for outputs of the subroutine called below.

    <do_stuff>       ; Prepare the subroutine input bytes to be passed.

    JSR  subroutine  ; Without changing the stack pointer, the subroutine

    ; can now give you two more bytes of output than input.

    Autre cas pratique, celui où vous auriez besoin de plus de données en entrées qu’en sortie.

    Dans ce cas, il suffit de dépiler les données inutiles après être sorti de la sous-routine.

    Ainsi, pour restaurer la hauteur de pile, faites :

    <do_stuff>        ; Set up inputs, reserve output byte places in stack, etc..

    TSX               ; Mark the current stack position.

    <do_more_stuff>

    JSR  <subroutine>

    <do_stuff>        ; Handle outputs, etc..

    TXS               ; Restore stack to the marker set earlier, possibly also

    ; to put certain outputs at the top.

    Évidemment le registre X ne devra pas être modifié par la sous-routine, ou alors il faudra le sauvegarder à son tour sur la pile.

    Comme décrit précédemment, la pile matériel n’est pas la seule solution en tant que pile de stockage. Une pile en page zéro (ZP) présente par exemple l’avantage de disposer de modes d’adressages supplémentaires.

    Utiliser une autre pile que la pile matériel permet aussi de résoudre le problème suivant : admettons que la pile matériel en page 1 serve pour passer des paramètres à une sous-routine qui elle-même doit fournir des paramètres à autre sous-routine. Lors de l’entrée dans la seconde sous-routine, l’adresse de retour se retrouve donc désormais au sommet de la pile, tandis que les données d’entrée sont décalées vers le bas, ce qui donnera à coup sûr des résultats erronés.

    Mettons en œuvre cet exemple avec la sous-routine GEOMEAN.

    GEOMEAN: JSR  UM_STAR  ; Multiply two 16-bit unsigned inputs on the stack.  Get 32-bit product.

             JSR  SQRT     ; Take a 32-bit input on the stack and get a 16-bit output, leaving two

             RTS           ; dummy bytes on stack (problem: GEOMEAN’s return addr is still on top!)

    Supposons que vous commenciez par empiler $1234 sur la pile matériel (poids fort en premier, sur le dessus de la pile), puis que vous empiliez $5678 avant d’appeler la sous-routine GEOMEAN.

    Lorsque le pointeur de programme attend la première instruction après PHA et TSX dans le programme UM_STAR, UM_STAR part à la recherche du premier nombre à multiplier ($1234 dans notre exemple), censé se trouver aux adresses $106,X et $107,X, puis du second nombre ($5678 dans l’exemple) qui se trouve aux adresses $104,X et $105,X. L’adresse de retour se trouve quant à elle indexée par $102,X et $103,X.

    Il y a cependant maintenant deux adresses de retour au sommet de la pile : celle qui a fait appel à UM_STAR et celle qui a fait appel à GEOMEAN. UM_STAR va donc ignorer $1234 et multiplier $5678 avec l’adresse de retour de GEOMEAN, entraînant ainsi un résultat erroné et l’écrasement de l’adresse de retour de GEOMEAN. C’est le plantage assuré !

    Vous vous apercevez également que le fait d’utiliser JMP au lieu de la paire d’instruction JSR / RTS peut entraîner des problèmes d’incompatibilité liés à une mauvaise gestion des données empilées sur la pile matériel.

    La pile en page zéro permet de contourner ce genre de problème, tout simplement parce que l’adresse de retour n’est pas empilée sur cette dernière.

    En plus de l’adressage de la page zéro qui est plus performant que celui de la pile matériel, signalons aussi les bénéfices suivants :

    • Inutile de replacer les paramètres d’entrée sur la pile sans arrêt, avec pour résultat immédiat une exécution plus rapide des étages de la pile ;
    • Réduction des effets liés à la récursivité (ce sujet sera abordé dans un prochain article) ;
    • Possibilité de placer plusieurs type (et quantité) de valeurs ;
    • Code plus compact qui permet un meilleur fonctionnement car plus besoin de devoir empiler/dépiler des paramètres lors de l’appel de sous-routines.

    Revenons maintenant à notre sous-routine qui transforme un demi-octet hexadécimal en octet ASCII et voyons comment réaliser cette transformation en utilisant une pile ZP.

    X servira de pointeur de pile, initialisé en début de programme pour indexer de la manière suivante $00,X.

    Pour empiler en page zéro, il faut donc faire un DEX puis stocker en $0,X.

    Voici ce que donne la routine modifiée pour la pile ZP :

    <do_stuff>      ; Get the nybble into A.  Allowable value is 0-F.

    DEX

    STA  0,X        ; Other code can be put between the STA and the LDA, and

    JSR  NYB2ASCII  ; NYB2ASCII’s input and output are protected on the stack.

    LDA  0,X        ; X must be preserved as the data stack pointer of course.

    INX

    <do_stuff>      ; Do something with the ASCII output, like add it to a string, display it, etc..

    Note : Si vous avez besoin du registre X dans le reste de votre programme, il vous faudra le sauver sur la pile matériel.

    La sous-routine NYB2ASCII devient :

    NYB2ASCII:              ; (The initial TSX is no longer necessary.)

            LDA  0,X        ; Get the input parameter from the data stack.

            CMP  #$0A       ; Anything below $0A will end up in the $30’s.

            CLC             ; CLC before the BMI so we only have to do it once.

            BMI  n2a1       ; For 9 or less, skip the next instruction.

            ADC  #7         ; $0A becomes $11, $0B becomes $12, etc..  C is still clear.

     n2a1:  ADC  #$30       ; Whether the 7 got added above or not, this gives the ASCII.

            STA  0,X        ; Put it on the data stack, overwriting the input value.  Note

            RTS             ; that the return address is not on this stack to worry about.

    Procédons selon la même logique pour la sous-routine UM_STAR.

    UM_STAR: DEX                    ; Add a stack byte to use as a temporary variable.

             LDA  #0

             STA  0,X               ; 0,X addresses the temporary variable.  These are in ZP.

             LSR  4,X

             ROR  3,X

             FOR_Y  16, DOWN_TO, 0

                 IF_CARRY_SET       ; The 1st time thru the loop, A needs 0; so don’t use STZ above.

                     CLC

                     PHA

                        LDA  0,X

                        ADC  1,X

                        STA  0,X

                     PLA

                     ADC  2,X

                 END_IF

                 ROR

                 ROR  0,X

                 ROR  4,X

                 ROR  3,X

             NEXT_Y

             STA  2,X

             LDA  0,X

             STA  1,X

             INX                    ; Take back the stack byte we used as a temporary variable.

             RTS

     ;——————

    Ici, un octet est empilé sur la pile ZP grâce à l’instruction DEX (puis dépilé grâce à INX), de la même manière que pour PHA (puis PLA). La pile ZP permet donc d’éviter d’avoir à sauver/restaurer l’index X.

    Il est en outre possible de se passer des instructions DEX/INX en adressant le nouvel octet grâce à $FF,X qui permet de rester en page zéro et qui économise au passage 2 octets et 4 cycles d’horloge.

    Assurez-vous cependant de n’avoir aucune sous-routine ou interruption susceptible d’écraser cet octet, car X semble indiquer que cet emplacement est libre.

    La sous-routine GEOMEAN quant à elle, peut désormais s’écrire :

    GEOMEAN: JSR  UM_STAR  ; Multiply two 16-bit unsigned inputs on the stack and get a 32-bit product.

             JMP  SQRT     ; (JSR, RTS)  Take a 32-bit input on the stack and get a 16-bit output.

     ;——————

    Voici dans le détail les avantages à travailler avec des piles séparées :

    • Le nombre d’octets entrés est indépendant du nombre d’octets sortis. En d’autres termes, nul besoin de nettoyer les octets de sortie factices ni de placer des octets factices sur la pile ;
    • En disposant de données séparées, les adresses de retour ne sont pas traitées comme des données. Le paramétrage devient implicite ;
    • Une sous-routine peut faire appel à une autre sous-routine sans avoir à faire le ménage sur la pile matériel (qui devient désormais la pile de retour) ;
    • La pile matériel est maintenant disponible pour d’autres usages tels que la gestion des limites et des index de boucles (y compris des boucles imbriquées) ;
    • L’ajout d’une nouvelle pile permet d’augmenter l’espace de stockage et d’éviter un crash sur la pile matériel ;
    • Grâce aux modes d’adressages du 6502 en page zéro (ZP,X), nous pouvons adresser la pile ZP très facilement.

    Voyons justement comment mettre à profit les modes d’adressage ZP,X dans l’exemple de routine de déplacement suivante.

    Nous appellerons cette routine CMOVE, pour “character move”.

    Avant de faire tourner cette routine, nous plaçons six octets sur la pile ZP, sous la forme de 3 nombres de 16 bits (poids fort sur l’adresse la plus haute dans chaque cas) :

    • Adresse de départ
    • Adresse de destination
    • Taille de mémoire à déplacer (pouvant aller jusqu’à quelques dizaines de milliers d’octets)

    CMOVE:  LDA  0,X      ; “See-move” Character (memory) move  ( from to len — )

            ORA  1,X

            BEQ  POP3     ; If remaining length is 0, branch to POP3 which is just 6 INX’s, then RTS.

            LDA  (4,X)    ; Get a byte and

            STA  (2,X)    ; transfer it.

            INC  4,X

            BNE  cm1$

            INC  5,X      ; Increment the source addr

     cm1$:  INC  2,X

            BNE  cm2$

            INC  3,X      ; and the destination addr,

     cm2$:  DEC  0,X      ; decrement the count left,

            LDA  0,X

            CMP  #$FF

            BNE  CMOVE

            DEC  1,X

                          ; and go back up for another loop.  If we’re done,

            BRA  CMOVE    ; that fact will be caught in the first three lines (BRA is for 65c02 only and stands for Branch Always).

    Quand faut-il éviter l’emploi d’une pile pour l’usage de paramètres d’entrées / sorties dans des sous-routines ?

    Déplacer des données dans une pile requiert du temps processeur.

    Aussi, si vous devez manipuler une grande quantité de données, comme une chaîne alphanumérique, un tableau ou même une matrice, placez là ailleurs en mémoire et indiquez son adresse de stockage sur la pile.

    RÉCURSIVITÉ

    Une sous-routine peut présenter la particularité d’être récursive, à savoir qu’elle peut s’appeler elle-même à de nombreuses reprises.

    Afin d’éviter que ce genre de sous-routine ne reboucle indéfiniment, entraînant une surcharge de la pile matériel, il faut prévoir une condition qui permette de quitter la sous-routine au bout de x boucles récursives.

    Lorsque cette (ou ces) condition(s) est respectée, on quitte alors la sous-routine par une série de RTS.

    Chaque sous-routine nidifiée (imbriquée) peut disposer de ses propres variables ayant  pourtant des noms semblables.

    Prenons l’exemple de la fonction de Fibonacci qui est utilisée en calculs financiers, lors de la génération de nombres pseudo aléatoires, dans des algorithmes de tri et de recherche ou encore pour la compression de fichiers audio.

    fiboIO:    SETL $103    ; For clarity, give names to the locals.

    fiboTemp:  SETL $102    ; (Record of X will be at $101,X.)

    FIBO:  CMP  #2          ; Test the input.

           BCC  end         ; If it’s 0 or 1, so is the output, so just end.

                            ; This prevents endless nesting, too.  Otherwise,

           PHA              ; create local variable fiboIO, and store A there.

           PHA              ; Create another local variable byte, fiboTemp.

                            ; (Its initial value doesn’t matter.)

           PHX              ; Push X too, to protect the calling routines.

           TSX              ; Prepare X to use for stack-relative addressing.

           DEA              ; Get the n-1 term

           JSR  FIBO        ; and calculate the number for it (recursing here)

           STA  fiboTemp,X  ; and store its result in local variable fiboTemp.

           LDA  fiboIO,X

           DEA

           DEA              ; Now get the n-2 term

           JSR  FIBO        ; and calculate the number for it (recursing here).

           CLC

           ADC  fiboTemp,X  ; Add those two together, and

           STA  fiboIO,X    ; store the answer.  Don’t forget the “,X”!

                            ; Note that there is no looping.

           PLX              ; Restore X for the calling routines.

           PLA              ; Pull fiboTemp off the stack and discard it.

           PLA              ; Pull the answer (fiboIO) off the stack into A.

    end:   RTS

    Cette sous-routine tient sur 34 octets et n’utilise pas la RAM pour le stockage, hormis bien sûr la pile matériel.

    La donnée d’entrée doit rester inférieure à 14 car la donnée de sortie est sur 8 bits et le nombre de Fibonacci pour 13 est 233.

    Avec une réponse sur deux octets ou plus, nous pourrions avoir une donnée d’entrée plus grande sans manquer de place sur la pile, même si ce genre de sous-routine en consomme beaucoup.

    La place disponible sur la pile est largement suffisante pour des sous-routines simples, mais rappelons que les routines récursives ne sont pas des routines normales.

    À suivre.

  • Fonctionnement de la pile matérielle du 6502 (1/8)

    EN GUISE D’INTRODUCTION

    Fasciné par certaines utilisations détournées de la pile de matériel du 6502, telles que les programmes de protections, j’ai voulu en apprendre davantage sur son fonctionnement.
    Je vous propose donc ici la traduction partielle d’un sujet dont la version originale se trouve à l’adresse suivante : http://wilsonminesco.com/stacks/

    QUELQUES DEFINITIONS

    Le type de pile dont il est question ici est une partie de la mémoire vive utilisée pour le stockage temporaire de données, qu’il s’agisse de variables ou d’adresses, avec comme mode d’accès, dernier entré, premier sorti (Last In First Out).
    On compare généralement ce type de pile à une pile d’assiettes dans un meuble à ressorts que l’on peut trouver à côté d’un buffet restaurant.

    La quantité de compression ou d’étirement des ressorts est proportionnelle au poids des assiettes que le meuble contient, de sorte que tant que le meuble n’est pas rempli complètement, un employé peut ajouter n’importe quel nombre d’assiettes récemment lavées et les assiettes seront toujours dans la même position au sommet, prêtes pour le prochain client.

    D’habitude, ni l’employé ni le client n’ont besoin de savoir combien d’assiettes chaudes sont stockées en dessous.

    On peut aussi prendre l’exemple d’une tige métallique avec un tas de papiers embrochés.  Vous pouvez voir les documents qui sont sous le sommet, et vous pourriez même lire ou écrire sur eux dans une certaine mesure sans retirer ceux qui sont au-dessus, mais le retrait ou l’ajout réel de documents a toujours lieu par le sommet.

    Le contraire d’une pile (ou mémoire LIFO) serait une mémoire de type premier entré, premier sorti (FIFO) ou file d’attente, qui peut dans ce cas être comparée au convoyeur d’une machine à rayons X dans un aéroport. Sur cette machine, les affaires passent d’un côté à un autre des rayons X, dans le même ordre qu’elles sont entrées.

    Contrairement à la pile d’assiettes, une pile de nombres dans la mémoire de l’ordinateur ne se déplace pas de haut en bas quand quelque chose est ajouté ou enlevé de la pile. Il serait en effet contre-productif de déplacer toutes ces données vers le haut ou vers le bas comme pour les assiettes, alors qu’il est plus simple d’utiliser un pointeur réglable pour savoir où se trouve le haut ou, en fait, dans le cas de la pile matériel du 6502, quelle est l’adresse RAM qui est la prochaine disponible pour mettre quelque chose sur la pile.

    Ce pointeur est le registre S (Stack pointer) du 6502.

    Dès lors, si l’analogie du distributeur d’assiettes ne tient plus, pourquoi l’évoquer ?

    En fait, ce qu’il convient surtout de retenir ici, c’est qu’habituellement, on ne s’occupe que de ce qui se trouve au-dessus de la pile, sans se soucier de la profondeur de celle-ci, comme un navire en pleine mer qui flotte de la même manière que la profondeur de l’eau soit de 100 mètres ou de 1000 mètres. Tant qu’une action ou une routine dispose des éléments de la pile dont elle a besoin, peu importe combien d’autres éléments sont plus bas dans la pile, en attendant d’être utilisés par d’autres routines ou parties d’une routine.

    La pile du processeur 6502 permet de stocker des adresses pour des sous-routines et des interruptions (moins fréquentes que pour des sous-routines) ainsi que des variables.

    C’est dans la première page du 6502 ($0100-$01FF) que réside la pile de matériel. Les tests montrent d’ailleurs que contrairement à une légende urbaine, cette zone mémoire est beaucoup plus grande que ce dont le programmeur a réellement besoin.

    Le pointeur de la pile de matériel est le registre S qui n’a que 8 bits de largeur; le 6502 ajustant automatiquement le numéro de page à $01 pour tous les accès à la pile. La pile de matériel en page 1 se développe vers le bas, et non vers le haut. Toute routine de réinitialisation doit donc initialiser le pointeur de pile au sommet de l’espace disponible. Il s’agit habituellement de l’adresse $01FF. Cette routine s’écrira simplement :

    LDX  #$FF ; Initialise X au sommet de la pile
    TXS ; Transfère dans le pointeur de pile

    Puisque la pile se développe vers le bas, le pointeur de pile se décrémente après chaque octet poussé sur la pile, et s’incrémente avant que chaque octet ne soit tiré hors de la pile.

    ADRESSES DE SOUS-ROUTINE ET NIDIFICATION

    Une sous-routine est une partie de votre programme dont vous avez besoin à de multiples reprises et à différents endroits. Au lieu d’avoir une copie de cette routine à chaque endroit où vous en avez besoin, vous l’écrivez une fois pour toutes et vous l’appelez depuis la portion de programme requise. Le compteur de programme se rend à cette sous-routine puis doit pointer de nouveau sur la routine d’appel pour reprendre l’exécution. Pour savoir où revenir, le compteur de programme (donc l’adresse de retour) a au préalable été sauvée sur la pile.

    C’est ce que réalise l’instruction JSR : « Jump, Saving Return address » (JSR est habituellement traduit par « Jump to SubRoutine », ce qui fonctionne tout aussi bien mais n’est pas tout à fait exact.) Supposons maintenant que cette routine doive en appeler d’autres. Comme chaque JSR met l’adresse de retour sur la pile, les adresses de retour nécessaires en premier lieu seront les dernières stockées sur la pile.

    La nidification est ce qui convient de mieux pour ce genre de situation. On appelle nidification (ou imbrication), le fait qu’une sous-routine en appelle une autre, qui à son tour en appelle une autre, et ainsi de suite. Si vous n’aviez pas besoin de la nidification, vous pourriez inscrire l’adresse de retour dans une variable ou un registre; mais vous vous apercevez que dès la première nidification, la variable serait écrasée et la première adresse de retour serait perdue.

    C’est là que la pile entre en jeu.

    Considérons à présent la nidification des sous-routines ci-dessous.

    Dans ce scénario, la pile contiendra les nombres hexadécimaux suivants aux points  (a), (b) et (c) ; (sans compter ce qui aurait déjà pu être sur la pile avant (a).

      (a)(b)(c)
    Sommet pile (*) $1915 $2632 $2F43
        $1915 $2632
          $1915

    (*) Top Of Stack (TOS)

    Ce tableau semble comporter des erreurs. En effet, au point (a)  l’adresse de la prochaine instruction est censée être $1916, et non $1915.

    En fait, l’instruction RTS (Return From subroutine) incrémentera l’adresse de retour et l’exécution du programme reprendra bien à $1916 (ce qui n’est pas le cas pour RTI, ReTurn-from-Interrupt, et fera l’objet d’un autre article).

    Puisque le bus de données et la mémoire du 6502 sont adressés sur 8 bits, chaque adresse de retour évoquée ci-dessus nécessite deux emplacements dans la pile. Lorsque JSR pousse l’adresse de retour (moins 1) sur la pile, il pousse d’abord l’octet haut, puis l’octet bas. N’oubliez pas que la pile grandit, et que le pointeur de la pile se décrémente lorsque les octets sont poussés sur la pile, de sorte que l’ordre d’octet résultant est l’octet de poids faible dans l’adresse mémoire la plus basse, comme le sont les autres adresses dans le 6502.

    Supposons par exemple que la valeur du pointeur de pile (registre S) était $EF avant que le code n’entre dans le diagramme de pile ci-dessus. Cela donnerait une occupation de l’espace mémoire de la pile selon le tableau suivant (ne sachant pas ce qui figurait de $EF à $FF, ces octets ne sont pas cartographiés).

    Lorsque vous quittez les sous routines et que vous revenez à la routine principale, voici à quoi ressemble la mémoire de la pile.

    Les nombres entre parenthèses sont toujours là, mais cette partie de la mémoire de la pile est libérée. Retirer quelque chose de la pile signifie simplement que le processeur en lit le contenu et incrémente ensuite le pointeur de la pile. Pouvez-vous utiliser ces chiffres, puisqu’ils sont toujours là ? Si vous avez des interruptions, la réponse est non, car une interruption pourrait survenir à tout moment et les écraser. Cela violerait le protocole LIFO et le processus des interruptions nécessite que ce protocole soit respecté.

    À suivre.