Redesigning Assembler Language Development.

Don V Nielsen, 19 mei 2000

Opmerking:
Bixoft heeft "Redesigning Assembler Language Development" omgezet in een web-document, met toestemming van de auteur om deze versie te publiceren. Tevens hebben wij een aantal kleine wijzigingen aangebracht. Desalniettemin blijven de copyrights voor het document hieronder volledig berusten bij de heer Nielsen.
Alle code fragementen in dit document zijn niet meer dan voorbeelden. Er wordt op geen enkele wijze gegarandeerd dat deze vrij zijn van fouten en/of omissies. U mag (delen van) de code fragmenten gebruiken, maar het blijft in alle aspecten uw eigen verantwoordelijkheid om uw programma's te testen.

Contact:
Dhr. Nielsen ontvangt graag uw commentaar, opmerkingen, etc. e-mail Don Nielsen. Wel graag in het engels.
Als u liever reageert in het nederlands, e-mail Bixoft.

Gezien de technische aard van het onderstaande document hebben wij ervoor gekozen de originele tekst niet te vertalen in het nederlands. Derhalve gaan wij nu verder in het engels.

Dit document bevat de volgende hoofdstukken:

Introductie.

Het doel van dit document is het belichten van codeermethoden en -technieken in High Levek Assembler. HLASM heeft zich ontwikkeld tot een veel krachtiger assembler dan veel mensen zich realiseren. Kennis van deze kracht en begrip voor de wijze waarop deze kracht kan worden aangewend kan je vele uren werk besparen bij het coderen en ontwikkelen, en ook bij later onderhoud.

Programmeer-concepten, zoals abstractie, hergebruik, en kwalificatie kunnen worden toegepast in assembler - net als in hogere programmeertalen zoals C, Cobol, en Basic. Het gebruik van copybooks wordt aangemoedigd. Nieuwe methoden om copybooks toe te passen middels het USING statement zullen worden geïntroduceerd. Tevens zal worden uitgelegd waarom DSECTs in het begin van het programma dienen te worden opgenomen.

We gebruiken twee eenvoudige applicaties om te laten zien hoe met de nieuwe concepten gewerkt kan worden. De eerste applicatie is een eenvoudig tel-programma dat records leest uit een dataset, om daarin opgeslagen staat-codes op te slaan in een tabel en ze te tellen. De applicatie toont de nieuwe codeer-technieken voor het bouwen van tabellen. Het toont tevens hoe deze technieken handmatige wijzigingen verminderen of overbodig maken wanneer element-definities in de tabel wijzigen.

De tweede applicatie is ook een tel-programma. Deze applicatie gebruikt geen tabel, maar een dataset die gesorteerd is op staat-code. Het telt aantallen staat-codes door de staat-code van elk gelezen record te vergelijken met die van het voorgaande record. Deze applicatie toont hoe een DSECT meerdere geheugen-gebieden gelijktijdig kan bestrijken.

In eerste instantie lijkt het wellicht veel werk om deze technieken te leren. Implementatie lijkt ook extra typewerk met zich mee te brengen. Maar als je wat meer ervaring opbouwt en beter vertrouwt raakt met deze technieken, dan gaat het min of meer vanzelf. Je programma's worden beter leesbaar en de benodigde onderhouds-inspanningen nemen geweldig af. In sommige gevallen kan onderhoud zelfs gereduceerd worden tot her-assemblage van de bestaande source.

De voorbeeld-programma's in Appendix A en Appendix B gebruiken de HLASM toolkit macro's. Deze macro's implementeren programmeer-structuren die ook in andere talen voorkomen, zoals IF/ELSE/ENDIF, DO lussen, etcetera. De macro's vereenvoudigen het coderen en verbeteren de leesbaarheid van het programma.

Bixoft opmerking:
In plaats van de toolkit kunenn ook de macro's uit Bixoft's eXtended Assembly language gebruikt worden voor structuur en nog betere leesbaarheid.

Achtergrond: Terminologie.

Abstractie.

De dikke van Dale definieert abstraheren als: "ontdoen van het toevallige, het concrete." Het toepassen van de term abstractie op het programmeren houdt in, dat de programmeur bevrijdt wordt van de onderliggende details van het proces. Het aanroepen van een functie is een voorbeeld van abstractie. Een functie wordt aangeroepen om een taak uit te voeren en een resultaat terug te geven. We hoeven niet te weten hoe de functie zijn taak uitvoert. We vertrouwen er gewoonweg op, dat het zijn taak correct zal uitvoeren, en het juiste resultaat zal terug geven.

Dit vertrouwen is het moeilijkste onderdeel voor ons assembler programmeurs. De meeste van ons willen alle details weten. Echter, het toepassen van abstractie vereenvoudigt ons werk. Dit vereist vertrouwen dat de zaken zijn zoals zij nu eenmaal zijn, en dat het niet nodig is dat we weten waarom ze zo zijn. Het kost vaak moeite om dit te accepteren. Bedenk echter dat dit concept allang wordt toegepast op de diverse systeem-routines.

Hergebruik.

