ZX Spectrum de coronavirus et de bâtons (en fait, pas vraiment)

L'auto-isolement est le fléau de l'humanité moderne. Ici, par exemple, dans la ville voisine, les vendredis et samedis, après des applaudissements traditionnels à 20 heures, ils organisent des concerts de balcon. Ils se sentent bien, leurs maisons sont hautes et leurs voisins sont jeunes. Nos voisins sont des personnes âgées, ils ne veulent pas de concerts. Et les maisons sont basses, ce qui ne contribue pas non plus au farniente. Par conséquent, nous sommes sauvés comme nous le pouvons.

L'après-midi, sur un site isolé, pas si mal. Comme le soir, jusqu'à ce que les enfants s'endorment. Comme les premiers jours, jusqu'à épuisement des livres et ennui de la série. Mais un mois passe, suivi d'un autre. L'âme a besoin de vieux fer. Mais pas seulement, mais avec perversion. Et j'ai fouillé dans les poubelles et y ai trouvé le processeur Zilog Z80:

image

Je dois dire que j'aime vraiment ce processeur. La seule chose que j'aime chez lui est probablement la 486e puce, mais mes mains ne l'atteindront pas bientôt, car il est difficile et inutile de l'insérer dans la planche à pain. Je dois souder. Mais je ne veux pas encore souder. Et plus encore que le Z80 lui-même, j'adore l'ordinateur ZX Spectrum construit sur sa base. Mais le Spectrum natif souffre d'un malheur sous la forme d'une puce logique personnalisée ULA, et ses clones sur le côté lâche ne sont pas particulièrement difficiles à construire et à affiner, mais toujours pas pour le modèle de maquette, et en effet, pourquoi tant de soucis quand il y a Arduino?

Un lecteur intelligent, équilibré et adéquat arrêtera la lecture ou lancera quelque chose comme «1 microcircuit FPGA pourra accueillir la classe informatique Spectrum» avant de s'arrêter. Je ne suis pas intelligent, pas adéquat, bien qu'équilibré, mais je ne sais que sur FPGA que c'est cool. Je ne peux que faire de l'arduino. Mais veut vraiment piquer les fils dans le Z80. Très.

Commençons

Bien sûr, commençons. Mais d'abord, Disclaimer., , , . — . , , . , (, ?), , , , , . , , , , , .

Pour commencer, qu'est-ce qu'un ordinateur 8 bits adéquat? Il s'agit en fait d'un processeur connecté à la ROM et à la RAM, et sur le côté se trouvent deux compteurs à afficher sur l'écran composite. Parfois, une minuterie pour grincer. ZX Spectrum n'est pas différent du schéma traditionnel, sauf pour un mais. Il y a un ULA. Il s'agit en fait du «chipset» du Spectrum. ULA gère les périphériques, tels qu'un magnétophone, un tweeter, un clavier (partiellement), la sortie vers l'écran (oui, oui, la carte vidéo intégrée est apparue dans le chipset Spectrum avant de devenir mainstream). Il y avait aussi un mémorial partagé, les 16 premiers Ko de RAM (adresses de 0x4000 à 0x5B00). De là, l'ULA a dessiné un composite sur l'écran, et pour que le Z80 ne puisse pas tâtonner là où ce n'était pas nécessaire, l'ULA pourrait arrêter le processeur, si nécessaire, car le signal d'horloge du Z80 en provenait. Autrement dit, si ULA a travaillé avec la mémoire et détecté,que le processeur se glisse également dans cette mémoire (pour cela, il surveillait constamment MREQ et les lignes A15 et A14), il arrêtait simplement le cadencement du processeur jusqu'à ce qu'il finisse de faire ce dont il avait besoin. Soit dit en passant, afin d'éviter la corruption des données sur le bus, des parties du bus côté processeur et côté ULA ont été délimitées par ... des résistances ... De plus, la mémoire était assise sur le bus du côté ULA et, en conséquence, en cas de collision, a complètement ignoré les données et l'adresse du côté processeur.complètement ignoré les données et l'adresse du processeur.complètement ignoré les données et l'adresse du processeur.

De plus, le Spectrum avait des ROM (adresses 0x0000 - 0x3FFF) et sa propre mémoire de processeur (0x8000 - 0xFFFF), à laquelle ULA n'avait pas accès, et qui fonctionnait plus rapidement que 16 Ko de mémoire partagée, car le processeur n'interférait pas avec ULA dans ce domaine . Mais ce n'était que sur la version 48K de l'ordinateur. Dans la version de base, il n'y avait que des ROM et 16 KiB compatibles avec ULA. Nous allons commencer avec elle.

