#Klooienmetcomputers

Variabele input

Arnout van Kempen over rommelen in een digitale wereld.

Ons programma kan nu vrij aardig uitkomsten weergeven, maar de som die wordt berekend is hard gecodeerd, dus voor iedere andere berekening moeten we het programma herschrijven en opnieuw opbouwen tot executable. Dat is misschien maar een kleine moeite, het heeft toch iets raars. We gaan dus wat vervolg stappenzetten. Om te beginnen gaan we input op de commandline toevoegen, en daarbij meteen ook maar verschillende rekenopties toevoegen: optellen, aftrekken, vermenigvuldigen en delen. Om het behapbaar te houden gaan we ons wel beperken tot de basisbewerkingen, op twee decimale integers van maximaal 64 bits. Als we ons programma bijvoorbeeld "calc" noemen, dan zou de volgende aanroep verwerkt moeten kunnen worden:

calc 1+1

En de uitkomst is dan natuurlijk 2, 2, 2, 10. Decimaal, hexadecimaal, octaal en binair immers.

We worden hier enorm geholpen door Linux. Of je nu in C, Rust of ARM64 programmeert, Linux levert bij de start van een programma via de stack pointers op naar alle argumenten die aan het programma zijn meegegeven. Als we die argv[n] noemen, dan is argv[0] de naam van het programma zelf, in dit geval dus calc, argv[1] het eerste argument, in dit geval 1+1 en zo verder, totdat er een argument argv[n] komt met de waarde NULL, waarmee het einde van de reeks wordt aangeduid.

Het ophalen van het eerste argument kan dus vrij simpel als volgt:

          ldr x0, [sp, 8]          // haal pointer naar argv[1] van de stack

Vervolgens zullen we dat argument moeten ontleden, ook wel parsen genoemd. Laten we dat in een aparte subroutine doen, dus:

     bl parse_expression

En ergens anders:

parse_expression:
     mov x3, 0                   // Beginwaarde operand 1
     mov x4, 0                   // Beginwaarde operand 2
     mov x5, 0                   // Operatie (bijv. '+')
     mov x6, x0                  // Pointer naar string

parse_loop:
     ldrb w7, [x6], 1            // Lees één karakter
     cmp w7, '+'                 // Controleer of het een '+'
     b.eq found_operator
     cmp w7, '-'                 // Controleer of het een '-'
     b.eq found_operator
     cmp w7, '*'                 // Controleer of het een '*'
     b.eq found_operator
     cmp w7, '/'                 // Controleer of het een '/'
     b.eq found_operator
     sub w7, w7, '0'             // Zet ASCII-cijfer om naar integer
     mul x3, x3, 10              // x3 = x3 * 10
     add x3, x3, w7              // Voeg cijfer toe aan x3
     b parse_loop

found_operator:
     mov x5, w7                  // Sla operator op in x5

parse_second_operand:
     ldrb w7, [x6], 1            // Lees volgende karakter
     cbz w7, done_parsing        // Stop als null-terminator
     sub w7, w7, '0'             // Zet ASCII-cijfer om naar integer
     mul x4, x4, 10              // x4 = x4 * 10
     add x4, x4, w7              // Voeg cijfer toe aan x4
     b parse_second_operand

done_parsing:
     ret                         // Klaar met parsen

Met de gezochte waardes in x3 en x4, kunnen we nu aan het rekenen. In x5 staat de bewerking, dus in het hoofdprogramma kunnen we dat als volgt verwerken: 

     cmp x5, '+'                 // Optelling?
     b.eq do_add

     cmp x5, '-'
     b.eq do_sub

     cmp x5, '*'
     b.eq do_mul

     cmp x5, '/'
     b.eq do_div

     b invalid_operator       // Ongeldig teken

do_add:
     add x6, x3, x4           // x6 = x3 + x4
     b print_result

do_sub:
     sub x6, x3, x4           // x6 = x3 - x4
     b print_result

do_mul:
     mul x6, x3, x4           // x6 = x3 * x4
     b print_result

do_div:
     udiv x6, x3, x4               // x6 = x3 / x4
     b print_result

invalid_operator:
     ldr x0, =error_msg       // Laad foutmelding
     mov x8, 64                    // Syscall: write
     svc 0
     b exit_program

print_result:
     mov x5, x6                  // Plaats resultaat in x5
     // hier komt de code van de vorige keer

exit_program:
     // zie code vorige keer

In principe hebben we hiermee een heuse calculator bij elkaar. We moeten deze code en die van de vorige keer nog wel samenvoegen, bekijken of we de logica en het gebruik van registers wat moeten stroomlijnen en foutafhandeling toevoegen. Op dit moment beperkt die foutafhandeling zich tot het ontbreken van een correcte operator, maar er kan nog veel meer mis gaan en dat zullen we moeten afvangen. Dan kan het ook netjes zijn om een wat uitgebreidere tekst toe te voegen over hoe het programma correct te gebruiken.

De code is redelijk leesbaar zo, maar een commando heeft nog wat toelichting nodig:

ldrb w7, [x6], 1 is een samenstelling van ldr dat we al eerder zagen, maar nu wordt 1 byte gelezen, vandaar de b erachter. Maar de opdracht doet nog meer, de ,1 op het eind betekent dat x6 met 1 verhoogd wordt na het inlezen van de byte op adres x6.

Wie mee wil doen met #klooienmetcomputers kan dat doen via GitHub. Maak een account op github.com en zoek naar Abmvk/kmc. Het account Abmvk volgen kan ook. Lezers zijn vrij te gebruiken wat ze willen en om zelf zaken toe te voegen of aan te passen, vragen te stellen of commentaar te leveren.

Arnout van Kempen di CCO CISA is directeur compliance & risk bij aaff, de fusieorganisatie van Alfa en ABAB. Hij schrijft op persoonlijke titel.

Gerelateerd

reacties

Reageer op dit artikel

Spelregels debat

    Aanmelden nieuwsbrief

    Ontvang elke werkdag (maandag t/m vrijdag) de laatste nieuwsberichten, opinies en artikelen in uw mailbox.

    Bent u NBA-lid? Dan kunt u zich ook aanmelden via uw ledenprofiel op MijnNBA.nl.