Hergebruik is het concept om in het verleden voltooid werk toe te passen om ontwikkel-tijd en -moeite te besparen. Een aangeroepen module is een voorbeeld van hergebruik. Een macro is een ander voorbeeld. Wanneer een standaard set logica kan worden gecodeerd als een aanroepbare module of een macro, dan kunnen anderen in de toekomst die logica toepassen zonder het opnieuw te hoeven ontwikkele (het wiel opnieuw uitvinden, bij wijze van spreken). Nog een andere vorm van hergebruiken is het copy-member. Het copy-member is een definitie van de een of andere structuur. Anderen hoeven geen tijd en moeite te verspillen om een structuur te coderen wanneer die structuur al beschikbaar is als een copy-member - of een macro.

Afleiding.

Afleiding is ontvangen of verkrijgen van iets vanuit een bron. Bij het definiëren van een veld kunnen we attributen van het veld afleiden van een eerder gedefinieerd veld. Bij voorbeeld: een programma moet een postcode uit een input record moet onthouden. Het programma gebruikt een macro of copy-member waarin de attributen van een postcode-veld al zijn vastgelegd. De attributen van de bestaande veld-definitie kunnen gebruikt worden om de attributen van het werk-veld voor de postcode af te leiden.

Bouwstenen.

Het Veld.

Het veld vertegenwoordigt de simpelste structuur. Het representeert een gegeven. Voor de assembler is een veld een aantal bytes in het geheugen, en niet veel meer. Sommigen vinden het data-type belangrijk, maar voor de assembler is het veldtype - packed, hexadecimaal, of tekst - voor het gemak van de programmeur. Het belangrijkgste attribuut van een veld is de hoeveelheid geheugen die het in beslag neemt.

De indeling van een record of tabel-regel.

Alle records en alle tabel-regels kunnen beschreven worden middels een indeling of lay-out. De indeling is een structuur die de gegevens beschrijft. Het definieert de plaats en de lengte van elk veld en het definieert de structuur. De lengte van de indeling is de cumulatieve lengte van alle velden plus de opvulling die hier en daar wellicht nodig is voor de juiste alignment. Records en tabel-regels zijn functioneel equivalent.

Codeer altijd de indeling voor tabel-regels en records. Dit is de eerste en allerbelangrijkste stap bij het schrijven van een programma. Het coderen van de indelingen vereenvoudigt het ontwikkelen en het inderhouden van de programmatuur. Wanneer een indeling gedeeld wordt door een aantal programma's, sla de definitie dan op als een macro of een copy-member. Dit vermindert programmeertijd en -inspanning in toekomstige projecten.

De tabel.

Een tabel is een geheugen gebied waarin een serie gerelateerde gegevens wordt opgeslagen. Een tabel bevat een of meer regels, die alle dezelfde indeling hebben. Een twee-dimensionale tabel kan men zich voorstellen als een stuk gelinieerd papier: het papier fungeert als tabel, elke regel bevat een tabel-regel.

Tabellen hebben attributen, net als velden. Een tabel heeft een begin-adres, dat is het adres van de eerste byte van de eerste tabel-regel. Het heeft een eind-adres, dat is de laatste byte van de laatste tabel-regel. De grootte van de tabel kan gedeeld worden door de lengte van een tabel-regel, het resultaat is het aantal regels dat de tabel kan bevatten.

We gebruiken een eenvoudige applicatie voor volkstelling om de besproken concepten te demonstreren. De tabel bevat twee velden: een staat-code en een telling. Later zullen we tellingen naar geslacht toevoegen aan de regel-indeling. Dit voorbeeld zal laten zien hoe een tabel en programma zodanig gecodeerd kunnen worden, dat een verandering in de indeling van de tabel-regel kan worden doorgevoerd zonder dat het programma gewijzigd behoeft te worden.

Stap 1, Beschrijf de gegevens.

De eerste stap bij het ontwikkelen van het programma is het vastleggen van de record-indeling van de input dataset. Het programma leest de input data vanuit de DDname INFILE. Het gebruikt een DCB met dezelfde naam om de gegevens uit het bestand te lezen. De records worden bij het lezen rechtstreeks geplaatst in een geheugengebied dat is beschreven door het copy-member CMS0001A. Het copy-member bevat alle veld-definities en is geïmplementeerd met een DSECT. De basis structuren (record-indeling en DCB) zijn als volgt gecodeerd:

         ...
INPUT_LAYOUT DSECT
         COPY  CMS0001A                * Standaard record-indeling
L_INPUT_LAYOUT EQU *-INPUT_LAYOUT      * Bepaal record lengte
         ...
INREC    DS    CL(L_INPUT_LAYOUT)      * Reserveer geheugen voor
                                         record
         ...
INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOFINFIL
         ...

Wanneer we een macro gebruiken in plaats van een copy-member (of een macro die een copy-member gebruikt), dan zou het voorbeeld hierboven er als volgt uit komen te zien:

         ...
INPUT_LAYOUT CMS0001A DSECT=YES        * Indeling en lengte input
         ...
INREC    DS    CL(L_INPUT_LAYOUT)      * Reserveer geheugen voor
                                         record
         ...
INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOFINFIL
         ...