Il est pratique que le processeur Z80 puisse régénérer la DRAM, mais d’une manière ou d’une autre, je ne veux pas m'en soucier, car la SRAM est plus facile à trouver et je n'ai pas de multiplexeur (ou je ne le trouve pas). Nous allons donc utiliser SRAM. Pour commencer, nous assemblerons le squelette principal, sur lequel tout le reste pourra être accroché. Le squelette sera un processeur, une ROM avec firmware, mappée à l'adresse de la ROM du Spectrum, de la RAM, mappée sur les 16 premiers Ko après la ROM et quelques puces pour tout envelopper ... Je dois dire que pendant longtemps je n'ai pas voulu faire pivoter, car j'ai des dispositions chinoises 1 $ pour 2 pièces en ibee. Mais, pour moi, le tapage en vaut la peine. Si vous ne voulez pas vous amuser pendant longtemps, prenez de bonnes dispositions.

Alors, installez le Z80.

Comme vous pouvez le voir sur la fiche technique ,



Le processeur possède 40 broches divisées en groupes: bus d'adresses, bus de données, contrôle système, contrôle processeur, contrôle bus processeur, puits, alimentation et horloge. Toutes ces conclusions ne sont pas utilisées dans des systèmes réels, tels que le spectre ZX, comme le montre le diagramme.. Du groupe "contrôle du processeur" dans le spectre, seuls les signaux INT et RESET sont utilisés. Le signal M1 n'est pas utilisé dans le groupe «contrôle du système», le groupe «contrôle du bus» n'est pas utilisé du tout. Il y a une raison pour cela. Les anciens systèmes 8 bits étaient très simples, et le spectre a été créé avec l'idée d'être aussi simple que possible et tout ce qui pouvait être ignoré a été ignoré. Bien sûr, les fabricants de périphériques pouvaient utiliser des interruptions (signaux INT et NMI), ils étaient acheminés vers l'emplacement d'extension, mais NMI n'était pas utilisé dans le spectre lui-même. Comme le montre le diagramme ci-dessus, les signaux NMI, WAIT, BUSREQ sont tirés par des résistances de puissance, car ce sont des entrées activées de bas niveau (cela est indiqué par la barre au-dessus du nom du signal), et il doit y avoir une unité logique (c'est-à-dire + 5V) pour que Dieu nous en préserve, le signal inutile n'a pas fonctionné. Et voici les résultats, BUSACK, HALT, M1,et pendre en l'air, pas connecté à quoi que ce soit. Soit dit en passant, il n'y a pas de bouton de réinitialisation dans le spectre. La broche de réinitialisation est connectée viaChaîne RC à alimenter (RESET est également activé par un niveau bas), car, selon la fiche technique, après avoir allumé RESET, au moins 3 cycles d'horloge doivent être actifs pour que le processeur entre en mode de fonctionnement. Ce circuit RC maintient un niveau bas jusqu'à ce que le condensateur soit chargé à un niveau élevé via une résistance.

Passons brièvement en revue les autres signaux:
M1. On n'a pas besoin. Il signale que le processeur a commencé à exécuter l'instruction suivante.
MREQ. J'en ai besoin. Il signale que le processeur accède à la mémoire. Si ce signal devient faible (c'est-à-dire connecté à la masse d'alimentation), nous devrons alors activer la mémoire connectée au processeur.
IOREQ . J'en ai besoin. Il signale que le processeur accède à un périphérique. Par exemple, au clavier.
RD . J'en ai besoin. Indique que le processeur lira les données de la mémoire (si MREQ est actif) ou des périphériques (IOREQ).
Wr . J'en ai besoin. Signale que le processeur écrira des données dans la mémoire / les périphériques.
RFSH . J'en ai besoin. En général, ce signal est nécessaire pour la mémoire dynamique (DRAM). Je n'envisage pas de l'utiliser, car son adressage est plus difficile (matrice, non linéaire, c'est-à-dire qu'il faudra installer un multiplexeur), et en général, à notre époque, les microcircuits SRAM de faible capacité sont plus faciles à obtenir. Mais puisque le processeur lui-même régénère la DRAM en triant les adresses sur le bus mémoire, ce signal nous permettra d'ignorer les cycles de régénération et de ne pas activer la mémoire avec la RFSH active.
HALT . Pas besoin. Indique que le processeur est arrêté.
ATTENDRE. Pas besoin. Ce signal est nécessaire pour demander au processeur de s'arrêter et d'attendre un peu. Généralement utilisé par les périphériques lents ou la mémoire. Mais pas dans le spectre. Lorsque dans les périphériques Spectrum (ULA) décide d'arrêter le processeur, il cesse simplement de lui envoyer un signal d'horloge. C'est plus fiable, car après avoir reçu WAIT, le processeur ne s'arrête pas immédiatement.
INT . Interrompre. Ce n'est pas encore clair. Nous supposons que ce n'est pas encore nécessaire. Ensuite, nous le découvrirons.
Le NMI . Interruption démasquable. Super interruption. Pas nécessaire.
RÉINITIALISER . Sans elle, elle ne volera pas.
BUSREQ . Pas besoin. Demande au processeur de se déconnecter des bus de données / adresses, ainsi que des signaux de commande. Cela est nécessaire si un appareil souhaite contrôler le bus.
BUSACK. Pas besoin. Sert à informer l'appareil qui a effectué BUSREQ que le bus est libre.
L'HORLOGE . Signal d'horloge. De toute évidence, il est nécessaire.
Des repas sont également nécessaires. Gloire aux développeurs, seulement + 5V / GND. Pas de stress 3 pour vous.
A0-A15 est le bus d'adresse. Sur celui-ci, le processeur affiche soit une adresse mémoire (MREQ est active) soit une adresse de port d'E / S (IOREQ est actif) avec les appels appropriés. Comme vous pouvez le voir, le bus a une largeur de 16 bits, ce qui vous permet d'adresser directement 64 Ko de mémoire.
D0-D7 - bus de données. Le processeur lui délivre (WR actif), ou y lit (RD actif) les données demandées.

Donc, nous allons placer le processeur sur la planche à pain. Ses conclusions se situent donc physiquement:

image

Connectez l'alimentation (broches 11 et 29). Au cas où, j'ai également mis un condensateur de 10 pF entre ces jambes. Mais il ne m'a pas aidé à la fin. Les broches 27, 23, 18 peuvent rester déconnectées de quoi que ce soit. Les broches 26, 25, 24, 17, 16 sont connectées via des résistances (j'ai utilisé 10 kOhm) à l'alimentation. J'ai amené le bus d'adresse (broches 1-5 et 30-40) de l'autre côté de la planche à pain, et le bus de données (broches 7-10 et 12-15) sur un bus de données distinct fabriqué à partir de prototypes de bus d'alimentation.
Les broches 6 (signal d'horloge) et 26 (RESET) sont connectées (plus tard) à Arduin afin que vous puissiez contrôler le processeur à partir de celui-ci.

Il s'est avéré comme ceci:



Jusqu'à ce que vous fassiez attention aux fils d'en haut, ils partent de la ROM, nous y reviendrons un peu plus tard. De plus, sur la photo à côté du processeur, une puce de plus est visible. Nous en avons besoin pour décoder les bits supérieurs de l'adresse. Comme je l'ai dit plus haut, il existe 3 types de mémoire dans le spectre. Les 16 Ko inférieurs de l'espace d'adressage sont ROM. Par conséquent, si les bornes A14 et A15 sont dans un état bas (0 Volt), nous devons tout déconnecter sauf la puce ROM du bus. Ensuite, 16 Ko de mémoire partagée. En conséquence, nous devons connecter cette mémoire au bus (et déconnecter le reste) si la sortie A15 est basse et A14 est haute (+5 Volts). Eh bien, vient alors 32 Ko de mémoire rapide. Nous attacherons cette mémoire plus tard, et nous l'activerons si la sortie A15 est dans un état haut. De plus, n'oubliez pas que nous n'activons la mémoire que lorsqu'elle est active (ici, active - faible,0 Volt) MREQ et inactif (ici, inactif - haut, + 5V) RFSH. Tout cela est assez simple à mettre en œuvre sur une logique standard, sur les mêmes NAND, comme 74HC00, ou Orthodox K155LA3, et je comprends que cette tâche est pour le groupe préparatoire de la maternelle, cependant, je ne peux penser que dans les tables de vérité en liberté et en captivitéJ'y ai un diagramme Harlequin complet , à partir duquel vous pouvez simplement prendre la partie où U4 est tiré (74HC138, heureusement j'en ai une centaine). Nous ignorerons U11 pour plus de clarté, car les 32 Ko supérieurs ne nous intéressent pas jusqu'à présent.

