Arduino UNO Breadboard Computer Programmeerder – Bouw en Programmeer Je Eigen 8-bit Systeem
Onderdelen en Benodigdheden
| Arduino UNO | × | 1 |
Over Dit Project
Wat is dit project?
Dit is een uitbreiding op Ben Eaters 8-bit breadboard computer project. Ik heb enorm genoten van het volgen van Ben's video's over het bouwen van een breadboard computer en het bouwen van mijn eigen versie. Als je Ben's video's niet hebt gevolgd, raad ik je ten zeerste aan om die eerst te bekijken – dit project maakt zonder die context weinig zin.
Na het bouwen van de computer werd snel duidelijk dat als ik elke keer bij inschakelen handmatig een programma via DIP-schakelaars moest invoeren, het systeem nauwelijks gebruikt zou worden.zelfs het invoeren van slechts 16 bytes herhaaldelijk wordt snel vervelend. Ik had ook van plan het geheugen uit te breiden naar 256 bytes, wat alleen nuttig is als programma's geladen en opgeslagen kunnen worden.
Wat kan ik ermee doen?
- Een programma opslaan vanuit het RAM van de breadboard computer
- Een programma laden in het RAM van de breadboard computer
- Automatisch een opgeslagen programma starten bij opstarten van de breadboard computer
- Geheugeninhoud bekijken en wijzigen via een seriële verbinding
- De breadboard computer programmeren in assembly-taal
- Geheugeninhoud disassembleren
- Instructie voor instructie single-stappen
- Breakpoints instellen en tot breakpoint draaien
Hier is een video die deze functies demonstreert. Excuseer de videokwaliteit – ik ben geen professionele videomaker:
Hoe werkt het?
Ik had een paar doelen in gedachte bij het maken van dit project:
- Zo min mogelijk hardware nodig om het te bouwen
- Zo min mogelijk wijzigingen aan de breadboard computer zelf
In het begin dacht ik aan het gebruik van een EEPROM om programma's op te slaan en dan een soort logica om programma's van de EEPROM naar het RAM van de breadboard computer te verplaatsen. Echter, het bedenken van de verplaatsingslogica bleek complexer dan ik aan kon (ik ben meer een softwaremens, ondanks dat ik geniet van werken dicht bij de hardware). Ik ben een grote Arduino-fan, dus op een gegeven moment begon ik te denken aan het vervangen van de verplaatsingslogica door een Arduino. Het kostte enige tijd om mezelf te overtuigen dat het gebruik van een Arduino niet 'bedriegen' was (immerhin is zelfs de Arduino UNO veel krachtiger dan de breadboard computer zelf), maar ik ben tevreden met het resultaat.
Wat moet de Arduino kunnen doen? Het moet data uit het geheugen kunnen lezen en data naar het geheugen kunnen schrijven. De eenvoudigste en minst invasieve manier (voor de breadboard computer) is het gebruik van dezelfde interface die het geheugenmodule al heeft: de bus en controle signalen. De digitale I/O-pinnen van de Arduino zijn bidirectioneel, dus door er 8 direct aan de bus te koppelen, kan de Arduino van en naar de bus lezen/schrijven. Om het RAM-module te laten lezen of schrijven naar de bus, hoeven alleen de MI/RI/RO signalen correct ingesteld te worden. Die signalen worden normaal aangestuurd door de controlelogica's EEPROMs, dus als de Arduino ze ook aanstuurt, ontstaan conflicten en mogelijke kortsluitingen. De AT28C16 EEPROMs die Ben gebruikt, hebben echter een Chip Enable (CE) ingang die alle data-uitgangen in een high-Z staat zet – wat de Arduino toelaat de signalen te manipuleren.
Om het RAM uit te lezen, moet de Arduino het volgende doen:
- Zet de CE van de EEPROM hoog (controlelogica uitschakelen)
- Zet het eerste te lezen adres op de bus
- Zet MI hoog en wacht op een klok laag-hoog overgang
- Zet MI laag en RO hoog en wacht op een klok laag-hoog overgang
- Lees het databyte van de bus
- Herhaal vanaf stap 2 voor alle 16 adressen
- Zet de CE van de EEPROM laag (controlelogica weer inschakelen)
Schrijven van RAM-inhoud is heel vergelijkbaar; de Arduino hoeft alleen naar de bus te schrijven en RI hoog te zetten in plaats van RO. Er zijn natuurlijk wel enkele technische uitdagingen, maar het bovenstaande basismechanisme is de kern van dit project.
Welke wijzigingen moet ik aan de breadboard computer maken?
Er zijn twee wijzigingen nodig:
- Pulldown-weerstanden op de data-uitgangen van de controlelogica EEPROM
- De ringteller uitschakelen (of continu resetten) zolang de EEPROM uitgeschakeld is
Pulldown-weerstanden
De pulldown-weerstanden zijn nodig omdat zodra de EEPROM uitgeschakeld is, de pull-up-weerstanden van de logische poorten waar de controle signalen (zoals AO/AI/EO…) aan verbonden zijn, die signalen hoog trekken. Dat zou betekenen dat meerdere registers naar de bus schrijven, wat conflicten veroorzaakt.
De pull-up-weerstanden op de ingangen van 74LS-poorten zijn ongeveer 10k. De pulldown-weerstanden moeten klein genoeg zijn om de spanning in laag gebied te brengen. Voor de meeste signaallijnen gebruikte ik 3,3 kΩ weerstanden. Er zijn twee uitzonderingen: het "SU" signaal is verbonden met 8 exclusieve OF-poorten, wat de effectieve pull-up op 1,25 kΩ brengt. Een pulldown zou significant onder 1k moeten zijn. Gelukkig regelt SU (subtract) geen bus-interactie, dus we kunnen het negeren en geen pulldown plaatsen. Ten tweede had de CE (counter enable) een 1k pulldown nodig – grotere waarden gaven willekeurig programmeelteller-gedrag.
Ik vond het makkelijkst om de pulldown-weerstanden op het breadboard te plaatsen dat alle blauwe LEDs bevat, dus tussen de LED-anodes (verbonden met de EEPROM-uitgangen) en GND.