De concepten van hergebruik, abstractie, en afleiding zijn alle toegepast in het voorbeeld hierboven. Het gebruik van macro of copy-member CMS0001A is een voorbeeld van hergebruik en abstractie. Allereerst wordt een bestaande indeling hergebruikt, in plaats van een nieuwe te ontwikkelen. Probeer altijd een bestaande macro of copy-member to gebruiken wanneer er een beschikbaar is. De voordelen van het gebruik van een macro of copy-member zijn:

  1. een macro of copy-member dwingt het gebruik van gestandaardiseerde veldnamen af voor alle programma's die de macro of het copy-member gebruiken
  2. er bestaat slechts een enkele definitie van de indeling
  3. een wijziging in de indeling hoeft uitsluitend in de macro of het copy-member aangebracht te worden, niet in alle programma's die er mee werken
  4. de tijd en moeite om de indeling te coderen voor elk programma wordt geëlimineerd

Het gebruik van een macro of cop-member past ook abstractie toe. De macro of het copy-member verbergt de details van de record-indeling. Ja, het is belangrijk om de record-indeling te kennen waarmee je werkt. Gedetailleerde kennis van de structuur van de indeling is echter maar zelden nodig. Abstractie is ook toegepast door de definitie van L_INPUT_LAYOUT. Dit statement gebruikt de assembler om de lengte van de gedefinieerde indeling te berekenen. Het is abstract omdat je de lengte niet hoeft te weten; je vertrouwt erop, dat de lengte voor je uitgerekend wordt.

Afleiding is gebruikt bij de definitie van het INREC veld. De assembler bepaalt de lengte van INREC aan de hand van de berekende record lengte, L_INPUT_LAYOUT. Dit is ook een voorbeeld hoe je een veldlengte impliciet kunt definiëren. Met HLASM is het mogelijk het lengte-attribuut van een veld over te laten nemen van een eerder gedefinieerd veld. HLASM kan dan geheugen reserveren voor het veld op basis van de lengte van het bestaande veld.

HLASM staat het toe een veldlengte expliciet of impliciet te definiëren. Bij een expliciete definitie wordt de lengte van het veld exact gecodeerd. De definitie van INREC hierboven bij voorbeeld, had ook expliciet gecodeerd kunnen worden als "INREC DS CL200". Dit is een simpeler methode om INREC te definiëren. Er zjn echter twee redenen om het gebruik van expliciete lengtes te af te raden:

  1. het vereist dat je van te voren exact weet hoe groot de indeling wordt
  2. de lengte van de indeling kan in de toekomst veranderen

Om geheugen expliciet te kunnen toewijzen moet je van te voren weten hoeveel ruimte nodig zal zijn. Je zou de macro of het copy-member moeten raadlpegen, de totale lengte bepalen, en dan die lengte coderen in de definitie. Bovendien kost het dan veel moeite om in alle programma's de lengtes aan te passen wanneer de lengte van de indeling wijzigt. Alle programma's die de indeling gebruiken moeten nagelopen worden om de expliciete geheugen-toewijzing aan te passen.

Een impliciete definitie houdt in dat een symbool wordt gebruikt om de lengte van het veld te definiëren. L_INPUT_LAYOUT is een voorbeeld van een impliciete lengte, omdat het symbool een representatie is voor de benodigde lengte. L_INPUT_LAYOUT is de lengte van CMS0001A zoals die door de assembler is berekend. Brede toepassing van dit concept kan benodigde onderhouds-inspanninge reduceren van uren tot minuten. Wanneer de feitelijke lengte van CMS0001A verandert, dan zal de assembler de nieuwe lengte berekenen, de berekende lengte toekennen aan het symbool L_INPUT_LAYOUT, wekle vervolgens weer gebruikt wordt om de lengte van INREC te bepalen. Zodoende leidt een wijziging van de macro of het copy-member er toe, dat ook de programma's - automatisch - worden aangepast.

Census Tabel toevoegen.

         ...
INPUT_LAYOUT CMS0001A DSECT=YES        * Indeling en lengte v/d input
         ...