La connexion est très simple.



Comme le montre la brève descriptionLe microcircuit est un décodeur qui reçoit des nombres binaires de 000 à 111 sur les bornes 1 à 3 et active l'une des 8 sorties (branches 7 et 9 à 15) correspondant à ce nombre. Étant donné que seuls 8 numéros différents peuvent être stockés sur 3 bits, il n'y a que huit sorties. Comme vous pouvez le voir, les conclusions sont inversées, c'est-à-dire que celle qui sera active aura un niveau de 0V, et toutes les autres + 5V. De plus, une clé sous la forme d'une porte à 3 entrées de type «I» est intégrée dans la puce, et deux de ses trois entrées sont également inversées.

Dans notre cas, nous connectons le décodeur lui-même comme suit: le bit le plus significatif (3e jambe) à la masse, il y aura toujours 0. Le bit du milieu est à la ligne A15. Il n'y aura 1 que si le processeur accède aux 32 Ko supérieurs de mémoire (adresses 0x8000 - 0xFFFF ou 1000000000000000 - 111111111111111111 en binaire, lorsque le bit le plus significatif est toujours défini sur 1). Nous connectons le bit le moins significatif à la ligne A14, où le niveau élevé sera en cas d'accès soit à la mémoire après les 16 premiers Ko, mais jusqu'aux 32 Ko supérieurs (adresses 0x4000 - 0x7FFF, ou 0100000000000000-0111111111111111 sous forme binaire), ou aux 16 Ko les plus récents de l'adresse espaces (adresses 0xB000 - 0xFFFF, ou 1100000000000000 - 1111111111111111 sous forme binaire).