[Kon de weerstanden voor HLT/MI/RI signalen hier niet onderbrengen, dus die staan elders op het breadboard]
Ringteller reset
De andere wijziging is het resetten van de ringteller. Technisch is dit niet strikt noodzakelijk voor opslaan/laden van programma's, maar het zorgt voor een soepele overdracht van controle van de Arduino terug naar de controlelogica. Het doel is de ringteller op 0 te houden zolang CE hoog is (controlelogica uitgeschakeld). Wanneer de Arduino CE weer laag zet (controlelogica inschakelen), staat de ringteller op nul en begint de breadboard computer met het uitvoeren van de volgende instructie.
In mijn build hoefde ik hier niets voor te doen omdat ik één EEPROM-uitgang gebruik om de ringteller te resetten. Dit verbetert prestaties door de ringteller zo snel mogelijk te resetten na een instructie. Het geeft ook automatisch de ringteller-reset wanneer de controlelogica EEPROM uitgeschakeld wordt: de pulldown op de EEPROM-uitgang trekt het signaal laag, wat de ringteller reset.
Als je Ben's implementatie van een vaste 5-staps ringteller gebruikt, zou de volgende uitbreiding van zijn reset-circuit de teller moeten resetten wanneer CE hoog is (klik op de pijltjes om te schakelen tussen Ben's originele circuit en de uitgebreide versie):