TABLE_ENTRY DSECT
STATE    DS    CL(L'CMSTATE)           * Staat-code
TOTAL    DS    F                       * Totaal aantal
L_TABLE_ENTRY EQU *-TABLE_ENTRY        * Bereken lengte tabel-regel
         ...
MAX_ENTRIES EQU 100                    * Maximum tabel-regels
         ...
INREC    DS    CL(L_INPUT_LAYOUT)      * Geheugen voor record
...
INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOFINFIL
         ...
* Definieer geheugen voor tabel
STATE_CENSUS_TABLE DC (MAX_ENTRIES)XL(L_TABLE_ENTRY)'00'
E_STATE_CENSUS_TABLE EQU *             * Einde tabel
         ...

Twee structuren en een constante zijn toegevoegd aan ons voorbeeld. De TABLE_ENTRY structuur beschrijft de velden die gebruikt worden in elke tabel-regel. Het eerste veld, STATE, bevat de staat-code. Dit is de sleutel voor het zoeken in de tabel. Merk op dat de definitie van STATE een tweede voorbeeld is van afleiding. STATE leidt zijn lengte af van CMSTATE. Je hoeft niet te weten hoe lang CMSTATE is. Je kunt er op vertrouwen dat STATE wordt gedefinieerd met dezelfde lengte.

Het tweede veld, TOTAL, is het teller-veld. Het wordt gedefinieerd als F, een 32-bits binair veld (32-bit signed binary integer). Bij het verwerken van elk input record wordt de staat-code gezocht in de tabel. Als de code niet gevonden wordt, dan wordt zij toegevoegd. Het telveld TOTAL in de betreffedne tabel-regel wordt vervolgens opgehoogd.

De assembler moet L_TABEL_ENTRY berekenen. Dit is een vereiste omdat de lengte van STATE afgeleid is. Je weet niet van te voren, zonder het na te zoeken, wat de lengte van CMSTATE is. Dus vertrouwen we erop dat de assembler de lengte van STATE correct afleidt van de feitelijke lengte van CMSTATE. Beide velden worden zodoende even lang. L_TABLE_ENTRY wordt dan gelijk aan L'STATE+L'TOTAL plus eventueel benodigde ruimte voor alignment. Nogmaals: de feitelijke lengte doet niet ter zake. Het is belangrijk je te realiseren, dat de velden een bepaalde lengte hebben, en dat die lengte correct bepaald wordt door de assembler.

MAX_ENTRIES is een constante die het maximale aantal regels in de tabel weergeeft. Het wrodt gebruikt in de definitie van de tabel STATE_CENSUS_TABLE. De grootte van de tabel wordt bepaald door deze constante. Zoals eerder al aangegeven is een tabel de geheugenruimte die door alle tabel-regels gezamenlijk in beslag kan worden genomen. De grootte van de tabel is dus gelijk aan lengte van een tabel-regel maal het maximale aantal tabel-regels. De definitie van STATE_CENSUS_TABLE is een voorbeeld van deze berekening. Door MAX_ENTRIES en L_TABLE_ENTRY te gebruiken om de attributen van de tabel impliciet aan te geven, kan de assembler de benodigde hoeveelheid geheugen berekenen, en die hoeveelheid toewijzen aan het symbool STATE_CENSUS_TABLE.

De werkelijke kracht van deze impliciete definitie wordt gerealiseerd wanneer de tabel onderhoud behoeft. Als STATE_CENSUS_TABLE meer regels moet kunnen bevatten, dan hoef je alleen maar de waarde van MAX_ENTRIES aan te passen. Sterker nog, wanneer de indeling van de tabel-regels verandert, dan zal de assembler de nieuwe lengte van de tabel-regel gebruiken om de grootte van STATE_CENSUS_TABLE te bepalen. Dit zullen we zien, als we straks telvelden naar geslacht gaan toevoegen aan de tabel-regels.

Stap 2, Bouw het programma.

Velden benaderen in record en tabel.

         ...
         USING INPUT_LAYOUT,INREC      * Beschrijf velden in INREC
         USING TABLE_ENTRY,R3          * Beschrijf tabel-regel
         ...
FILELOOP GET   INFILE,INREC            * Lees een record
         LA    R3,STATE_CENSUS_TABLE   * Wijs naar begin tabel
TBLLOOP  C     R3,=A(E_STATE_CENSUS_TABLE) * Einde tabel?
         BNL   ERRTABLE                * Ja: foutafhandeling
         CLI   STATE,X'00'             * Staat nul = lege regel
         BE    ADDSTATE                * Niet gevonden: voeg staat toe
         CLC   CMSTATE,STATE           * Staat-code komt overeen?
         BE    STFOUND                 * Ja: gevonden
         LA    R3,L_TABLE_ENTRY(R3)    * Volgende tabel-regel
         B     TBLLOOP                 * Ga die ook controleren
ADDSTATE MVC   STATE,CMSTATE           * Plaats staat in lege regel
STFOUND  LA    R1,1                    * Neem de waarde 1
         A     R1,TOTAL                * Tel oud staat-totaal bij
         ST    R1,TOTAL                * Wordt nieuw staat-totaal
         B     FILELOOP                * En verwerk volgende record
         ...

Nadat een input record gelezen is, moeten we de overeenkomstige staat-code lokaliseren in de tabel. Maar voordat we de gegevens kunnen benaderen, moeten zowel het input record als de tabel-regels beschreven worden. Dit doen we middels het USTING statement. USING is niet een instructie die de processor uitvoert zoals een vergelijking of een sprong-instructie. Het is een directief dat de assembler gebruikt om te bepalen waar een beschreven geheugen-gebied zich bevindt. We gebruiken DSECTs om geheugen-gebieden te beschrijven. Een DSECT kan gebruikt worden om geheugen te beschrijven dat is toegewezen aan een symbool, of aan een register. Beide vormen worden gebruikt in het voorbeeld.

USING INPUT_LAYOUT,INREC wijst de CMS0001A record-indeling toe aan het geheugengebied dat is gedefinieerd als INREC. Wanneer een veld uit het CMS0001A copy-member wordt gebruikt in een instructie, dan worden de benodigde gegevens uit het INREC-gebied bewerkt. Wanneer CMSTATE wordt gebruikt in het programma, dan is positie 147 van INREC (oftewel INREC+146) de lokatie waar de gegevens te vinden zijn.

USING TABLE_ENTRY,R3 wijst de regel-indeling voor TABLE_ENTRY toe aan het geheugen waar register 3 naar toe wijst. Wanneer een veld uit de TABLE_ENTRY indeling wordt gebruikt, dan bevinden de gegevens zich in geheugen waar register 3 naar toe wijst. STATE bevindt zich op positie 1 van de tabel-regel. Wanneer STATE gebruikt wordt in het programma, dan zal de eerste byte die door R3 wordt aangewezen [oftewel 0(R3)] de staat-code van die tabel-regel bevatten.

De programmeerlus is als volgt opgebouwd:

Laten we nog eens een paar belangrijke punten nalopen. In de eerste plaats de relatie tussen INPUT_LAYUOT en INREC. INREC was gedefinieerd met de berekende lengte van de input record-indeling, oftewel L_INPUT_LAYOUT. Hoewel INREC gedefinieerd is met de juiste hoeveelheid geheugen, bevat het geen velden. Referenties aan velden in INREC worden toegewezen middels het USING INPUT_LAYOUT,INREC statement. Zodoende wordt geheugen uit INREC benaderd wanneer een veld uit INPUT_LAYOUT wordt gebruikt.

Het symbool L_TABLE_ENTRY wordt gebruikt om R3, de tabel-regel pointer, te verzetten. De assembler gebruikt de berekende lengte. Een verandering in de lengte van de tabel-regels zal geen invloed hebben op deze code.

Tellers naar geslacht toevoegen aan de tabel.

         ...
INPUT_LAYOUT CMS0001A DSECT=YES        * Indeling en lengte input
         ...
TABLE_ENTRY DSECT
STATE    DS    CL(L'CMSTATE)           * Staat-code
TOTAL    DS    F                       * Totaal aantal
FEMALE   DS    F                       * Aantal vrouwen
MALE     DS    F                       * Aantal mannen
UNKNOWN  DS    F                       * Geslacht onbekend
L_TABLE_ENTRY EQU *-TABLE_ENTRY        * Lengte tabel-regel
         ...
MAX_ENTRIES EQU 100                    * Max. regels in tabel
         ...
INREC    DS    CL(L_INPUT_LAYOUT)      * Definieer input buffer
         ...
INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOFINFIL
         ...
* Definieer geheugen voor tabel
STATE_CENSUS_TABLE DC (MAX_ENTRIES)XL(L_TABLE_ENTRY)'00'
E_STATE_CENSUS_TABLE EQU *             * Einde tabel
         ...

Nu dat het basis-programma ontwikkeld is, zullen we nieuwe tellers toevoegen aan de tabel. Bij het toevoegen van deze tellers zullen we besparingen in tijd en moeite realiseren die deze programmeer-technieken mogelijk maken.

De enige verandering in het gehele programma is de toevoeging van de velden FEMALE, MALE, en UNKNOWN. Dat is alles. Het programma kan opnieuw geassembleerd worden zonder verdere aanpassingen.

Laten we bekijken wat er gebeurd is en hoe de assembler omgaat met de veranderingen. Drie velden zijn toegevoegd aan TABLE_ENTRY. Deze velden hebben de tabel-regel 12 bytes langer gemaakt. De berekende lengte L_TABLE_ENTRY wordt in het gehele programma gebruikt. De assembler bepaalt het benodigde geheugen voor de tabel aan de hand van de impliciete waarden van L_TABLE_ENTRY en MAX_ENTRIES, en kent die hoeveelheid geheugen toe aan STATE_CENSUS_TABLE.

Er zijn geen wijzigingen nodig in de programma-code. De statements die door de wijziging beïnvloed worden zijn de volgende:

Opmerking:
Als de grootte van CMSTATE gewijzigd wordt in de CMS0001A macro of het copy-member, dan kan dit programma opnieuwe geassembleerd worden, foutloos, zonder wat voor aanpassing dan ook.

Dezelfde indeling tweemaal gebruiken.

Veel programma's moeten de inhoud van twee records met elkaar vergelijken. In die gevallen hebben we dus twee exemplaren van dezelfde structuur. In het verleden moesten we dan altijd twee varianten coderen van dezelfde indeling met veldnamen die lichtelijk verschillen, bij voorbeeld ZIP1 an ZIP2.

HLASM heeft een voorziening genaamd "Labelled Using" waarmee dit probleem wordt opgelost. Een gelabelde using staat ons toe om een DSECT op meerdere plaatsen gelijktijdig van toepassing te verklaren. Die plaatsen kunnen aangewezen worden door een register, of zij kunnen toegewezen zijn aan een symbool in het programma.

Om de kracht hiervan aan te tonen zullen we een nieuw tel-programma ontwikkelen. We zullen aannemen dat de input dataset gesorteerd is op staat-code. Het programma leest de records en verhoogt een teller. Wanneer de staat-code wijzigt bevat de teller het aantal records met de verwerkte staat-code

Om dit te bereiken zal het programma een record lezen en de staat-code van het gelezen record vergelijken met de staat-code van het voorgaande record. Als er een wijziging optreedt, dan wordt de teller getoond. Elk gelezen record wordt gekopieerd naar het geheugen dat is gereserveerd voor het "voorgaande record".

         ...
COUNTER  DC    PL4'1'                  * Staat teller
PREVREC  DS    CL(L_INPUT_LAYOUT)      * Voorgaande record
         ...
INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GL,EODAD=EOFINFIL
         ...

De code hierboven definieert een gebied om het voorgaande verwerkte record te bewaren. Nadat een record verwerkt is, wordt het gekopieerd naar PREVREC. Bovendien is de MACRF-parameter op de DCB-macro gewijzigd van GM in GL. Het resultaat hiervan is, dat een record bij het lezen niet gekopieerd hoeft te worden naar een in het programma toegewezen geheugengebied. Het door het systeem gelezen record wordt beschouwd als het actuele record, maar dat record blijft wel in de buffer staan, waar het middels R1 aangewezen kan worden.

Opmerking:
Als het programma gebruik maakt van system services, anders dan de GET macro, of als het een subprogramma aanroept tijdens de verwerking van het record, dan wordt R1 beschouwd als een vluchtig register. Het kan dan niet gebruikt worden om gegevens of een pointer te vast te houden gedurende de uitvoering van die system service of dat subprogramma. In een echt programma dient R1 uitsluitend als tijdelijk of werk-register gebruikt te worden.

In deze applicatie wordt geen tabel gedefinieerd omdat er niets getabuleerd wordt. Het programma gebruikt een staat-wissel om de staat-telling weer te geven.

         ...
PRV      USING INPUT_LAYOUT,INREC      * Benoem velden in INREC
CUR      USING INPUT_LAYOUT,R1         * Benoem velden in input record
         ...
FILELOOP GET   INFILE                  * Lees een record
         CLC   CUR.CMSTATE,PRV.CMSTATE * Staat-wissel?
         BNE   CHGSTATE                * Ja: handel wisseling af
* Save current record
         MVC   INREC,CUR.INPUT_LAYOUT  * of codeer "INREC,0(R1)"
         AP    COUNTER,P'1'            * Verhoog teller
         B     FILELOOP                * en verwerk volgende record
         ...

De USING statements zijn voorzien van labels PRV en CUR. De USING met label PRV gebruikt INPUT_LAYOUT om de velden in PREVREC te benoemen. De USING met label CUR gebruikt INPUT_LAYOUT om de velden van het record in zijn buffer te benoemen, dit betreft geheugen op locatie 0(R1). De USING labels worden gebruikt om de veldnamen te kwalificeren, zodat de assembler begrijpt welke versie van het veld bedoeld wordt. PRV.CMSTATE verwijst naar de locatie van CMSTATE in PREVREC, oftewel PREVREC+146. CUR.CMSTATE verwijst naar de locatie van CMSTATE in de record buffer, oftewel 146(R1).

DSECTs opnemen aan het begin van het programma.

Waarom DSECTs aan het begin van het programma plaatsen?

De assembler verwerkt de source code in twee verwerkingsgangen. In de eerste verwerkingsgang van de source code identificeert de assembler alle symbolen en probeert het alle attributen van die symbolen vast te stellen. Sommige attributen kunnen echter niet direct worden bepaald omdat ze niet op een sequentieel kunnen worden vastgesteld. In deze gevallen worden die attributen pas in de tweede verwerkingsgang vastgesteld.

Identificatie van symbolen omvat het lezen van copy-members, en het expanderen van macro's. Macro's zijn enkelvoudige statements die code voor je genereren. DCB, OPEN, en GET zijn voorbeelden van macro's. Macro's voor gestructureerd programmeren uit de HLASM toolkit en uit Bixoft's eXtended Assembly language zoals IF/ELSE/ENDIF en DO/END zijn andere voorbeelden. Sommige macro's vereisen dat de attributen van alle symbolen al tijdens de eerste verwerkingsgang bekend zijn. Zonder dat kan de assembler foutboodschappen afgeven omdat het b.v. veldlengtes niet kan vaststellen.

De assembler verwerkt de source volgordelijk. Door DSECTs en macro's die copy-members aanroepen op te nemen an het begin van het programma, worden de symbolen al vroeg in de verwerking vastgesteld zodat macro's daarna probleemloos geëxpandeerd kunnen worden.

Persoonlijk geef ik er de voorkeur aan om alle DSECTs direct in het begin van het programma op te nemen. Dit komt overeen met het gebruik in andere talen zoals PASCAL, C, COBOL, en BASIC. Het voorziet in een vaste plek voor alle structuren en verschaft de programmeur de benodigde informatie betreffende de gegevens die het programma moet verwerken.

Bixoft opmerking:
Wij adviseren om alle DSECTs boven in het programma op te nemen. Dit heeft de volgende voordelen:

Overige benodigde huishoudelijke taken.

Assembler programma's beginnen normaal gesproken met enige huishoudelijke taken (entry macro). Maar als je de DSECTs nog voor de entry macro opneemt, dan kan dat assemblage-fouten veroorzaken. Om deze fouten te voorkomen dien je de huishouding aan het begin van het programma lichtelijk aan te passen. Programma's dienen dan als volgt opgebouwd te worden:

ProgName CSECT
*
*        DSECTs en gebieds-indelingen komen hier
*
ProgName "Huishoud-Macro of entree-logica voor de CSECT"
         ...

Het CSECT statement aan het begin van het programma voorkomt dat de assemblage fouten optreden.

Bixoft opmerking:
Met Bixoft's eXtended Assembly language wordt de PGM macro gebruikt om zowel de entree logica als alle benodigde indelingen te genereren. Bij voorbeeld:

ProgName PGM   VERSION=V00R00M00,      * Versie nummer
                                       *
               HDRTXT='Sample program',
                                       *
               SAVES=2,                * Interne save-areas
                                       *
               MAPS=(CMS0001A,         * Eigen indelingen
                                       *
               DCB,DCBE,DECB,IOB)      * Standaard IBM gebieden

Conclusie.

Zoals dit document aangeeft kunnem programmeer-concepten zoals abstractie, hergebruik, en afleiding gebruikt worden in assembler, net als in ander programmeer-talen zoals PASCAL, C en BASIC. Abstractie kan gebruikt worden om details van structuren te af te schermen, die anders het overzicht op het programma maar in de weg zouden staan. Hergebruik stelt ons in staat het werk van anderen efficiënt en snel toe te passen. Middels afleiding ten slotte kunnen we attributen van bestaande velden gebruiken wanneer we een overeenkomstig veld definiëren. Al deze concepten geven ons de mogelijkheid om HLASM het zware werk voor ons te laten opknappen, zodat we dat zelf niet meer hoeven te doen.

Het voorbeeld toont hoe abstractie en hergebruik kunnen worden toegepast bij het gebruik van macro's en copy-members. Macro's en copy-members standaardiseren onze programma's. Het bevordert het begrijpen van de programma-code, doordat veld-namen consistent zijn over de programma's heen. Macro's of copy-members vormen ook de basis van waaruit we overeenkomstige gebieden en velden kunnen definiëren. We kunnen bij voorbeeld de definitie van CMSTATE gebruiken om een tabel-regel te definiëren, zodanig dat beide velden dezelfde eigenschappen hebben.

Het voorbeeld-programma laat ook zien dat breed gebruik van deze technieken onze toekomstige onderhouds-inspanningen dramatisch kan verminderen. In sommige gevallen kan het onderhoud zelfs tot nul gereduceerd worden wanneer een veld of record-indeling moet worden aangepast.

Ik hoop dat je belangstelling is gewekt voor de technieken die hier besproken zijn. Het gebruik van deze technieken zal je in de toekomst eel tijd en inspanning besparen. Deze technieken reduceren de impact van wijzigingen op het hele module.

Appendix A: Voorbeeld programma met tabel-gebruik.

SAMPLE01 CSECT
INPUT_LAYOUT DSECT
         COPY  CMS0001A                * Standaard record-indeling
L_INPUT_LAYOUT EQU *-INPUT_LAYOUT      * Bepaal lengte record

TABLE_ENTRY DSECT
STATE    DS    CL(L'CMSTATE)           * Staat afkorting
TOTAL    DS    FL4                     * Totaal aantal
FEMALE   DS    FL4                     * Aantal vrouwen
MALE     DS    FL4                     * Aantal mannen
UNKNOWN  DS    FL4                     * Aantal geslacht onbekend
L_TABLE_ENTRY EQU *-TABLE_ENTRY        * Bepaal lengte tabel-regel

MAX_ENTRIES EQU 100                    * 100 tabel regels

SAMPLE01 ENTERMPG BASE=R12,VER=A,MAIN=N,TEST=Y,DESC='TABLE EXAMPLE'
         USING INPUT_LAYOUT,INREC      * Benoem velden in record
         USING TABLE_ENTRY,R3          * Adresseer tabel-velden

         IF    (CLI,FirstTime,EQ,FTyes) * Alleen de eerste keer:
          MVI  FirstTime,FTno          * Indicator uitzetten
          OPEN (INFILE,INPUT)          * Input dataset openen
         ENDIF ,                       *

MAINLINE EQU   *
         DO    INF                     * Doe voor elk record
READREC   GET  INFILE,INREC            * Lees input record

          LA   R3,STATE_CENSUS_TABLE   * Zoek juiste tabel-regel
          DO   UNTIL=(C,R3,NL,=A(E_STATE_CENSUS_TABLE))

           IF  (CLI,STATE,EQ,X'00')    * Tabelregel is leeg?
            MVC STATE,CMSTATE          * Ja: voeg staat toe
           ENDIF

           IF  (CLC,CMSTATE,EQ,STATE)  * Juiset staat gevonden?
            INCR TOTAL                 * Verhoog totaal-telling
            SELECT CLI,CMGENDER,EQ     * Controleer geslacht:
            WHEN CMGENDER_MALE         * Mannelijk?
             INCR MALE                 * Ja: tel bij de mannen
            WHEN CMGENDER_FEMALE       * Vrouwelijk?
             INCR FEMALE               * Ja: tel bij de vrouwen
            ELSE ,                     * Geslacht onbekend, dus:
             INCR UNKNOWN              * tel bij 'onbekend'
            ENDSEL
            B  READREC                 * Forceer einde zoek-lus
           ENDIF

           LA  R3,L_TABLE_ENTRY(R3)    * Wijs volgende tabel-regel
          ENDDO ,                      * en herhaal...

          WTO  'No more room in table' * Geen vrije regel meer
          ABEND 666                    * User abend 666

         ENDDO ,                       * Einde leeslus

EOF      CLOSE INFILE                  * Sluit input dataset
         LEAVEMPG RC=0                 * Einde programma

         LTORG
FirstTime DC   XL1'00'                 * Eerste-keer indicator
FTYES    EQU   X'00'                   * Ja: eerste keer
FTno     EQU   X'FF'                   * Nee: initialisatie klaar
INREC    DS    CL(L_INPUT_LAYOUT)      * Record buffer

INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOF

* Definieer de tabel
STATE_CENSUS_TABLE DC (MAX_ENTRIES)XL(L_TABLE_ENTRY)'00'
E_STATE_CENSUS_TABLE EQU *             * Definieer einde-tabel addres

         END   SAMPLE01

Appendix B: Voorbeeld programma met wijziging in status.

SAMPLE02 CSECT

INPUT_LAYOUT DSECT
         COPY  CMS0001A                * Standaard record indeling
L_INPUT_LAYOUT EQU *-INPUT_LAYOUT      * Bepaal lengte record

SAMPLE02 ENTERMPG BASE=R12,VER=A,MAIN=N,TEST=Y,DESC='ABBR SAMPLE'
PRV      USING INPUT_LAYOUT,INREC      * Benoem velden vorig record
CUR      USING INPUT_LAYOUT,R3         * Benoem velden actueel record

         IF    (CLI,FirstTime,EQ,FTyes) * Alleen de eerste keer:
          MVI  FirstTime,FTno          * Indicator uitzetten
          OPEN (INFILE,INPUT)          * Input dataset openen
          GET   INFILE                 * Lees eerste record
          LR    R3,R1                  * Wijs naar gelezen record
* Kopieer eerste record om te voorkomen dat er een staat-wissel
* wordt herkend voor het allereerste record.
          MVC   PRV.INPUT_LAYOUT,CUR.INPUT_LAYOUT
         ENDIF ,                       *

MAINLINE EQU   *
         DO    INF                     * Voor elk record
          GET  INFILE                  * Lees een record
          LR   R3,R1                   * Wijs naar actueel record
          IF   (CLC,CUR.CMSTATE,NE,PRV.CMSTATE) * Staat-wissel?
           OI  COUNTER+L'COUNTER-1,X'0F' * Verwijder teken uit telveld
           UNPK WTO01+24(7),COUNTER    * Teller naar bericht
           MVC WTO01+15(L'CMSTATE),PRV.CMSTATE * Voeg staat toe
WTO01      WTO 'STATE: ..   QTY=XXXXXXX ' * Toon staat en teller
           ZAP COUNTER,=P'0'           * Teller terug naar 0
          ENDIF
          AP   COUNTER,=P'1'           * Tel gevonden record
* Het actuele record wordt nu het vorige record
          MVC  PRV.INPUT_LAYOUT,CUR.INPUT_LAYOUT
*         MVC  INREC,0(R3)             * Alternatieve wijze van
                                         coderen
         ENDDO

EOF      CLOSE INFILE                  * Sluit input dataset
         LEAVEMPG RC=0                 * Einde programma

         LTORG
FirstTime DC   XL1'00'                 * Eerste-keer indicator
FTYES    EQU   X'00'                   * Ja: eerste keer
FTno     EQU   X'FF'                   * Nee: initialisatie klaar
COUNTER  DC    PL4'1'                  * Initialiseer teller op 1
INREC    DS    CL(L_INPUT_LAYOUT)      * Definieer 'vorig-record'
                                         buffer

INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOF

         END

© Copyright Don V. Nielsen 2000. All rights reserved.

 

Deze site is aangesloten bij WebRing.
Bekijkt u gerust de lijst van mainframe-gerelateerde sites.
Rennende
    Tyrannosaurus Rex Dino's zijn niet dood. Ze zijn gezond en wel en leven in computer-centra overal om ons heen. Zij spreken in tongen en doen wonderbare magie met computers. Pas op voor de dino! En voor het geval u zit te wachten op het definitieve einde van deze dino's: onthoud dat dino's de wereld 155 miljoen jaren hebben geregeerd!
Dino's en andere anachronismen
[ Aanmelden | Ring Overzicht | Willekeurig | << Vorige | Volgende >> ]
 

Naar Introduction.
Naar Background Terminology.
Naar Building Blocks.
Naar Step 1, Map the Data.
Naar Step 2, Build the Program.
Naar Using the Same Lay-out Twice.
Naar Placing DSECTs at the Top of the Program.
Naar In Conclusion.
Naar Sample Program Using Table.
Naar Sample Program Using Change in State Abbreviation.



Hieronder vindt u het logo van onze sponsor en logos van web-standaarden waaraan deze web-pagina voldoet.