Voyons ce que sera la sortie dans chacun des cas:

  • 14 15 , 16 , , 000, 0 ( ), Y0 (15 ). , .
  • 14 , 15 — , 16 , 32 , 001, 1 , Y1 (14 ). , 16 , .
  • 14 , 15 — , - 32 48 , 010, Y2 (13 ). , .
  • Si les deux lignes (A14 et A15) sont actives, le processeur accède aux 16 premiers Ko de mémoire, de 48 à 64 Ko, nous ne l'avons pas, donc la broche Y3 (12e broche) est également dans l'air.

De plus, grâce à un autre élément, le microcircuit n'activera ses résultats que si les entrées 4 et 5 sont basses et 6 sont hautes. La 4e entrée est toujours à l'état bas (elle est connectée directement à la masse), la 5e ne sera faible que lorsque le processeur accédera à la mémoire (rappelez-vous, MREQ à l'état bas signifie accéder à la mémoire), et la 6e sera élevée lorsque le processeur n'effectuera pas de cycle de mise à jour DRAM (nous avons SRAM, donc les cycles de mise à jour de DRAM sont le moyen le plus sûr d'ignorer). Ça se passe bien.

Ensuite, mettez la ROM.

J'ai pris le W27C512 car il est bon marché, gai, tout ira bien et vous pourrez également faire des opérations bancaires. 64 Ko! 4 micrologiciels peuvent être téléchargés. Eh bien, j'ai environ un million de ces microcircuits. J'ai décidé de ne coudre que la moitié supérieure, car sur Harlequin, la jambe A15 est liée à + 5V et l'A14 est réglable avec un cavalier. Ainsi, je peux tester le firmware sur Harlequin afin de ne pas déconner longtemps. Fiche technique Smorim . Nous avons mis la puce sur la planche à pain. Encore une fois, je l'ai mis dans le coin droit pour placer le bus d'adresse sur la gauche. Nous tirons la jambe A15 pour alimenter, le câblage A14 au sol. Câblage - c'est pour que vous puissiez changer les banques de mémoire. Étant donné que l'A15 sera toujours à un niveau élevé, seuls les 32 principaux lecteurs flash KiB seront à notre disposition. Parmi ceux-ci, la ligne A14 sélectionnera les 16 Ko supérieurs (+ 5 V) ou inférieurs (masse). En eux, j'ai rempli l' image de test avec le programmeuret firmware 48K BASIC .

Les 14 lignes d'adresse restantes (A0 - A13) sont connectées au bus d'adresse sur la gauche. Nous connectons le bus de données (Q0 - Q7) à notre bus improvisé sous la forme de bus d'alimentation des modèles de maquette. N'oubliez pas la nourriture!

Maintenant, les signaux de contrôle. OE est une autorisation de sortie. Nous avons besoin de la ROM pour envoyer des données au bus de données lorsque le processeur les lit. Nous nous connectons donc directement à la sortie du processeur RD. De manière pratique, les deux broches, à la fois OE sur ROM et RD sur le processeur, sont actives dans un état bas. C'est important, vous n'avez rien à inverser. De plus, la ROM possède une entrée CS, également active à l'état bas. Si cette entrée n'est pas active, la ROM ignorera tous les autres signaux et ne sortira rien vers le bus de données. Nous allons connecter cette entrée à la broche Y0 (15 broches) de la puce 74HC138, qui est également active à l'état bas. Dans le circuit Harlequin , ce signal, pour une raison quelconque, est connecté via une résistance. Nous ferons de même. Pourquoi, je ne sais pas. Peut-être que les gens intelligents me disent dans les commentaires ... Ils me l'

ont dit. Remercier,sterr:
. , «» . .




Tout.

Maintenant RAM.

C'est plus difficile avec cela, car non seulement le processeur, mais aussi ULA, ou, dans notre cas, Arduino, fonctionnent avec de la RAM (avec nos 16 Ko). Puisqu'il est nécessaire de lire quelque chose qui s'affiche à l'écran. Par conséquent, nous devons être capables de déconnecter les signaux de contrôle et le bus d'adresse RAM du processeur. Nous ne déconnecterons pas le bus de données, nous agirons comme dans le spectre d'origine (et dans Harlequin): nous diviserons le bus avec des résistances (470-500 Ohms). D'une part, les résistances seront le processeur et la ROM, d'autre part, la RAM et l'Arduino. Ainsi, en cas de conflit sur le bus de données, il fonctionnera comme 2 bus distincts. Mais pour le reste, nous utilisons 74HC245 , comme dans Harlequin (U43, U44 dans le diagramme), bien que dans le présent Speccyil y avait aussi des résistances (entre IC1 d'une part, c'est ULA, et IC3, IC4 d'autre part).

Le 74HC245 est un tampon de bus 8 bits. Mais nous avons 2 signaux de contrôle (RD - dans le cas de la lecture dans la mémoire et CE pour activer la RAM elle-même. Nous traiterons de WR dans le cas de l'écriture dans la mémoire plus tard) et 14 bits de l'adresse: rappelez-vous, ci-dessus, nous générons déjà un signal vers la mémoire en utilisant 74HC138 uniquement dans le cas où le processeur a activé A14 avec A15 inactif, nous n'avons donc pas besoin de faire de décodage supplémentaire de l'adresse, la mémoire ne fonctionnera que lors de l'accès aux 16 premiers Ko après la ROM. Bien sûr, pour adresser 16 Ko, il vous suffit de 14 lignes d'adresse (A0-A13). Au total, 16 signaux sont obtenus, nous avons donc besoin de 2 microcircuits 74HC245. Nous les connectons à la maquette à gauche, à la place du bus d'adresse.

D'après la fiche technique du 74HC245, il est clair que, en général, peu importe de quel côté connecter les microcircuits, mais depuis que j'ai commencé à créer les dispositions de bas en haut, et que tous les autres microcircuits sont installés avec la première broche à gauche, le bus d'adresse se connectera au côté A (broches 2 -9 puces, dans la fiche technique sont désignées comme A0-A7). La direction de transfert est toujours du processeur vers la RAM, car la RAM ne définit jamais l'adresse, mais la reçoit uniquement. Dans le 74HC245, la broche 1 (DIR) est responsable du sens de transmission. Selon fiche techniquede sorte que du côté B il y ait une sortie égale à l'entrée du côté A, le DIR doit être réglé sur HIGH. Connectez donc la 1ère broche des deux circuits à + 5V. L'OE (20e broche, activée par un niveau bas) est connecté via un câblage à la terre afin de pouvoir être rapidement commuté sur + 5V et déconnecté du processeur. Encore plus simple. Connectez l'alimentation des deux puces. Les broches les plus à droite du microcircuit droit (8e et 9e broches, entrées A6 et A7) seront des signaux de commande. J'ai connecté A7 à la borne RD du processeur et A6 à la broche Y1 de la puce 74HC138, car il n'y aura un niveau bas que lorsque le processeur accédera à notre RAM. Les conclusions restantes du côté A des deux microcircuits (jambes 2–9 pour la gauche et jambes 2–7 pour la droite) Je me suis connecté au bus d'adresse, bornes A13-A0. Nous n'avons pas besoin des 2 bits supérieurs de l'adresse, car ils sont déjà décodés dans le signal du 74HC138.Maintenant sur la RAM elle-même. Naturellement, j'ai utilisé ce que j'avais déjà: une puce de cache de l'ancienne carte mère. Je suis tombé surIS61C256 à 20 ns, mais tout fera l'affaire. Speccy fonctionnait à une fréquence de 3,5 MHz, mais pour l'instant nous traiterons généralement Arduinki. Si 100 kHz sort, il y aura du bonheur! Donc, nous nous connectons. Bien sûr, n'oubliez pas la nourriture. Conclusions I / O0 - I / O7 sont connectées à la platine du bus de données APRÈS les résistances. J'ai eu de la chance (en fait, non), sur mes maquettes chinoises, les bus électriques sont divisés exactement au milieu. J'ai utilisé cette fonction pour séparer le bus avec des résistances. Si vos dispositions sont incorrectes, vous devez être perverticréer un deuxième bus de données et le connecter avec des résistances au premier. Les conclusions de A0-A13 sont rejetées sur les conclusions B correspondantes des puces 74HC245, sans oublier que les plus à droite sont connectées non pas au bus de données, mais aux signaux de commande. A14 - au choix, soit au sol, soit à + 5V. Une puce de 32 Ko, cette conclusion déterminera quelle moitié nous utiliserons. Si vous trouvez une SRAM de 16 Ko, il n'y aura pas de ligne A14. Les sorties sont WE (autorisation d'écriture), CE (activation de puce) et OE (activation de sortie). Tous sont activés bas. L'OE doit être connecté au RD du processeur, mais, bien sûr, pas directement, mais par le 74HC245 droit, où le RD arrive sur mon pied A7, et en conséquence sort du pied B7 (11ème broche). Là et connectez-vous. CE doit être connecté à Y1 à partir du 74HC138, qui décode l'adresse. Son signal me parvient sur l'A6 de la puce droite 74HC245, respectivement,sort du pied B6 (12 broches). WE Je l'ai directement connecté à la sortie du processeur WR. J'ai également installé un cavalier du signal OE et l'ai collé juste dans la partie inutilisée de la planche à pain. En connectant ce fil à la masse d'alimentation, je peux forcer la RAM à être activée lorsque je la lis depuis Arduinka. Pourtant, j'ai tiré tous les signaux de contrôle de la RAM à + 5V en utilisant des résistances de 10 kOhm. Au cas où. Il s'est avéré comme ceci:



En général, ici, et le cas échéant, au tout début, il devrait y avoir un programme éducatif sur la synchronisation des signaux sur les pneus. Je ne le ferai pas, car cela a été fait plusieurs fois sur le réseau par des gens beaucoup plus intelligents que moi. Pour ceux intéressés, je peux recommander cette vidéo:


En général, si vous n'êtes pas abonné à cette chaîne et que l'électronique vous intéresse en tant qu'amateur et non en tant que professionnel, je vous le recommande vivement. Il s'agit d'un contenu de très haute qualité.

En général, c'est presque tout. Maintenant, il vous suffit de comprendre comment lire les données de la RAM dans Arduino. Pour commencer, calculons le nombre de conclusions Arduinki dont nous avons besoin. Nous devons donner un signal d'horloge et contrôler le RESET, ce sont 2 broches. 8 bits de bus de données - 8 autres broches. Plus 13 bits d'adresse, 23 broches au total. De plus, nous devons communiquer avec Arduinka, nous le ferons via son interface série, c'est encore 2 broches. Malheureusement, il n'y a que 20 conclusions sur mon ADN.

Eh bien, cela n'a pas d'importance. Je ne connais pas une seule personne qui a Arduino et qui n'a pas 74HC595. Il me semble qu'ils ne sont vendus que dans le kit. Au moins, je n'ai que des puces 74HC00 de plus de 595x. Nous les utilisons donc. De plus, je gagnerai de la place dans l'article, car le travail du 595x avec arduino est parfaitement décrit ici. 595mi nous générerons l'adresse. La puce aura besoin de 2 pièces (car nous avons 13 bits d'adresse et la 595e a 8 broches). Comment connecter plusieurs 595x pour l'extension du bus est décrit en détail sur le lien ci-dessus. Je note seulement que dans les exemples à ce lien OE (broche 13) 595x est tiré au sol. Nous ne le ferons pas catégoriquement, nous y enverrons un signal depuis Arduinki, car les broches 595x seront connectées directement au bus d'adresse RAM, et nous n'avons besoin d'aucun signal parasite. Après avoir connecté les broches 595x au bus d'adresse RAM, il n'y a plus rien à faire sur les maquettes. Il est temps de connecter l'arduinka. Mais d'abord, écrivez un croquis:

// CPU defines
#define CPU_CLOCK_PIN 2
#define CPU_RESET_PIN 3

// RAM defines
#define RAM_OUTPUT_ENABLE_PIN 4
#define RAM_WRITE_ENABLE_PIN 5
#define RAM_CHIP_ENABLE_PIN 6
#define RAM_BUFFER_PIN 7

// Shift Register defines
#define SR_DATA_PIN 8
#define SR_OUTPUT_ENABLE_PIN 9
#define SR_LATCH_PIN 10
#define SR_CLOCK_PIN 11

//////////////////////////////////////////////////////////////////////////

void setup() {
  // All CPU and RAM control signals need to be configured as inputs by default
  // and only changed to outputs when used.
  // Shift register control signals may be preconfigured

  // CPU controls seetup
  DDRC = B00000000;
  pinMode(CPU_CLOCK_PIN, INPUT);
  pinMode(CPU_RESET_PIN, INPUT);

  // RAM setup
  pinMode(RAM_WRITE_ENABLE_PIN, INPUT);
  pinMode(RAM_OUTPUT_ENABLE_PIN, INPUT);
  pinMode(RAM_CHIP_ENABLE_PIN, INPUT);
  pinMode(RAM_BUFFER_PIN, OUTPUT);
  digitalWrite(RAM_BUFFER_PIN, LOW);

  // SR setup
  pinMode(SR_LATCH_PIN, OUTPUT);
  pinMode(SR_CLOCK_PIN, OUTPUT);
  pinMode(SR_DATA_PIN, OUTPUT);
  pinMode(SR_OUTPUT_ENABLE_PIN, OUTPUT);
  digitalWrite(SR_OUTPUT_ENABLE_PIN, HIGH); // active low

  // common setup
  Serial.begin(9600);
  Serial.println("Hello");
}// setup