Dit vereist 3 extra NAND-poorten, dus één extra 74LS00 chip. Let op: ik heb deze aanpak niet getest, maar het zou moeten werken.
Deze wijziging is niet absoluut noodzakelijk – je kunt het initieel weglaten. Laden, opslaan, monitor, assembler en disassembler werken prima. Echter, elke actie die overdracht van controle van de Arduino naar de controlelogica vereist, werkt niet. Met name auto-run bij opstart, single-steppen en debugger-run.
Hoe stel ik de Arduino in?
Upload de sketch uit het GIT-archief naar de Arduino en verbind de Arduino met de breadboard computer als volgt:
- Arduino 5V pin (niet Vin!) naar 5V rail op het breadboard
- Arduino GND pin naar GND op het breadboard
- Arduino digitale pinnen 2-9 naar bus 0-7
- Arduino digitale pin 10 naar controlelogica EEPROM-uitgang die RO aanstuurt
- Arduino digitale pin 11 naar controlelogica EEPROM-uitgang die RI aanstuurt
- Arduino digitale pin 12 naar controlelogica EEPROM-uitgang die MI aanstuurt
- Arduino analoge pin 0 naar CLOCK signaal
- Arduino analoge pin 3 naar CE (pin 18) van alle controlelogica EEPROMs en via een 10k weerstand naar +5V
Daarnaast moet je de Arduino's analoge 1 en 2 ingangen verbinden met de DIP-schakelaars en drukknoppen zoals in de schema's getoond (zie bijgevoegd Fritzing-bestand voor details).


