Assembly Cursus 2

ACVorige keer hebben we een korte inleiding gehad op de hardware-laag van de Computer. We grijpen heel even kort terug naar Deel 1, hoe zat het ook alweer? Nu we de basis behandeld hebben van de binnenste werking van de Commodore, ga ik verder met het beschrijven van de registers. We hebben er zes te gaan in dit deel.

CursusAssembly Deel1 hdr

ACComputer: Commodore 64
Datum: 2011
Type: Programmeertaal
Door: Addy van Ladesteijn

De Registers
Vorige keer hebben we een korte inleiding gehad op de hardware-laag van de Computer. We grijpen heel even kort terug naar Deel 1, hoe zat het ook alweer?

We hebben de verschillen tussen de verschillende formaten Hexadecimaal, Decimaal en Binair behandeld en de manier van omrekenen uitgelegd. Handig om te weten, niet vereist omdat we hiervoor rekenmachines hebben.

Ook hebben we het over de Address-, Data en Memorybus gehad. De Addressbus dient als communicatie tussen processor en de chips. De Databus handelt de lees- en schrijfopdrachten van en naar de chips af. De Memorybus bestaat uit ROM (Alleen lezen), RAM (Lezen en Schrijven) en IA (Koppelingen naar buitenwereld).

De verschillende registers binnen de processor zijn kort besproken. Ook hebben we ter afsluiting laten zien hoe deze registers werken en hoe je er informatie in kunt stoppen of uit kunt halen. We hebben hiervoor een klein assembleerprogramma gebruikt.

Nu we de basis behandeld hebben van de binnenste werking van de Commodore, ga ik verder met het beschrijven van de registers. We hebben er zes te gaan in dit deel.

Stel we willen het onderstaande basic programma vertalen naar Assembly:

BASIC
10 LET A = 1 (Geef A de waarde "1")
20 LET B = 2 (Geef B de waarde "2")
30 C = A + B (Vul C met de som van A en B)

Voor de meeste mensen is dit een eenvoudig programma. Je stopt een waarde in A (in dit geval een “1”) en  je stopt een waarde in B (in dit geval een “2”). Vervolgens tel je de inhoud van A bij de inhoud van B op en stopt het antwoord in C. Kind kan de was doen nietwaar? 
In BASIC misschien wel, maar nu gaan we dit programma eens naar Assembly vertalen. We gaan hiervoor gebruik maken een aantal Assembly commando’s (er bestaan er in totaal trouwens 56). We gebruiken in dit voorbeeld LDXIM (LoaD X IMmediately) als je het X-register wilt vullen met een door jou opgegeven waarde. Dit commando is er voor elk register, dus er bestaat ook een LDYIM en LDAIM. Zie hier de Assembly versie van bovengenoemde BASIC programma.

ASSEMBLY
LDXIM 1 (Laad Register X met de waarde "1")
STX 900 (Sla de waarde van X (1) op in adreslocatie 900, dat is hex $0384)
LDAIM 2 (Laad Register A de waarde "2")
ADC 900 (Tel de inhoud van geheugenlocatie 900/$0384 op bij register A)
STA 901 (Zet de inhoud van het A Register in geheugenlocatie 901/$0384)
RTS     (Keer terug uit de machinecode subroutine - Einde dus)

Oplettende lezers hebben natuurlijk direct gezien dat we de registers nodig hebben om data in het geheugen te kunnen plaatsen. Waar je in Basic gewoon een variabele een waarde geeft, moet je dit in Assembly via een Register doen. Je kunt dus niet rechtstreeks een waarde in het geheugen zetten; je vult een register met een waarde, waarna je de inhoud van het register in een geheugenplek moet zetten. Het vakjargon voor het  A register is eigenlijk “Accumulator”. In het verdere verloop van deze cursus zullen we dus de term Accumulator gebruiken als we het over het A register hebben. Het waarom wordt vanzelf duidelijk, voor nu gewoon onthouden.