//////////////////////////////////////////////////////////////////////////

void shiftReadValueFromAddress(uint16_t address, uint8_t *value) {
  // disable RAM output
  pinMode(RAM_WRITE_ENABLE_PIN, OUTPUT);
  digitalWrite(RAM_WRITE_ENABLE_PIN, HIGH); // active low
  pinMode(RAM_OUTPUT_ENABLE_PIN, OUTPUT);
  digitalWrite(RAM_OUTPUT_ENABLE_PIN, HIGH); // active low
  // set address
  digitalWrite(SR_LATCH_PIN, LOW);
  shiftOut(SR_DATA_PIN, SR_CLOCK_PIN, MSBFIRST, address>>8); 
  shiftOut(SR_DATA_PIN, SR_CLOCK_PIN, MSBFIRST, address);  
  digitalWrite(SR_LATCH_PIN, HIGH);
  digitalWrite(SR_OUTPUT_ENABLE_PIN, LOW); // active low
  // write value to RAM
  digitalWrite(RAM_OUTPUT_ENABLE_PIN, LOW); // active low
  delay(1);
  DDRC = B00000000;
  *value = PINC;
  digitalWrite(RAM_OUTPUT_ENABLE_PIN, HIGH); // active low
  // disable SR
  digitalWrite(SR_OUTPUT_ENABLE_PIN, HIGH); // active low
  pinMode(RAM_WRITE_ENABLE_PIN, INPUT);
  pinMode(RAM_OUTPUT_ENABLE_PIN, INPUT);
}// shiftWriteValueToAddress

