Programmeren in TI-83+ Assembly/Printversie
Programmeren in TI-83+ Assembly
Inleiding
Dit boek leert je om te programmeren in de programmeertaal Assembly voor de TI-83+ of de TI-84+. Dit zijn grafische rekenmachines van Texas Instruments. Er is ook een andere programmeertaal op deze rekenmachines, namelijk TI-Basic. (zie het wikibook Programmeren in TI-Basic).
Voordelen en nadelen t.o.v. TI-Basic
[bewerken]Assembly heeft een aantal voor- en nadelen ten opzichte van TI-Basic:
Voordelen
[bewerken]- Assembly is veel sneller dan TI-Basic, bijvoorbeeld alle geavanceerde spelletjes voor de rekenmachine zou je nooit in TI-Basic kunnen maken, omdat deze programmeertaal daarvoor gewoon te langzaam is.
- Assembly heeft controle over de hele rekenmachine. Je kunt er veel meer mee dan met TI-Basic.
Nadelen
[bewerken]- Assembly moet op de computer geprogrammeerd en gecompileerd worden, voordat het naar de rekenmachine kan worden overgezonden.
- Assembly is veel moeilijker te leren dan TI-Basic.
- Als een Assembly-programma crasht, is direct het RAM-geheugen gewist, bij TI-Basic verschijnt er gewoon een foutmelding.
Inhoudsopgave
[bewerken]
Hoofdstuk 1 - Assembly-basis
In dit deel leer je de basis van het programmeren in Assembly. We beginnen met het opzetten van de programmeer-omgeving, daarna schrijven we onze eerste programma's.
Voorbereiding
Voordat we kunnen beginnen met Assembly, moet er eerst een aantal dingen worden voorbereid, namelijk:
- Installeer de TI SDK (deze is te downloaden op https://education.ti.com/educationportal/sites/US/productDetail/us_sdk_73_83_84.html), zodat de gemaakte programma's op de computer getest kunnen worden, voordat ze worden overgezet naar de rekenmachine.
- Maak een map aan, waarin je alle bestanden zult zetten die met Assembly te maken hebben (in ons voorbeeld C:\ASM).
- Maak in deze map drie submappen, met de namen Exec, Source en Tasm.
- Download het bestand ti83plus.inc en zet hem in de map Tasm.
- Download TASM (Table Driven Assembler) op de site https://www.ticalc.org/archives/files/fileinfo/15/1504.html en uit het ZIP-archief de bestanden tasm.exe en tasm80.tab in de map Tasm.
- Download Devpac8x op https://www.ticalc.org/archives/files/fileinfo/129/12922.html en zet deze weer in de map Tasm.
- Nu gaan we een batch-bestand maken dat ervoor zorgt dat de programma's die je maakt eenvoudig kunnen worden gecompileerd. Maak (bijvoorbeeld in Kladblok) een nieuw bestand en typ het volgende in:
@echo off echo ==== Compileert %1.z80 voor de TI-83+ of TI-84+. ==== tasm -80 -i -b c:\asm\source\%1.z80 c:\asm\exec\%1.bin if errorlevel 1 goto FOUTEN rem This is necessary because of a DevPac8x bug cd c:\asm\exec c:\asm\tasm\devpac8x %1 cd c:\asm\tasm echo ==== Klaar; het programma is opgeslagen in Exec\%1.8xp ==== goto KLAAR :FOUTEN echo ==== Fouten!!! ==== :KLAAR del c:\asm\source\%1.lst > NUL del c:\asm\exec\%1.bin > NUL echo ==== Klaar ====
Sla het op als asm.bat in de submap Tasm.
Controleer nu of alle bestanden in de map staan zoals hieronder is aangegeven:
Het eerste programma
Nu zijn we klaar om te beginnen met het eerste programma.
Het programma maken
[bewerken]Het programma intypen
[bewerken]Maak in Kladblok een nieuw bestand en typ in:
.nolist #include "ti83plus.inc" .list .org $9D93 .db t2ByteTok, tAsmCmp b_call(_ClrLCDFull) ; wis het LCD-scherm ret ; terug naar de TI-OS .end .end
Let op: vervang de vier spaties aan het begin van sommige regels door een tab. Sla het bestand op als clrscrn.z80 in de map Source. Let op: een bestandsnaam van een programma mag alleen uit letters bestaan en niet meer dan acht tekens bevatten!
Het programma compileren
[bewerken]Start een MS-DOS-venster. Dat kan via het startmenu (Opdrachtprompt). Ga nu naar de submap Tasm, bijvoorbeeld met het commando
cd C:\Asm\Tasm
Sluit af met een enter. Geef daarna het commando
asm clrscrn
Er komt veel tekst in het DOS-scherm en als alles goed is gegaan, staat er op de laatste twee regels:
==== Klaar; het programma is opgeslagen in Exec\clrscrn.8xp ==== ==== Klaar ====
Het programma is nu klaar. De volgende stap is om het te testen.
Het programma testen
[bewerken]Open nu de TI-83 Flash Debugger. Klik op de knop Nieuw (met het vel papier erop). Klik op TI-83 Plus. Je ziet nu een scherm met bovenin een menubalk, klik hierin op Load en dan op RAM File... en ga naar de map Asm\Exec. Open het bestand CLRSCRN.8XP. Klik dan op het zwarte driehoekje om de rekenmachine te starten.
Je ziet nu een rekenmachine op het scherm. Klik achtereenvolgens op 2nd en dan op Catalog (boven de 0). Ga met de pijltjes naar Asm( en klik op de Enter-knop. Klik op PRGM en druk op Enter als het programma CLRSCRN geselecteerd is. Druk nogmaals op Enter om het programma uit te voeren. Er kunnen nu twee dingen gebeuren:
- Het scherm wordt leeg en er verschijnt Done op het scherm. Alles is goed gegaan: ga door naar de volgende paragraaf.
- Het scherm wordt leeg en het RAM wordt gewist. Het programma is gecrasht. Controleer of je alles goed hebt uitgevoerd... Als je er niet uitkomt, ga dan naar het hoofdstuk Speciaal en de paragraaf Problemen.
Hoe werkt dit programma?
[bewerken]Nu we weten wat dit programma doet, willen we natuurlijk ook graag weten hoe het werkt.
Standaard-code
[bewerken]Ieder programma bevat bepaalde regels aan het begin en het eind. Deze regels zie je hieronder.
.nolist #include "ti83plus.inc" .list .org $9D93 .db t2ByteTok, tAsmCmp ; Het programma komt hier. .end .end
Alle regels die je hierboven ziet, staan dus in ieder programma dat je schrijft. Het gaat er nu niet helemaal om wat iedere regel van deze standaard-code doet, maar hieronder staat een kleine samenvatting.
- .nolist Dit heeft te maken met een list-file die TASM genereert.
- #include "ti83plus.inc" Het bestand ti83plus.inc wordt hier ingevoegd in de broncode. Dit bestand zorgt ervoor dat je commando's kunt geven met hun namen en niet met hun hex-codes.
- .list Dit heeft weer te maken met de list-file.
- .org $9D93 Dit geeft aan waar het programma wordt geladen in het geheugen.
- .db t2ByteTok, tAsmCmp ?
- .end Het einde van het programma. Er staan er twee, omdat TASM de laatste regel in het bestand overslaat...
Tot zover een korte beschrijving van de standaard-code.
De rest van de code
[bewerken]We houden nu het volgende stukje code over.
bcall(_ClrLCDFull) ; wis het LCD-scherm ret ; terug naar de TI-OS
Dat ziet er al veel beter uit, toch? Regel voor regel zal ik uitleggen wat dit betekent.
Gedeelten achter een puntkomma zijn commentaar, hier doet de rekenmachine dus niets mee.
bcall(_ClrLCDFull) ; wis het LCD-scherm
Dit is een rom-call: een aanroep aan de rekenmachine om iets te doen, in dit geval dus het scherm leegmaken. Rom-calls worden altijd voorafgegaan door bcall, daarachter komt tussen haakjes de opdracht die je geeft. Deze opdrachten worden in ti83plus.inc omgezet in hexadecimale codes, waar de rekenmachine mee overweg kan. Vandaar de opdracht #include "ti83plus.inc" aan het begin, anders zou je de hex-codes in moeten geven.
ret ; terug naar de TI-OS
Deze regel zorgt ervoor dat de processor in de rekenmachine weer teruggaat naar de TI-OS, het besturingssysteem van de rekenmachine. Zonder deze regel zou het programma crashen (en het RAM-geheugen dus wissen) omdat de TI-OS nooit meer bereikt wordt. Denk er dus altijd aan om deze regel in te geven...
Registers
In deze paragraaf vind je alles over registers. Registers zijn opslagplaatsen voor data, net zoals je in andere programmeertalen variabelen hebt.
Soorten registers
[bewerken]Er zijn twee soorten registers: 8-bit-registers en 16-bit-registers.
8-bit-registers
[bewerken]8-bit-registers kunnen 8 bits aan gegevens opslaan (de naam zegt het al). De 8-bit-registers heten: a, b, c, d, e, f, h, l.
16-bit-registers
[bewerken]Deze registers kunnen dus 16 bits aan gegevens opslaan. Eigenlijk zijn deze registers combinaties van twee 8-bit-registers, bijvoorbeeld het register af is een combinatie van a en f. De 16-bit-registers zijn: af, bc, de, hl. Let op: als je iets opslaat in een 16-bit-register, dan wis je de bijbehorende 8-bit-registers. Voorbeeld: als je het geheugenadres $84AA opslaat in hl, dan komt in h $84 te staan en in l $AA (zie figuur hiernaast).
ld
[bewerken]Nu we weten wat registers zijn, willen we er ook graag gebruik van maken. Ten eerste zullen we hier bespreken hoe je data in de registers laadt. Dit gaat met het commando ld. Dit commando werkt als volgt.
ld a, b
Dit commando laadt de gegevens uit register b in register a.
ld a, 0
Dit commando laadt het getal 0 in register a.
ld hl, tekst [...] tekst: .db "Hoi", 0
Dit commando laadt het adres van tekst: in hl, in dit geval dus het adres van "Hoi" (de nul achter "Hoi" zorgt ervoor dat de string eindigt). Andere commando's kunnen dat dan gebruiken. Dit lijkt misschien niet zo belangrijk, maar dat is het wel. Zie het hoofdstuk over variabelen.
Soms is het beter om bepaalde registers te gebruiken als je bepaalde dingen wil doen. Soms heb je zelfs geen andere keuze. Wil je bijvoorbeeld getallen optellen, dan moet je één getal in a hebben. (a is de accumulator, het register waarin het getal staat waarmee je kunt rekenen. Er zijn bijvoorbeeld instructies om een getal bij a op te tellen)
Problemen bij het gebruik van registers
[bewerken]Er is een groot probleem als je registers gebruikt om data op te slaan. De meeste instructies, zoals bcall's, gebruiken namelijk zelf ook registers! Als je een instructie hebt gebruikt, is er een goede kans dat je data opeens weg is. Gebruik dus geen registers om data voor langere tijd op te slaan. Hiervoor kun je bijvoorbeeld variabelen of de stack gebruiken, waarover je in een volgende les meer zult leren.
Opdracht 1
[bewerken]Maak een programma dat het getal 14 in het register a laadt, en dit daarna overzet naar register b.
Het antwoord vind je aan het einde van dit boek.
Variabelen
Registers zijn perfect om gegevens op te slaan die je direct weer nodig hebt. Maar gegevens die je voor langere tijd nodig hebt, moet je dus vooral niet in een register zetten (zie einde vorige paragraaf). Daarom is er nog een andere mogelijkheid om gegevens op te slaan, namelijk variabelen. Om deze te gebruiken moeten we echter eerst twee andere dingen leren, die er ogenschijnlijk niets mee te maken hebben.
ld bij geheugenadressen
[bewerken]Soms wil je iets opslaan in het geheugen, bijvoorbeeld de inhoud van hl opslaan in geheugenplaats $8000. Je zou het misschien zo doen:
ld $8000, hl
Alleen, dat gaat niet goed. De rekenmachine gaat nu namelijk proberen om de waarde van hl in het nummer 8000 op te slaan.
Hieronder zie je hoe het wel werkt:
ld ($8000), hl
De haakjes rond het adres betekenen dat het geheugenadres wordt bedoeld.
Wil je nu het getal 0 in het adres $8000 laden, kan dat als volgt.
ld hl, $8000 ld (hl), 0
Labels
[bewerken]Labels bestaan uit regels zonder tab met een naam en daarachter een dubbele punt. Bijvoorbeeld:
Tekst:
Later in deze cursus leren we naar deze labels toe te springen, maar ze zijn nu alvast nodig voor variabelen.
Variabelen gebruiken
[bewerken]Nu we deze twee kleine puntjes behandeld hebben, gaan we door naar iets wat moeilijker is, namelijk het gebruiken van variabelen. Eerst een voorbeeld, daarna de uitleg.
Tekst: .db "Hallo!",0
Wat doet dit nu? Eerst staat er een label, dat Tekst heet. Op de volgende regel staat de instructie .db. Dit zorgt ervoor dat er aan het programma een stukje wordt toegevoegd, in dit geval dus de string "Hallo!", en daarachter een 0. Wil je de variabele nu aanspreken, dan gebruik je (Tekst). Het label Tekst is namelijk eigenlijk een geheugenadres (zoals $8000). We willen de gegevens hebben uit dit geheugenadres, dus gebruik je haakjes.
Tekst op het scherm schrijven
[bewerken]Omdat je waarschijnlijk nog steeds niet helemaal snapt wat je nu met variabelen moet en hoe ze werken, volgt hier een voorbeeldprogramma dat tekst op het scherm schrijft.
; Vul de standaardcode zelf aan (zie hoofdstuk 2). bcall(_ClrLCDFull) ; Eerst maken we het scherm leeg. ld hl, 0 ; Laad 0 in hl. ld (PenCol), hl ; Laad nu hl=0 in de kolom van schrijven. ld hl, Tekst ; Laad het label in hl. bcall(_PutS) ; Zet de tekst, die staat vanaf hl (dus het label) op het scherm. bcall(_NewLine) ; Zet de cursor op de volgende regel op kolom 0. ret ; Terug naar de TI-OS. Tekst: ; Label Tekst .db "Hallo!", 0 ; Voeg aan het programma de tekenreeks "Hallo!" toe, gevolgd door een nul.
Ik zal nu stukje voor stukje uitleggen wat er gebeurt.
bcall(_ClrLCDFull) ; Eerst maken we het scherm leeg.
Voordat we op het scherm gaan schrijven, wordt het leeggemaakt om te voorkomen dat we over iets anders heen schrijven.
ld hl, 0 ; Laad 0 in hl. ld (PenCol), hl ; Laad nu hl=0 in de kolom van schrijven.
Dit is de code om de (PenCol) (dat is de kolom waarin we gaan schrijven) 0 te maken. Dit moet echter via het register hl, dus we laden eerst 0 in hl, en daarna de inhoud van hl in (PenCol).
ld hl, Tekst ; Laad het label in hl.
Nu wordt het geheugenadres van het label Tekst geladen in het register hl. Dit zou bijvoorbeeld $8000 kunnen zijn.
bcall(_PutS) ; Zet de tekst, die staat vanaf hl (dus het label) op het scherm.
Dit is een nieuwe bcall. Hij zet de tekst, die begint bij hl, op het scherm. Hierbij houdt hij rekening met (PenCol). Het resultaat is dus dat "Hallo!" op het scherm wordt gezet.
bcall(_NewLine) ; Zet de cursor op de volgende regel op kolom 0.
De cursor wordt nu naar de volgende regel verplaatst, zodat de tekst op de volgende regel komt, als we nog iets zouden willen schrijven.
ret ; Terug naar de TI-OS.
Dit moet nu duidelijk zijn. Zie anders hoofdstuk 2.
Tekst: ; Label Tekst .db "Hallo!", 0 ; Voeg aan het programma de tekenreeks "Hallo!" toe, gevolgd door een nul.
Het label Tekst is dus eigenlijk een geheugenadres. Hierop wordt dan met de opdracht .db "Hallo!" gezet. Daarachter volgt een 0, omdat de _PutS stopt als hij een 0 tegenkomt.
Opdracht 2
[bewerken]Maak een programma met een variabele Getal, die aan het begin van het programma 0 bedraagt. Aan het eind van het programma moet de variabele 2 bedragen. Als het niet lukt, kijk dan op het einde van het boek voor het antwoord.
De stack
In plaats van variabelen, kun je om registers op te slaan en weer in te lezen ook de stack gebruiken. De stack is een soort rij met getallen. Stel het je voor als een toren met blokken. Je kunt alleen blokken erbij of eraf halen aan de bovenkant. Wat je dus het eerste erin legt komt er het laatste weer uit. Met de stack kun je ook maar twee dingen doen: een registerpaar er 'aan de bovenkant opleggen' en een registerpaar er 'aan de bovenkant afhalen'.
De stack gebruiken
[bewerken]Een registerpaar op de stack leggen
[bewerken]Je kunt alleen registerparen toevoegen aan de stack (dus af, bc, de, hl). Als je bijvoorbeeld af op de stack wilt leggen, gaat dit als volgt.
push af
Wil je bijvoorbeeld a opslaan op de stack, dan moet je f erbij nemen, omdat je alleen maar registerparen kunt opslaan in de stack.
Een registerpaar van de stack afhalen
[bewerken]Je kunt aan de 'bovenkant' van de stack een getal afhalen en deze weer opslaan in een registerpaar. Als je bijvoorbeeld het bovenste getal van de stack wilt halen en dit opslaan in af, gaat dat als volgt:
pop af
Een praktische toepassing
[bewerken]Je weet al dat je er bij instructies vanuit moet gaan dat alle registers veranderd worden. Wil je a bijvoorbeeld bewaren, maar moet er een instructie uitgevoerd worden, kan dat eenvoudig met de stack:
push af ; instructie pop af
a is nu weer teruggebracht in zijn oude staat, dus hoe hij was voordat de instructie werd uitgevoerd.
Problemen bij het gebruik van de stack
[bewerken]Er is iets waar je bij het gebruik van de stack heel goed op moet letten. Het besturingssysteem van de TI gebruikt de stack zelf namelijk ook. Je moet de stack dus precies hetzelfde achterlaten als hij was toen het programma startte. Zie het volgende voorbeeld.
; start van het programma push af push hl pop af ret ; terug naar de TI-OS
Als het programma wordt beëindigd, en de TI-OS probeert iets van de stack te halen, veroorzaak je een crash (dus het RAM wordt gewist), omdat de stack, en dus het 'bovenste' getal, niet meer hetzelfde is als vóór het programma.
Zorg er dus voor dat de stack altijd gelijk is vóór en na je programma!
Verder kan het programma crashen als je heel veel registerparen op de stack legt (bij ongeveer 100).
Hoofdstuk 2 - Controle
In dit deel leer je meer over verschillende controlestructuren die je in programma's kunt gebruiken.
Springen en subroutines
Je weet al wat labels zijn. Het is mogelijk om naar zo'n label te springen.
jp
[bewerken]Naar een label springen kan met
jp labelnaam
jp staat voor JumP. Als de processor zo'n regel tegenkomt, gaat hij verder met lezen bij het label met labelnaam. TASM vervangt ieder label namelijk door het geheugenadres daarvan. De regel kan zou dan bijvoorbeeld kunnen worden vervangen door
jp $8000
De processor weet nu waar hij heen moet springen.
jr
[bewerken]Een andere manier om te springen is
jr labelnaam
jr staat voor Jump Relative. Dit is één byte kleiner dan jp, maar het kan alleen maar als het label ongeveer 128 bytes vóór of achter de jr-regel staat. Bij jr zet TASM namelijk, in plaats van het precieze geheugenadres, neer hoeveel bytes er gesprongen moet worden. Bijvoorbeeld
jr 4
Als de label te ver weg staat, krijg je een foutmelding van TASM.
call
[bewerken]Er is nóg een manier van springen, en dat is om subroutines te maken. Het werkt als volgt:
call Subroutine ; instructies-1 ret Subroutine: ; instructies-2 ret
Eerst springt de processor naar Subroutine. Daar voert hij dus instructies-2 uit. Als hij ret tegenkomt, gaat hij weer terug naar waar hij vandaan kwam en voert hij instructies-1 uit. Bij de volgende ret eindigt het programma (dus terug naar de TI-OS).
Opdracht 3
[bewerken]Maak een programma dat vier keer een tekst op het scherm schrijft. Gebruik daarvoor een subroutine met een bcall. Roep deze vier keer aan. Het resultaat moet er ongeveer uitzien als het plaatje hiernaast. Het antwoord vind je weer aan het einde van het boek.
Optellen en aftrekken
Voordat we kunnen beginnen met voorwaarden, moeten we eerst iets weten over optellen en aftrekken.
add
[bewerken]We beginnen met optellen.
add a, b
Dit telt b op bij a en slaat het antwoord op in a.
add a, 8
Dit telt 8 op bij a en slaat het antwoord op in a.
Je kunt alleen maar iets optellen bij a of bij hl. Fout is bijvoorbeeld:
add b, 8
Met het optellen bij hl zullen we ons nu even niet bezig houden, dit komt later aan de orde.
sub
[bewerken]Aftrekken gaat als volgt.
sub b
Dat lijkt een beetje vreemd, maar het komt erop neer dat je toch alleen maar kunt aftrekken van a, daarom wordt de a ervoor weggelaten. Deze instructie haalt dus b van a af en slaat het antwoord op in a.
inc en dec
[bewerken]Wil je één optellen of aftrekken, dan kun je respectievelijk inc of dec gebruiken. Dit gaat als volgt:
inc a
Dit telt één op bij a. Je kunt hier alle registers voor gebruiken.
Het register f
Uit het hoofdstuk Registers ken je al het register f. Dit register is echter anders dan de andere registers die je al kent; je kunt het niet rechtstreeks wijzigen.
De opbouw van het register f
[bewerken]Hieronder zie je een schema hoe het f-register in elkaar zit. Iedere kolom in de tabel is een vlag. Zo'n vlag is een bit in het register f.
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
Sign | Zero | - | Half-carry | - | Parity-overflow | Add-subtract | Carry |
Er zijn twee vlaggen die nu voor ons van belang zijn. Als een vlag 1 is (ook wel set genoemd, 0 heet ook wel reset), betekent dit het volgende.
Zero
[bewerken]De vorige berekening had als uitkomst 0.
Carry
[bewerken]De vorige berekening had een uitkomst die groter was dan wat er in het register paste. Zie het volgende voorbeeld. Tel de (binaire) getallen 1101 en 1000 op. Het resultaat is 10101. Ofwel, er is één bit meer nodig om het antwoord op te slaan. Het resultaat van deze bewerking wordt dan 0101 en de Carry-vlag wordt 1 gemaakt. Als een aftreksom een antwoord kleiner dan nul opleverde, gaat de Carry-vlag ook aan. Wil je de Carry-vlag 1 maken, dan gebruik je de instructie SCF (Set Carry Flag) Wil je de Carry-vlag veranderen (dus 1 wordt 0 en omgedraaid), dan gebruik je de instructie CCF.
Voorwaarden
Een bekende structuur die in heel veel programma's voorkomt, is de if-then-structuur, dit is een structuur die een stuk code alleen uitvoert als een voorwaarde waar is. In Assembly gaat dit iets moeilijker...
cp
[bewerken]Als eerste is de instructie cp van belang. Deze doet hetzelfde als sub, maar slaat het resultaat niet op. Wat hebben we er dan aan? De vlaggen worden wél ingesteld. Zie dit voorbeeld.
sub 8
Stel je voor dat a 8 was, staat er nu 0 in a. De Zero-vlag is dus ingesteld op 1, want de uitkomst is 0.
cp 8
Stel je voor dat a 8 was, is de Zero-vlag dus ook ingesteld op 1, want de uitkomst van de som is 0, maar er staat nog steeds 8 in a. Als a bijvoorbeeld 9 was geweest, is de Zero-vlag dus 0.
Voorwaarden gebruiken
[bewerken]Om een voorwaarde te kunnen gebruiken, hebben we dus iets nodig dat kijkt of de Zero-vlag 0 is. Kijk eerst naar dit voorbeeld:
jp z, Label
Dit springt alleen naar Label als de Zero-vlag 1 is. Als de Zero-vlag 0 is, gaat de uitvoering gewoon verder na de instructie. Op deze manier kun je dus een voorwaarde stellen. De voorwaarden die je kunt gebruiken:
Voorwaarde | Werking |
---|---|
z | Voert de actie alleen maar uit, als de Zero-vlag 1 is. Ofwel, als je een cp hebt uitgevoerd, alleen als a gelijk is aan het cijfer achter cp. |
nz | Voert de actie alleen maar uit, als de Zero-vlag 0 is. Ofwel, als je een cp hebt uitgevoerd, alleen als a niet gelijk is aan het cijfer achter cp. |
c | Voert de actie alleen maar uit, als de Carry-vlag 1 is. Ofwel, als je een cp hebt uitgevoerd, alleen als het resultaat van de aftreksom kleiner is dan 0, dus alleen als a kleiner is dan het cijfer achter cp. |
nc | Voert de actie alleen maar uit, als de Carry-vlag 0 is. Ofwel, als je een cp hebt uitgevoerd, alleen als het resultaat van de aftreksom niet kleiner is dan 0, dus alleen als a groter dan of gelijk is aan het cijfer achter cp. |
Deze voorwaarden kun je gebruiken achter de volgende instructies: jp, jr, call en ret.
Loops
Met behulp van de voorwaarden is het mogelijk om loops te maken. (Loops zijn lussen, zie de pagina Controlestructuren uit het boek Programmeren in TI-Basic als je niet weet wat dat zijn.)
De While-loop
[bewerken]De While-loop bouwen we door eerst te kijken hoe je deze in woorden zou uitdrukken. Stel je een While-loop voor: While a=0
- Als a niet gelijk is aan 0, spring naar het einde van de loop.
- Voer bepaalde instructies uit.
- Ga nu terug naar het begin.
- Deze instructies worden uitgevoerd als a een keer niet gelijk is aan 0 geweest.
Dit kan als volgt worden omgezet in Assembly-code:
; stap 1 BeginLoop: cp 0 ; controleer of a gelijk is aan 0 jp nz, EindLoop ; als dat niet zo is, spring naar EindLoop ; stap 2 ; instructies ; stap 3 jp BeginLoop ; stap 4 EindLoop: ; instructies
Bij While-loops moet je oppassen voor oneindige loops. Zie het volgende voorbeeld:
ld a, 1 BeginLoop: cp 0 ; controleer of a gelijk is aan 0 jp nz, EindLoop ; als dat niet zo is, spring naar EindLoop inc b jp BeginLoop EindLoop: ; instructies
Dit zorgt voor een probleem: a blijft natuurlijk 0, omdat er in de instructies binnen de While-loop niets mee gebeurt. Het resultaat is bekend: het enige wat hiertegen nog kan worden gedaan, is de batterijen uit de rekenmachine halen.
De For-loop
[bewerken]De For-loop is een loop waarbij het aantal keren herhalen van te voren bekend is. Het is eigenlijk een speciaal soort While-loop, namelijk een While-loop waarbij bij iedere cyclus een teller ééntje verhoogd wordt. De voorwaarde is hierbij een controle of de teller een bepaald getal is. Bijvoorbeeld: je wilt een instructie vijf keer uitvoeren.
ld a, 0 BeginLoop: cp 5 ; controleer of a gelijk is aan 5 jp nz, EindLoop ; als dat niet zo is, spring naar EindLoop inc a jp BeginLoop EindLoop: ; instructies
Dit is goede code, maar hij kan wellicht beter. Het is immers niet handig om a te gebruiken voor de teller, aangezien dit ook het rekenregister is. Het is dus beter om b te gebruiken. Maar dan is het niet mogelijk een cp te gebruiken, aangezien dit alleen bij a kan. dec kan wél bij b gebruikt worden. Deze set de zero-vlag als het resultaat 0 is. Om het gebruik van dec mogelijk te maken, is het dus nodig om de teller steeds te verlagen, in plaats van hem te verhogen. Je begint bijvoorbeeld bij 5, en telt dan af tot 0. Als de teller 0 is, dan spring je uit de loop. Dit gaat dus als volgt.
ld b, 5 BeginLoop: ; instructies dec b ; b met 1 verlagen; als b niet 0 is, gaat de zero-vlag uit jp nz, BeginLoop ; als de zero-vlag uit is, ofwel als b niet 0 is, spring naar BeginLoop EindLoop: ; instructies
Of, korter:
ld b, 5 BeginLoop: ; instructies djnz BeginLoop ; is hetzelfde als dec b, jp nz BeginLoop EindLoop: ; instructies
Opdracht 4
[bewerken]Pas opdracht 3 aan, en gebruik nu een for-loop. Het antwoord staat weer achterin het boek.
Antwoorden
Opdracht 1
[bewerken]Een mogelijk antwoord zie je hier. Vul de standaardcode zelf aan.
ld a, 14 ; 14 in a laden ld b, a ; a in b laden ret ; terug naar TI-OS
Opdracht 2
[bewerken]Een mogelijk antwoord zie je hier. Vul de standaardcode zelf aan.
ld a, 2 ; 2 in a laden ld (Getal), a ; het getal in a in Getal laden (haakjes!) ret ; terug naar TI-OS Getal: .db 0
Merk op dat je 2 niet direct in (Getal) kunt laden, dit moet via een register (in dit geval a).
Opdracht 3
[bewerken]Een mogelijk antwoord zie je hier. Vul de standaardcode zelf aan.
call TekstSchrijven ; TekstSchrijven aanroepen call TekstSchrijven ; ... en nog een keer ... call TekstSchrijven call TekstSchrijven ret ; Terug naar de TI-OS. TekstSchrijven: ; Let op, níét PenCol instellen, want dan zouden de "Hallo!"'s iedere keer op dezelfde plek komen! ld hl, Tekst ; Laad het label in hl. bcall(_PutS) ; Zet de tekst, die staat vanaf hl (dus het label) op het scherm. bcall(_NewLine) ; Zet de cursor op de volgende regel op kolom 0. ret ; Terug naar de hoofdmodule. Tekst: ; Label Tekst .db "Hallo!", 0 ; Voeg aan het programma de tekenreeks "Hallo!" toe, gevolgd door een nul.
Opdracht 4
[bewerken]Een mogelijk antwoord zie je hier. Vul de standaardcode zelf aan. Deze oplossing is nog niet getest!
ld b, 4 BeginLoop: call TekstSchrijven ; TekstSchrijven aanroepen djnz BeginLoop ; Één aftrekken van b en als het resultaat niet 0 is, naar BeginLoop springen EindLoop: ret ; Terug naar de TI-OS. TekstSchrijven: ; Let op, níét PenCol instellen, want dan zouden de "Hallo!"'s iedere keer op dezelfde plek komen! ld hl, Tekst ; Laad het label in hl. bcall(_PutS) ; Zet de tekst, die staat vanaf hl (dus het label) op het scherm. bcall(_NewLine) ; Zet de cursor op de volgende regel op kolom 0. ret ; Terug naar de hoofdmodule. Tekst: ; Label Tekst .db "Hallo!", 0 ; Voeg aan het programma de tekenreeks "Hallo!" toe, gevolgd door een nul.
Opmerking: in dit geval is het niet veel korter, maar vooral bij 5 of meer herhalingen is dit erg nuttig.