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
  1. Assembly-basis
    1. Voorbereiding  
    2. Het eerste programma  
    3. Registers  
    4. Variabelen  
    5. De stack  
  2. Controle
    1. Springen en subroutines  
    2. Optellen en aftrekken  
    3. Het register f  
    4. Voorwaarden  
    5. Loops  
  3. Geheugenbeheer
    1. Getallenstelsels  
    2. Het gebruik van EQU  
    3. Arrays en matrices  
    4. Het gebruik van ldir  
  4. Input en output
    1. Logische en bit-instructies  
    2. Strings schrijven  
    3. Karakters schrijven  
    4. getKey  
    5. Invoer door de gebruiker  
  5. Applications  
    1. Het maken van applications  
  6. Speciaal
    1. Problemen  
    2. TASM-instructies  
    3. ROM-calls  


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:

@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...
 De include-file
Wil je dit boek naast andere tutorials gebruiken, dan is het verstandig om ti83plus.inc enigszins aan te passen. Sommige tutorials gebruiken namelijk andere versies van dit bestand en dat zorgt ervoor dat programma's uit die tutorials dan niet goed werken of niet compileren. Het grootste verschil is bcall(...), deze wordt door veel andere tutorials geschreven als b_call(...). Dit kan gemakkelijk worden opgelost in ti83plus.inc door de volgende aanpassing te doen: voeg onder de regel
#define bcall(xxxx)	rst 28h \ .dw xxxx

de volgende regel toe:

#define b_call(xxxx)	rst 28h \ .dw xxxx


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).

 
Als je iets opslaat in hl, dan wordt dat eigenlijk gewoon gesplitst en geplaatst in h en l.

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 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.

 
Iedere keer als iets op de stack gelegd wordt, dan komt dit "bovenop de toren" te liggen. Je kunt dan niet meer direct bij onderliggende getallen.
 
Merk op dat je een getal dat op de stack ligt, er weer met een ander registerpaar vanaf kunt halen. Dat gebeurt hier bijvoorbeeld met af/de.

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.

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.

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.

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
 
Het resultaat

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.

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.

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.

De vorige berekening had als uitkomst 0.

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.

 Wijzigen van f
Zoals hierboven gezegd is, kun je f niet zomaar wijzigen:
    ld f, 5

werkt bijvoorbeeld niet. Je kunt wel af aanpassen, bijvoorbeeld door pop af, f verandert dan mee. Denk eraan dat de vlaggen dan dus weg zijn.



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...

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

  1. Als a niet gelijk is aan 0, spring naar het einde van de loop.
  2. Voer bepaalde instructies uit.
  3. Ga nu terug naar het begin.
  4. 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
 
Het resultaat

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.

Informatie afkomstig van https://nl.wikibooks.org Wikibooks NL.
Wikibooks NL is onderdeel van de wikimediafoundation.