6502 Assembly Cursus Deel 5
Als je programma's schrijft, ontkom je er niet aan om zo af en toe een sprong terug of naar voren te maken. Vergelijkbaar met BASIC heeft Assembly deze natuurlijk ook. Maar Assembly heeft ook een eigen register dat bijhoudt welke regel momenteel wordt uitgevoerd. Na dit deel zul je dus naast de reeds bekende A, Y en X registers ook kennis gemaakt hebben met het programcounter (PC) register! Er zijn maar weinig programma's die alle opdrachten van voren naar achteren afwerken zonder ergens te springen of te vertakken. In dit deel behandelen we twee technieken van verspringen. Ook het register dat die de sprongen controleert en bijhoudt laten we kort de revu passeren. Onvoorwaardelijke sprongen Een onvoorwaardelijke sprong doet precies wat zijn naam zegt; spring naar een ander deel van het programma zonder verder ergens naar te kijken. Er zijn dus geen voorwaarden aan verbonden. Sprongen zijn met name handig om bepaalde stukken code te hergebruiken. Door bijvoorbeeld een stuk muziek wat in het geheugen staat op verschillende momenten aan te kunnen roepen. Er bestaan slechts twee onvoorwaardelijke 6510 opdrachten: JMP - JuMP JSR - Jump naar SubRoutine BASIC vertaling van GOTO = JMP In BASIC kon je met behulp van het GOTO commando door je hele programma heen springen, naar voren en naar achteren. In Assembly gebruiken we hier JMP voor. We kunnen het JMP commando het beste demonstreren aan de hand van een voorbeeld (startadres is 828): OPCODE OPERAND LDAIM #1 (Laad 1 in register A) JMP 834 (Spring naar geheugenadres 834) RTS (Spring terug - in dit geval terug naar BASIC) STA 1024 (Zet de inhoud van A op adres 1024) STA 55296 (Zet de inhoud van A op adres 55296) JMP 833 (Spring naar geheugenadres 833) END Wat hier gebeurt is dat er direkt naar de STA 1024 gesprongen wordt, over de RTS heen. Bovenstaande voorbeeld is natuurlijk weinig zinvol, maar dient louter ter demonstratie van het JMP commando. Het zou bijvoorbeeld gebruikt kunnen worden om later een stukje coding "in te voegen". Vergeet niet dat het voor de snelheid niet uitmaakt waar je regels staan. Je kunt zonder snelheidsverlies van hot naar hen springen. Bovendien waren de editors vroeger niet zo gebruikersvriendelijk, en het was vaak makkelijker/sneller om achteraf een sprong te maken naar extra coding in plaats van coding tussen te voegen. Zoals je aan dit schema hiernaast kunt zien is dat precies wat we met het voorbeeld aantonen. Dit (stroom)schema is een grafische vertaling van het Assembly programma hierboven. Dit laat duidelijk zien hoe het verloop van de code is. Het programma drukt overigens een witte "A" af in de linker bovenhoek, maar na Deel 4 had je dat natuurlijk al zelf gezien. Als je dus het JMP commando gebruikt, zul je precies moeten vertellen waarheen er gesprongen moet worden, er moet een adres opgegeven worden. Ook om weer terug te komen op de originele plek zul je dus een JMP commando moeten gebruiken. Het heen springen zal mischien niet zo schokkend zijn, maar hoe kom je nu weer precies terug op de regel na je originele sprong? Met andere woorden, hoe weet je dat je naar geheugenadres 833 moet springen zoals in ons voorbeeld? Adressen berekenen Alle OPCODES hebben een bepaalde grote, ze nemen precies 1 byte in beslag. De OPERAND neemt ook geheugen in beslag, alleen de bepaling daarvan is iets ingewikkelder. Dit is een kwestie van weten of opzoeken, raak dus niet in paniek. Als je een geheugenadres in een OPERAND gebruikt (zoals bij JMP en STA) kost dat 2 bytes extra omdat een geheugenadres getallen kan bevatten die groter zijn dan de 255 die in één byte passen. (1 byte = 8 bits en kan maximaal de waarde 255 bevatten weten we uit Deel 1) Ons programma begint op geheugenadres 828. Voor het gemak zullen we de geheugen adressen voor de coding zetten: ADRES OPCODE OPERAND GEBRUIKT 828 LDAIM #1 (828 plus 1 byte voor "#1") 828 t/m 829 830 JMP 834 (830 plus 2 bytes voor het adres "834") 830 t/m 832 833 RTS (geen operant, dus geen extra bytes) 833 t/m 833 834 STA 1024 (834 plus 2 bytes voor het adres "1024") 834 t/m 836 837 STA 55296 (837 plus 2 bytes voor het adres "55296") 837 t/m 839 840 JMP 833 (840 plus 2 bytes voor het adres "833") 840 t/m 842 END Om dus over (voorbij) RTS te springen, springen we van geheugenadres 830 naar 834. We springen hierbij dus over adres 833 waar RTS staat. Zoals eerder gezegd, gewoon opzoeken, zo zijn we allemaal begonnen. Geloof het of niet, na een tijdje wordt het tweede natuur. Ik wil bovenstaande ook nog in een Assembly Machinetaal vergelijking gieten: ADRES ASSEMBLY MACHINETAAL 828 LDAIM #1 A9 01 830 JMP 834 4C 42 03 833 RTS 60 834 STA 1024 8D 00 04837 STA 55296 8D 00 D8840 JMP 833 4C 41 03 Als je onder het kopje Machinetaal kijkt, bevat de eerste kolom de OPCODE, de tweede en derde kolom bevat de OPERANT. In machinetaal kun je duidelijk zien dat "LDAIM #1" twee bytes in beslag neemt (adres 828 en 829) en "JMP 834" drie bytes (830, 831 en 832) in beslag neemt. Als je naar de Machinetaal listing kijkt, zie je overigens ook dat ons gehele programma 15 bytes in beslag neemt. BASIC vertaling van GOSUB...RETURN = JSR...RTS De BASIC kenners onder ons kunnen zich nog een sprong herinneren. Naast de GOTO kennen we namenlijk ook nog een GOSUB ... RETURN opdracht in BASIC. In Assembly gebruiken we hiervoor JSR...RTS. Het grote voordeel is dat je bij het terugspringen niet een adres hoeft op te geven (en berekenen), de RTS maakt gebruik van de verderop besproken programmateller om te bepalen waar naartoe teruggesprongen moet worden. We maken weer gebruik van hetzelfde voorbeeld, maar ditmaal gebruiken we JSR en RTS. OPCODE OPERANDLDAIM #1 (Laad 1 in register A) JSR 834 (Spring naar geheugenadres 834) RTS (Spring terug - in dit geval terug naar BASIC) STA 1024 (Zet de inhoud van A op adres 1024) STA 55296 (Zet de inhoud van A op adres 55296) RTS (Spring terug naar de regel na JSR 834 - in dit geval RTS) END Hieronder laten we het voorbeeeld nog even in een programma stroomschema zien. De JSR instructie biedt dus de mogelijkheid om naar kleine stukjes programma's (subroutines) te springen vanuit je hoofd programma. De RTS instructie springt telkens uit de huidige subroutine terug naar de bovenliggende programma vanwaar het werd aangeroepen. Gebruik je RTS in je hoofdprogramma, dan snapt de 6510 processor dat je terug wilt springen naar BASIC. Oplettende lezers zien trouwens ook dat ons JSR...RTS voorbeeld slechts 13 bytes groot is (2 bytes kleiner dan ons JMP voorbeeld) omdat het RTS commando geen OPERAND code nodig heeft voor een adres van twee bytes groot. De Programmateller Zoals we bij het laatste voorbeeld zagen, wist de processor zelf naar waar hij terug moest springen bij de RTS instructie. Dit betekent dus, dat de computer ergens bijhoudt op welke plek hij in een programma is. Dit is het moment waarop we de programmateller (programcounter of PC) uit de kast trekken. De PC is een 16-bits register dat het adres bevat van het volgende commando dat moet worden uitgevoerd. Iedere keer als de PC een byte uit het geheugen haalt wordt hij met 1 verhoogd. Op deze manier blijft hij altijd naar de volgende geheugenlocatie met de bijbehorende instructie wijzen. Maak je geen zorgen, een voorbeeld zal dit verhaal verduidelijken. Ons programma begon op adres 828. OPCODE OPERAND PC voor instructie PC na instructie 828 LDAIM #1 828 830 830 JSR 834 830 834 ... De PC weet dus altijd waar je bent in je programma. De methode die de PC gebruikt noemen we stapelen, waar we later bij het bespreken van het stapelgeheugen (STACKs) op terugkomen. Wat belangrijk is om voor nu te begrijpen, is (1) dat het PC register bestaat en (2) dat het dient om tijdens de uitvoering van je programma bij te houden op welke "regel" (eigenlijk adres) het programma zich bevindt. Kort samenvattend is het duidelijk dat de nummering die je bij de BASIC programma's gebruikt geenzins overeenkomt met de nummering in Assembly. Waar je in BASIC gewoon nummers kunt verzinnen, moet je bij Assembly bewust zijn van het feit dat dit adressen zijn. Later, als we Macro's gaan gebruiken wordt dit wat makkelijker. We kennen nu twee manieren om door het programma te springen. De JMP gebruik je in feite om voorbij stukken programma te springen, terwijl je de JSR...RTS combinatie gebruikt om een tijdelijk "uitstapje" te maken met het voornemen later weer verder te gaan waar je gebleven was. Het programmateller register houdt voor ons bij welke regel er uitgevoerd gaat worden om ons wat denkwerk te besparen.
25 februari 2024