//////////////////////////////////////////////////////////////////////////

void runClock(uint32_t cycles) {
  uint32_t currCycle = 0;
  pinMode(CPU_CLOCK_PIN, OUTPUT);
  while(currCycle < cycles) {
    digitalWrite(CPU_CLOCK_PIN, HIGH);
    digitalWrite(CPU_CLOCK_PIN, LOW);
    currCycle++;
  }
  pinMode(CPU_CLOCK_PIN, INPUT);
}// runClock

//////////////////////////////////////////////////////////////////////////

void trySpectrum() {
  pinMode(RAM_WRITE_ENABLE_PIN, INPUT);
  pinMode(RAM_OUTPUT_ENABLE_PIN, INPUT);
  pinMode(CPU_RESET_PIN, OUTPUT);
  digitalWrite(CPU_RESET_PIN, LOW);
  runClock(30);
  digitalWrite(CPU_RESET_PIN, HIGH);
  runClock(12500000);
}// trySpectrum

//////////////////////////////////////////////////////////////////////////

void readDisplayLines() {
  uint8_t value;
  digitalWrite(RAM_BUFFER_PIN, HIGH);
  pinMode(RAM_CHIP_ENABLE_PIN, OUTPUT);
  digitalWrite(RAM_CHIP_ENABLE_PIN, LOW);
  for(uint16_t i=16384; i<16384+6144;i++) {
    shiftReadValueFromAddress(i, &value);
    Serial.println(value);
  }
  pinMode(RAM_CHIP_ENABLE_PIN, INPUT);
}// readDisplayLines

//////////////////////////////////////////////////////////////////////////

void loop() {
  trySpectrum();
  Serial.println("Hope we are ok now. Please set up memory for reading");
  delay(40000);
  Serial.println("Reading memory");
  readDisplayLines();
  Serial.println("Done");
  delay(100000);
}// loop

Comme vous pouvez le voir sur le croquis (enfin, vraiment, tout à coup, quelqu'un l'a lu), j'ai lu le bus de données vers le port C. Comme Arduischik s'en souvient, dans le port CID C, il y a 6 broches. Autrement dit, je ne lis que 6 bits. Oui, pour la simplicité du processus, j'ignore les 2 bits de poids fort dans chaque octet du tampon d'écran. Cela se traduira par le fait que tous les 2 pixels après 6, il y aura toujours des couleurs d'arrière-plan. Pendant un trajet, puis réparez-le. Ceci est le squelette.

Maintenant, pour la connexion elle-même. En principe, tout est peint en haut du croquis:

// CPU defines
#define CPU_CLOCK_PIN 2 -  2     6  ( )
#define CPU_RESET_PIN 3 -  3     26  (RESET)

// RAM defines
#define RAM_OUTPUT_ENABLE_PIN 4 -  4     22  (OE)
#define RAM_WRITE_ENABLE_PIN 5 -  5    .     .
#define RAM_CHIP_ENABLE_PIN 6 -  6     .        ,        .   - ,   -  .   ,   .
#define RAM_BUFFER_PIN 7 -  ,    6,    .

// Shift Register defines
#define SR_DATA_PIN 8   -  8     14 "" 595.        9 ,     .
#define SR_OUTPUT_ENABLE_PIN 9 -   13  595
#define SR_LATCH_PIN 10 -   12  595
#define SR_CLOCK_PIN 11 -   11  595.