Voor een absolute minimaal (maar functionele) versie kun je het volgende doen:
- Voeg 3,3kΩ pulldown-weerstanden toe aan de EEPROM-uitgangspinnen die AO, CO, EO, IO, RO aansturen
- Sla de "Ringteller reset" instructies over
- Doe de Arduino-naar-breadboard-computer wiring zoals hierboven (je kunt de 10k weerstand in de laatste stap weglaten)
- Verbind zowel analoge pin 1 als analoge pin 2 met GND
Om de laad/opslaanknoppen te gebruiken, hoef je alleen de knoppen, DIP-schakelaars en bijbehorende weerstanden aan analoge pin 1 en 2 te verbinden volgens de schema's.
Om de autostart-functie te gebruiken, moet de Arduino de programmeelteller en ringteller op 0 houden tijdens reset en tijdens het overdragen van het programma. De pull-up-weerstand tussen analoge pin 3 en +5V houdt de controlelogica uitgeschakeld (en dus de programmeelteller op 0) terwijl de Arduino reset. Voor de ringteller, volg de "Ringteller reset" instructies hierboven.
Hoe laad en bewaar ik programma's?
De minimaal benodigde setup hierboven stelt je in staat de Arduino via de seriële interface te besturen. Je hebt een terminalprogramma nodig zoals Putty of TeraTerm. De seriële monitor in de Arduino-software werkt ook, maar de scheiding tussen in- en uitvoergebied maakt het hier wat onhandig.
- Zet de breadboard computer aan
- Verbind een PC met de Arduino via de USB-kabel
- Start het terminalprogramma en configureer op 9600 baud, 8 bits, geen pariteit, 1 stopbit
- Druk op ESC in het terminalvenster om de Monitor-modus te activeren
- Je zou een "." commando-prompt moeten zien
- Typ "h" en druk op enter voor een lijst met ondersteunde commando's
Met de minimale setup kun je de "m", "M", "C", "l" en "s" commando's gebruiken. Deze laten je geheugeninhoud bekijken, wijzigen en programma's laden/opslaan.
Om een programma via de knop op te slaan of te laden:
- Zet de klok van de breadboard computer uit
- Zet de DIP-schakelaars om het bestandnummer te selecteren waar de data opgeslagen moet worden
- Druk op de "save" of "load" knop. De LED verbonden met CE gaat branden, wat aangeeft dat de Arduino controle heeft overgenomen
- Zet de klok van de breadboard computer aan. Je ziet de Arduino door de adressen cyclen (bekijk de LEDs van het geheugenadresregister)
- Wacht tot de bus-LEDs stoppen met knipperen en het geheugenadresregister 1111 toont
- Zet de klok uit. De LED verbonden met CE gaat uit, controle is teruggegeven aan de controlelogica
Om automatisch een programma bij opstart te draaien (zorg dat alle benodigde circuitry aanwezig is), zet de DIP-schakelaars op het bestandnummer waar het programma opgeslagen is en zet de breadboard computer aan (of druk op de resetknop). Er zijn twee speciale gevallen: als alle DIP-schakelaars uitstaan, start de computer normaal op zonder autostart. Als alle DIP-schakelaars aanstaan, gaat de Arduino direct in Monitor-modus bij opstart.
Hoe gebruik ik de assembler en disassembler?
Om de assembler/disassembler en debugger-functies te gebruiken, moet je eerst de code op de Arduino aanpassen aan jouw specifieke setup. Zoek de sectie in de broncode die de opcodes_4bit structuur definieert:
struct opcodes_struct opcodes_4bit [] = { {"NOP ", B00000000, 0, false}, {"LDA ", B00010000, 2, true}, ... {".OR ", B11111110, 0, true}, // set start adres {".BY ", B11111111, 0, true}, // definieer één byte data {NULL, 0, 0, false} };
Elke regel specificeert één opcode:
- Eerste veld is de mnemonic ("LDA"). Voor directe adressering, voeg een "#" toe in de mnemonic. Wat Ben "LDI" noemt, zou hier "LDA #" heten. Kan je zien dat ik opgegroeid ben met 6510 assembler op een C64?
- Tweede veld is de opcode zelf. De lagere vier bits moeten altijd 0 zijn (behalve voor de speciale opcodes .OR en .BY).
- Derde veld is het aantal cycli dat de opcode neemt om uit te voeren (naast de fetch-cycli). Bijvoorbeeld, in mijn implementatie heeft LDA opcode 0001 en neemt het in totaal vier cycli, twee daarvan zijn de fetch-cycli. Als je Ben's instructies volgde (waar alle opcodes 5 cycli gebruiken), zou dit altijd 3 moeten zijn.
- Laatste veld specificeert of deze opcode een argument vereist. Bijvoorbeeld LDA vereist een argument, OUT niet.
Je moet deze lijst aanpassen om de opcodes weer te geven die je voor je breadboard computer hebt geïmplementeerd. De laatste twee regels zijn speciale opcodes voor de assembler en moeten ongewijzigd blijven.
Na het invoeren van al je opcodes, upload de software naar de Arduino. Verbind je terminal en ga in Monitor-modus (door ESC in het terminalvenster te drukken of alle DIP-schakelaars aan te zetten). Je kunt nu je programma disassembleren. Typ gewoon "d" in de monitor om te beginnen met disassembleren vanaf adres 0.
De assembler is minimaal maar werkt goed. Typ "a" om te beginnen met assembleren vanaf adres 0. Conventies:
- Als een regel niet begint met witruimte, moet het beginnen met een labeldefinitie. Labels moeten beginnen met een alfabetisch teken gevolgd door alfanumerieke tekens, maximaal 3 karakters lang en zijn hoofdlettergevoelig.
- Na de eerste witruimte in een regel, verwacht de assembler een mnemonic (LDA, STA, OUT...).
- Speciale mnemonic ".BY" specificeert direct een databyte op de huidige locatie.
- Speciale mnemonic ".OR" vertelt de assembler om verder te assembleren op een nieuw adres.
- Als een argument begint met een alfabetisch teken, wordt aangenomen dat het een label is.
- Elk numeriek argument wordt als decimaal verwacht. Om hexadecimaal op te geven, voeg een "$" toe. Bijvoorbeeld, om hexadecimaal FF in de A-register te laden, gebruik "LDA #$FF".
- Alles na een ";" wordt als commentaar genegeerd.
Bijvoorbeeld, de Fibonacci-code kan als volgt worden ingevoerd:
rst LDA #0 ; x = 0 STA x LDA #1 ; y = 1 STA y lp ADD x ; z = y + x STA z JC rst ; restart als overflow OUT ; print z LDA y ; x = y STA x LDA z ; y = z STA y JMP lp ; loop x .BY 0 y .BY 0 z .BY 0
Wat zijn de beperkingen?
Om RAM-ruimte op de Arduino te besparen, werkt de assembler als een 1-pass assembler (anders zou de Arduino alle broncode moeten bufferen). De assembler schrijft de opcodes naar het geheugen van de breadboard computer zodra ze worden ingevoerd. Dit betekent dat assembleren vertraagd wordt door de kloksnelheid van de breadboard computer. Als je tekst in het terminalvenster kopieert en plakt, kan dat leiden tot verloren karakters omdat de Arduino niet bij kan komen met karakters die met 9600 baud binnenkomen (omdat het te veel tijd besteedt aan wachten op de klok van de breadboard computer). Om dit te omzeilen: verlaag de baudrate of gebruik TeraTerm dat een instelling heeft voor vertraging tussen verzonden karakters. De andere workaround is de kloksnelheid van de breadboard computer te verhogen. Mijn klok gaat tot 160kHz en op die snelheid kan ik code kopiëren en plakken bij 9600 baud zonder problemen.
In de standaardconfiguratie kan de Arduino-sketch klokfrequenties van de breadboard computer tot ongeveer 1-2kHz aan (misschien iets meer). Ben's klok in standaardconfiguratie gaat niet sneller dan 500Hz. Als je klok sneller is, zoek de #ifdef FAST_IO switch in de code. FAST_IO aanzetten zou de Arduino moeten laten werken met kloksnelheden tot 250kHz. Ik heb het getest tot 160kHz. Het zou mogelijk zijn om hogere snelheden te ondersteunen door de tijdskritische lussen direct in assembler te implementeren, maar eerlijk gezegd voelt een 160kHz klok al te snel op de breadboard computer met zijn overige beperkte mogelijkheden. Lees de bijbehorende comments in de code voordat je FAST_IO aanzet.
De Arduino heeft 1k EEPROM en kan dus 1024/16=64 verschillende programma's opslaan. Eigenlijk 63, want 16 bytes zijn gereserveerd voor configuratiedata. Dat is niet veel, maar waarschijnlijk genoeg voor alle programma's die je kunt bedenken. Alleen programma's 0-15 kunnen via de DIP-schakelaars geselecteerd worden (1-14 voor autostart), maar de "s" en "l" commando's werken met het volledige 0-62 bereik.
Het ziet er wat rommelig uit – kun je het netter maken?
Ja! In mijn finale versie heb ik de bare Atmega 328P chip gebruikt (samen met een 16MHz kristal en condensators) in plaats van een Arduino UNO. De spanningsregelaar op de UNO is hier niet nodig omdat de Arduino direct de 5V van onze voeding gebruikt. Het enige nadeel is dat we nu een aparte USB-naar-seriële converter (FTDI of vergelijkbaar) nodig hebben om met de Atmega te praten. Het uiterlijk past veel beter bij de rest van de breadboard computer:


Een andere optimalisatie is het verwijderen van de bootloader van de Arduino/Atmega. Dat verwijdert de 2 seconden vertraging terwijl de Arduino-bootloader opstart. Ik zal instructies posten hoe dat te doen als er interesse is. Laat het weten in de reacties!
Code
Breadboard computer programmeerder
De Arduino-sketch voor de Breadboard Computer Programmeerder: https://github.com/dhansel/programmer
Schema's
drive_pD8k28E85v.fzz
Productieproces
- De Evolutie van de Computermuis: Ontwerp, Productie en Toekomsttrends
- Sensor-gestuurde Audio-effecten Bouwen met de Satellite CCRMA Kit
- Bluetooth-gecontroleerde Arduino Spybot bouwen
- FlickMote: Gebarengecontroleerde IR-afstandsbediening voor Slimme Huizen
- DIY Arduino TV-B-Gone: Universele TV-uit-schakelaar
- Bouw een Aangepaste LED Hoofdklok met Alarm – Zichtbaar vanaf 12 Meter
- Find Me: Slimme Item Locator met Arduino en Bluetooth
- Geoptimaliseerde Voedingsoplossingen voor Arduino Projecten
- Arduino Boter-Kaas-Eieren met MAX7219 LED Matrix en Kartonnen Behuizing
- Bouw een Slimme Arduino Vierpotige Robot: Stap-voor-Stap Handleiding
- Realtime Weerstation met Arduino & ThingSpeak IoT