Even kort de “vertaling” van de hierboven gebruikte commando’s:

LDXIM   - LoaD register X IMmediately
STX     - STore X register (to location …)
LDAIM   - LoaD register A IMmediately
ADC     - ADd to accumulator with Carry
STA     - STore A register (to location …)
RTS     - ReTurn from Subroutine

Dus, samenvattend, er zijn zes typen registers, ieder met zijn eigen kracht en taak. We beginnen met de Accumulator en Indexregisters (X en Y). Deze registers kunnen informatie uit het geheugen halen maar slechts een stukje informatie per keer bevatten. Registers zijn zelf geen onderdeel van het geheugen en hebben daarom ook geen eigen adres. Ze zijn onderdeel van de microprocessor en er bestaan speciale instructies (commando’s) voor elk specifiek register.

ACDe Accumulator

Het eerste register wat we behandelen is de Accumulator (het A-register). We kunnen de Accumulator beschouwen als het hoofdregister en dat hij, zoals de meeste andere registers, acht bits groot is. Dit betekent dat hij op elk moment één enkele byte met informatie kan bevatten. Omdat dit het hoofdregister is maken de meeste instructies er gebruik van en heeft de accumulator als belangrijkste kenmerk dat alle rekenkundige en logische operaties door de Accumulator worden uitgevoerd. Als er een wiskundige of logische handeling wordt uitgevoerd, moet een van de waarden altijd in de Accumulator staan, de andere in het geheugen. Het resultaat zal vervolgens altijd in de Accumulator komen te staan. Dit kun je zien in het Assembly programma op de vorige pagina. De ADC 900 instructie zorgt ervoor dat de waarde die in geheugenlocatie 900 stond (in dit geval “1”) opgeteld wordt met de waarde die in de Accumulator stond (dat was “2”). Het resultaat zal vervolgens in de Accumulator terecht komen. Na het uitvoeren zal dus de eerdere Accumulator waarde “2” vervangen zijn door de waarde “3”; het resultaat van de som van 1 + 2. Naast zijn rol als wiskundige, mag de Accumulator ook gewoon gebruikt worden voor het verplaatsen van data, zoals de X en Y registers.

De instructies die specifiek bij de Accumulator horen zijn:
ADC - ADd with Carry (tel op met overdracht)
AND - logical AND (logische AND)
ASL - Arithmetic Shift Left (rekenkundige verschuiving naar links)
BIT - compare memory BITs (vergelijk met bits in het geheugen)
CMP - CoMPare to accumulator (vergelijk met de inhoud van het A-register)
EOR - logical EOR (logische EOR)
LDA - LoaD the Accumulator (laad het A-register)
LSR - Logical Shift Right (rekenkundige verschuiving naar rechts)
ORA - logical OR Accumulator(logische OR)
PHA - PusH Accumulator (plaats de inhoud van het A-register op de stapel)
PLA - PuLl Accumulator (haal de inhoud van het A-register van de stapel)
ROL - ROtate Left (roteer naar links)
ROR - ROtate Right (roteer naar rechts)
SBC - SuBstract with Carry (trek af met overdracht)
STA - STore the Accumulator (sla de accumulatorinhoud op)
TAX - Transfer Accumulator to X-register (inhoud van A-register naar X-register)
TAY - Transfer Accumulator to Y-register (inhoud van A-register naar Y-register)
TXA - Transfer X-register to Accumulator (inhoud van X-register naar A-register)
TYA - Transfer Y-register to Accumulator (inhoud van Y-register naar A-register)

De Indexregisters (X-register en Y-register)
Behalve de Accmulator bevat de processor nog twee registers die de gegevens van één enkele byte kunnen bevatten, het X-register en het Y-register. Deze registers worden algemeen als de indexregisters betiteld, omdat ze meestal als loop counter (For/Next) of als tabelindex gebruikt worden. Ze hebben hiervoor ook speciale instructies, bijvoorbeeld DEX/INX of DEY/INY voor het verlagen/verhogen van de waarde in het X of Y register. Ze kunnen echter ook gebruikt worden voor het verplaatsen van data.