Tout est simple. Voici à quoi cela ressemble, je suis tout assemblé (l'arduino a été coupé dans l'image, mais il n'y a rien de spécial à regarder):



Au démarrage, Arduino dira joyeusement Bonjour au port série de l'ordinateur (bien que virtuel), et commencera à tourmenter le processeur. Après l'avoir complètement torturé (quelques minutes), le programme arrêtera le pauvre gars et vous proposera de réorganiser les cavaliers avec les stylos sur la planche à pain, déconnectant la mémoire du bus d'adresse et des signaux de contrôle du processeur.

Maintenant, nous devons utiliser les poignées pour réorganiser le câblage connecté aux broches 19 des deux 74HC245 de la masse à + 5V. Ainsi, nous déconnectons le processeur de la RAM. La broche 22 de la puce RAM elle-même doit être connectée à la terre (j'ai écrit ci-dessus sur le câblage, que je viens de coincer dans la planche à pain jusqu'à présent, dans un endroit inutilisé). Ainsi, nous activons de force la RAM.

Après cela, après avoir attendu un peu, Arduinka commencera à lire le contenu de la mémoire et à le sortir dans une colonne vers le port série. Il y aura beaucoup, beaucoup de chiffres. Maintenant, vous pouvez copier ces données à partir de là et les coller, par exemple, dans un fichier texte, sans oublier de nettoyer tout le texte inutile (quelques lignes en haut et "Terminé" en bas), nous avons seulement besoin de chiffres. C'est ce que notre Speccy a enregistré dans la mémoire vidéo. Il ne reste plus qu'à voir ce qu'il y avait dans la mémoire vidéo. Et la mémoire vidéo du Spectrum n'est pas facile ...

Comme vous pouvez le voir, les pixels eux-mêmes sont stockés séparément de la couleur. Nous allons ignorer la couleur pour l'instant, lisons seulement les pixels eux-mêmes. Mais ils ne sont pas si faciles à décoder. Après beaucoup de douleur dans Visual Studio, je suis arrivé à cette élégante solution:


#include "stdafx.h"
#include <windows.h>
#include <stdint.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
uint8_t *scrData;

VOID OnPaint(HDC hdc) {
	size_t arrSize = 6144;//sizeof(scrData) / sizeof(scrData[0]);
	//int currRow = 0, currX = 0, currBlock = 0, currY = 0, currBase = 0;
	for (size_t arrPos = 0; arrPos < arrSize; arrPos++) {
		int blockPos = arrPos % 2048;
		int currBase = (blockPos % 256) / 32;
		int currX = blockPos % 32;
		int currBlock = arrPos / 2048;
		int currRow = blockPos / 256;
		int currY = currBlock * 64 + currBase * 8 + currRow;
		for (int trueX = 0; trueX < 8; trueX++) {
			char r = ((scrData[arrPos] >> trueX) & 1)*255;
			SetPixel(hdc, currX * 8 + (8-trueX), currY, RGB(r, r, r));
		}
	}
}

void loadData() {
	FILE *file;
	errno_t err;
	if ((err = fopen_s(&file, "data.txt", "r"))) {
		MessageBox(NULL, L"Unable to oopen the file", L"Error", 1);
	}
	scrData = (uint8_t*)malloc(6144);
	int currDataPos = 0;
	char buffer[256];
	char currChar = 0;
	int currLinePos = 0;
	while (currChar != EOF) {
		currChar = getc(file);
		buffer[currLinePos++] = currChar;
		if (currChar == '\n') {
			buffer[currLinePos] = 0;
			scrData[currDataPos++] = (uint8_t)atoi(buffer);
			currLinePos = 0;
		}
	}
	fclose(file);
}

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow) {
	HWND                hWnd;
	MSG                 msg;
	WNDCLASS            wndClass;
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = TEXT("GettingStarted");
	RegisterClass(&wndClass);
	hWnd = CreateWindow(
		TEXT("GettingStarted"),   // window class name
		TEXT("Getting Started"),  // window caption
		WS_OVERLAPPEDWINDOW,      // window style
		CW_USEDEFAULT,            // initial x position
		CW_USEDEFAULT,            // initial y position
		CW_USEDEFAULT,            // initial x size
		CW_USEDEFAULT,            // initial y size
		NULL,                     // parent window handle
		NULL,                     // window menu handle
		hInstance,                // program instance handle
		NULL);                    // creation parameters
	loadData();
	ShowWindow(hWnd, iCmdShow);
	UpdateWindow(hWnd);
	while (GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}  // WinMain

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	HDC          hdc;
	PAINTSTRUCT  ps;
	switch (message) {
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		OnPaint(hdc);
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
} // WndProc

Le programme ouvre le fichier data.txt à partir de son répertoire. Dans ce fichier, la sortie texte de l'arduino (après avoir supprimé toutes les lignes supplémentaires, comme mentionné ci-dessus.)

Nous lui donnons le fichier résultant, et par conséquent:



Oui, alors que le résultat est très loin d'être idéal, mais c'est certainement la sortie à l'écran. De plus, celui qui est nécessaire. Depuis la ROM avec le firmware de diagnostic.

Eh bien, le squelette de l'ordinateur est prêt. Oui, il n'est pas encore possible de l'utiliser, mais vous pouvez voir à quel point les anciens ordinateurs 8 bits étaient extrêmement simples. J'ai encore battu un peu sur la planche à pain, mais la conclusion n'a fait qu'empirer. Il semble que la prochaine étape consiste à souder sur une planche à pain normale, non soudée, avec une puissance normale.

Mais est-ce nécessaire?

All Articles