Instructies voor het X-register:
CPX - ComPare X-register
DEX - DEcrement X-register (verminder het X-register)
INX - INcrement X-register (verhoog het X-register)
LDX - LoaD the X-register (laad/vul het X-register)
STX - STore the X-register (sla de inhoud uit het X-register ergens op)
TAX - Transfer Accumulator to X-register (inhoud A-register naar X-register)
TXA - Transfer X-register to Accumulator (inhoud X-register naar A-register)
TSX - Transfer Status to X-register (inhoud van status register naar X-register)
TXS - Transfer X-register to Status (inhoud van X-register naar Statusregister)

Instructies voor het Y-register:

CPY - ComPare Y-register (vergelijk inhoud met het Y-register)
DEY - DEcrement Y-register (verminder het Y-register)
INY - INcrement Y-register (verhoog het Y-register)
LDY - LoaD Y-register (laad/vul het Y-register)
STY - STore the Y-register (sla inhoud uit het Y-register ergens op)
TAY - Transfer Accumulator to Y-register (inhoud A-register naar het Y-register)
TYA - Transfer Y-register to Accumulator (inhoud Y-register naar het A-register)

Programmateller
De programmateller is het adressenboek van de processor; hij bevat bijna altijd het geheugenadres van de volgende uit te voeren instructie. In tegenstelling tot de andere registers is de programmateller een register van 16 bits dat fysiek is samengesteld uit twee 8-bits registers. Deze twee registers noemt men respectievelijk de program counter high (PCM) of hoge programmateller en de program counter low (PCL) of lage programmateller. De programmateller heeft dus als hoofdtaak om te onthouden wat de volgende instructie is die uitgevoerd moet worden, handig, want dan hoeven wij dat niet meer te doen.

Statusregister
De ACprogram status register is het enigste register dat elke bit uniek adresseert. Elke bit heeft een functie; vertelt een status. Als we het statusregister gebruiken interesseert ons niet zozeer de actuele hexwaarde, maar wel de toestand of status van de individuele bits.  Van de acht registerbits worden er slechts zeven gebruikt – de resterende bit (bit nummer 5) is voortdurend geset (is dus altijd “1”).
ACIk wil nu nog niet te ver ingaan op het status register, ik wil je er alleen maar even aan voorstellen. Als we later verder zijn, zullen we het statusregister leren uitlezen en worden de bits afzonderlijk behandeld.

Stackpointer
De stack is een gemakkelijke plek om informatie tijdelijk in weg te zetten. Je kunt de stack het beste zien als een stapel documenten: je kunt een document bovenop de stapel leggen (drop/push) of je kunt een document van de stapel halen (pull/take). Formeel geldt hier het concept Last In, First Out (LIFO). De belangrijkste regel is dat je opruimt wat je op de stap legt. Leg je drie dingen op de stapel, haal deze er dan ook weer af. De stack is pagina 1 in het geheugen. De stackpointer houdt dus bij wat het laatste is wat op de stapel gelegd werd. Terwijl de stapel vult, zal de stackpointer lager worden (minder ruimte). Als er dingen van de stapel gehaald worden zal de pointer weer stijgen. De processor maakt het niets uit hoeveel je op de stapel gooit, maar als de stapel vol is, zal de pointer niet meer veranderen met het gevolg dat je dingen niet meer van de stapel kunt halen.

We hebben deze keer de registers binnen in de processor behandeld. Het lijkt niet veel, maar geloof me, deze zijn erg belangrijk en gaan we nog heel vaak tegenkomen. Volgende keer gaan we met de eerder besproken commando’s aan de gang en leer je de registers nog beter kennen. We zullen dan tevens onze eerste programma’s invoeren en hopelijk zonder kleerscheuren runnen.