(@AN8 ROM Kernel

Reference Manual: Exec

Commodore Business Machines, Inc.

Amiga ROM Kernel Reference Manual

Exec

Amiga ROM Kernel Reference Manual

Exec

Commodore Business Machines, Inc.

Amiga Technical Reference Series

Addison-Wesley Publishing Company, Inc.

Reading, Massachusetts Menlo Park, California New York Don Mills, Ontario Wokingham, England Amsterdam Bonn Sydney Singapore Tokyo Madrid San Juan

Authors: Carl Sassenrath, Rob Peck, and Susan Deyl Program examples by Carl Sassenrath

Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book and Addison-Wesley was aware of a trademark claim, the designations have been printed in initial caps.

Library of Congress Cataloging-in-Publication Data

Amiga ROM kernel reference manual.

(Amiga technical reference series) Includes index. 1. Amiga (Computer)—Programming. 2. Read-only storage. I.. Commodore Business Machines. QA76.8.A177A655 1986 005.4°46 86-10887 ISBN 0-201-11099-7

COPYRIGHT © 1986 by Commodore Electronics, Ltd.

All rights reserved. No part of this publication may be reproduced, stored, in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior written permission of the publisher. Printed in the United States of America. Published simultaneously in Canada.

DISCLAIMER

COMMODORE-AMIGA, INC., (“COMMODORE”) MAKES NO WARRANTIES, EITHER EXPRESSED OR IMPLIED, WITH RESPECT TO THE PROGRAMS DESCRIBED HEREIN, THEIR QUALITY, PERFORMANCE, MERCHANTABILITY, OR FIT- NESS FOR ANY PARTICULAR PURPOSE. THESE PROGRAMS ARE SOLD “AS IS.” THE ENTIRE RISK AS TO THEIR QUALITY AND PERFORMANCE IS WITH THE BUYER. SHOULD THE PROGRAMS PROVE DEFECTIVE FOLLOWING PURCHASE, THE BUYER (AND NOT THE CREATOR OF THE PROGRAMS, COMMODORE, THEIR DISTRIBUTORS OR THEIR RETAILERS) ASSUMES THE ENTIRE COST OF ALL NECESSARY DAMAGES. IN NO EVENT WILL COMMODORE BE LIABLE FOR DIRECT, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES RESULTING FROM ANY DEFECT IN THE PROGRAMS EVEN IF IT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME LAWS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF IMPLIED WARRANTIES. OR LIABILITIES FOR INCIDENTAL OR CONSE- QUENTIAL DAMAGES, SO THE ABOVE LIMITATION OR EXCLUSION MAY NOT APPLY.

Amiga is a trademark of Commodore-Amiga, Inc.

Printed from camera-ready mechanicals supplied by the authors.

FGHIJ-BA-9898

Sixth Printing, August 1988

PREFACE

System Software Architecture

The Amiga kernel consists of a number of system modules, some of which reside per- manently in the protected kzckstart memory and others that are loaded as needed from the system disk. Figure P-1 illustrates how the various modules interact with one another. At the top of the hierarchy are Workbench and the Command Line Interface (CLI), the user-visible portions of the system. Workbench uses Intuition to produce its displays and AmigaDOS to interact with the filing system. Intuition, in turn, uses the

input device to retrieve its input and the graphics and layers library routines to produce its output.

AmigaDOS controls processes and maintains the filing system and is in turn built on Exec, which manages tasks, task switching, interrupt scheduling, message-passing, I/O, and many other functions.

At. the lowest level of the hierarchy is the Amiga hardware itself. Just above the hardware are the modules that control the hardware directly. Exec controls the 68000, scheduling its time among tasks and maintaining its interrupt vectors, among other things. The trackdisk device is the lowest-level interface to the disk hardware, perform- ing disk-head movement and raw disk I/O. The keyboard and gameport devices handle the keyboard and gameport hardware, queuing up input events for the input device to

process. The audio device, serial device, and parallel device handle their respective hardware. Finally, the routines in the graphics library handle the interface to the graph- ics hardware. |

Programming

The functions of the kernel were designed to be accessed from any language that follows the Amiga’s standard interface conventions. These conventions define the proper nam- ing of symbols, the correct usage of processor registers, and the format of public data structures.

REGISTER CONVENTIONS

All system functions follow a simple set of register conventions. The conventions apply when any system function is called; programmers are encouraged to use the same con- ventions in their own code.

The registers DO, D1, AO, and A1 are always scratch; they are free to be modified at any time. A function may use these registers without first saving their previous contents. The values of all other data and address registers must first be preserved. If any of these registers are used by a function, their contents must be saved and restored appropriately.

If assembly code is used, function parameters may be passed in registers. The conven- tions in the preceding paragraphs apply to this use of registers as well. Parameters passed in DO, D1, AO, or Al may be destroyed. All other registers must be preserved.

If a function returns a result, it is passed back to the caller in DO. If a function returns more than one result, the primary result is returned in DO and all other results are returned by accessing reference parameters.

The A6 register has a special use within the system, and it may not be used as a param- eter to system functions. It is normally used as a pointer to the base of a function vec- tor table. All kernel functions are accessed by jumping to an address relative to this base.

v1

Workbench

AmigaDOS CLI icons/Drawers/

and Utilities Utilities

intuition Windows, Menus, Gadgets, Events

AmigaDOS Processes, File System

Layers Library

Keyboard

Exec Graphics Tasks, Messages see Rendering, Text, oe interrupts, /O ere Geis Devi

Devices |

Keyboard 68000 Processor and Graphics /O Ports

Mouse

Amiga Hardware

Figure P-1: Amiga System Software Modules

Vi

DATA STRUCTURES

The naming, format, and initial values of public data structures must also be consistent The conventions are quite simple and are summarized below.

1. All non-byte fields must be word-aligned. This may require that certain fields be padded with an extra byte.

2. All address pointers should be 32 bits (not 24 bits) in size. The upper byte must never be used for data.

3. Fields that are not defined to contain particular initial values must be initialized to zero. This includes pointer fields

4. All reserved fields must be initiahzed to zero (for future compatibility).

5. Data structures to be accessed by custom hardware must not be allocated on a pro- gram stack.

6. Public data structures (such as a task control structure) must not be allocated on a program stack.

7. When data structures are dynamically allocated, conventions 3 and 4 above can be satisfied by specifying that the structure is to be cleared upon allocation.

OTHER PRACTICES

A few other general programming practices should be noted.

1.

Never use absolute addresses. All hardware registers and special addresses have symbolic names (see the include files and amiga.lzb).

Because this is a multitasking system, programs must never directly modify the pro- cessor exception vectors (including traps) or the processor priority level.

Do not assume that programs can access hardware resources directly. Most hardware is controlled by system software that will not respond well to interference. Shared hardware requires programs to use the proper sharing protocols.

Do not access shared data structures directly without the proper mutual exclusion.

Remember, it is a multitasking system and other tasks may also be accessing the same structures.

vill

5. Most system functions require a particular execution environment. For example, DOS functions can be executed only from within a process; execution from within a task is not sufficient. As another example, most kernel functions can be executed from within tasks, but cannot be executed from within interrupts.

6. The system does not monitor the size of a program stack. Take care that your pro- grams do not cause it to overflow.

7. Tasks always execute in the 68000 processor user mode. Supervisor mode is reserved for interrupts, traps, and task dispatching. Take extreme care if your code executes in supervisor mode. Exceptions while in supervisor mode are deadly.

8. Do not disable interrupts or multitasking for long periods of time.

9. Assembly code functions that return a result do not necessarily affect the processor condition codes. By convention, the caller must test the returned value before act- ing on a condition code. This is usually done with a TST or MOVE instruction. Do not trust the condition codes returned by system functions.

68010 AND 68020 COMPATIBILITY

If you wish your code to be upwardly compatible with the 68010/68020 processors, you must avoid certain instructions and you must not make assumptions about the format of the supervisor stack frame. In particular, the MOVE SR,<ea> instruction is a privileged instruction on the 68010 and 68020. If you want your code to work correctly on all 680x0 processors, you should use the GetCC() function instead (see the Exec library function descriptions in the appendixes to Amiga ROM Kernel Reference Manual: Libraries and Devices).

USING AMIGA EXEC FUNCTIONS

The following guidelines will be helpful when you are trying to determine which func- tions may be run from within a task or from within interrupt code, when to forbid or permit task switching, and when to disable or enable interrupts.

Functions That Tasks Can Perform

Amiga system software distinguishes between tasks and processes. Figure P-1 illustrated this difference. Specifically, the information in a task control block is a subset of the information contained in a process control block. Consequently, any functions that

expect to use process control information will not function correctly if provided with a pointer to a task. Generally speaking, tasks can perform any function that is described in this manual. A task cannot, however, perform any function that is related to AmigaDOS (such as printf, Read, Write, and so on). If you want a task to perform DOS-related functions, you should arrange for the task to send a message to a “process,” which in turn can perform the function (filling a buffer that is passed to the task, for example) and signal that the job has been done. The alternative is to use the DOS func- tion CreateProc() instead of the Exec support function CreateTask() for tasks that you spawn yourself. A process can call all functions, including DOS functions.

More information about tasks can be found in the ‘““Tasks” chapter.

Functions That Interrupt Code Can Perform

/

The following Exec functions can be safely performed during interrupts:

Alert() FindPort() Disable() FindTask() Cause() PutMsg(}

Enable() ReplyMsg()

FindName() Signal()

In addition, if you are manipulating your own list structures during interrupt code, you can also use the following functions:

AddHead() Add Tail() Enqueue() RemHead() RemTail()

General Information about Synchronization

The system functions Enable() and Disable() are provided to enable and disable inter- rupts. The system functions Forbid() and Permit() disallow or allow task switching. You need only determine what you are trying to synchronize with before deciding if you must wrap an Enable()/Disable() pair around a function call, use Forbid()/Permit(), or simply allow the system to interrupt or switch tasks at its whim.

If you are trying to modify a data structure common to two tasks, you must assure that your access to these structures is consistent. One method is to put Forbid()/Permit() around anything that modifies (or reads) that structure. This makes the function atomic; that is, the structure is stable and consistent after each full operation by either task. If you are trying to synchronize with something that might happen as a result of interrupt code (for example, Exec data structures), you put Disable()/Enable() around any of your own operations that might interact with such operations. There are other methods (sending messages, using semaphores, and so on), but they are somewhat more involved.

Note that if you are trying to read the contents of a data structure while it is being changed, it is possible to generate an address error that will be sensed by the 68000, causing an exception. This is caused by reading a pointer that is supposed to point to where the data is located. If the pointer value is no longer valid, it may point to a nonexistent memory location that, when read, causes an exception.

Contents of This Manual

This manual describes the functions of Amiga’s multi-tasking executive (Exec). For infor- mation about the graphics support routines (including text and animation) and the I/O devices, see Amiga ROM Kernel Manual: Libraries and Devices. Also included in that volume are the Workbench, which is an environment for running programs, and the floating point mathematics library.

The discussion of the data structures and routines in this manual. is reinforced through numerous C-language examples. The examples are kept as simple as possible. Whenever possible, each example demonstrates a single function. Where appropriate, there are complete sample programs.

Boldface type is used for the names of functions, data structures, macros, and variables. System header files and other system file names are shown in italics.

In code examples that show data structures and pointers, this book adheres to the fol- lowing naming conventions. For example, the name node refers to an instance of a

Node and In refers to a pointer to a Node.

For more information, see also Amiga Intuition Reference Manual, AmtgaDOS User’s Manual, AmigaDOS Developer’s Manual, and AmigaDOS Technical Reference Manual.

Xl

Contents

aaa ae ate Sie eta ae ade teal acs, Vv System Software Architecture Sagan ep sti neat a aS aa ad Vv MET TINY TN NN as asec Pa cache ria alee ential ene vi

REGISTER CONVENTIONS. ounuicccccccccsccsscccccsccsscscccssscecssccsssssssesestsecececsseserarscecerees vl DATA STRUCTURES saciid csasietecdh Aisle eee aaah vill OTHER PRACTICES vated erases onoatla pal ae eeoih en hake Vill 68010 AND 68020 COMPATIBILITY oun... cccccccccccsscsecesssesssssececssssssssesseseseseees 1x USING AMIGA EXEC FUNCTIONS ounces cccccscscscsccescssesesececscseseseseccesseees 1X Contents of This Mamural once cecssccssssssssccccsssccscccnscececcssssessccsssuecssccssssseeecceseesenees xi

Chapter 1 LISTS AND QUEUES o.oo ccccssessssssessssssecssssseccssseeesssnseessseeeen 1 THEPOC UCL IO eivcisscteestetectscSieshlatccshcrceltictridedecesctasenclseleactiliatenlbeties acca tnedaleccte eleee: 2 DSU OE AC GUC eget sche aise healer nteen ten eee eed 2

INODE STRUCTURE cists ct ates atleast iuseuiaes 2 NODE: INITIALIZATION -scccc eli centectennatatatiiuasttietinciceleinucihan 3 HEADER: S (RUC TURES cnc eile etl eta ieee: 4 HEADER INITIALIZATION onc cccccccccsssescscstsssccscsssecssscesersseserersscecscaes 5 BASU UTC CIOS 3 iereicc sence t eee teks seul tence ecaecaapcs ceattease eeatleslata thors 6 INSERTION AND REMOVAL, 00... ccc cccccccsccccsscssccccssscsesesecssesesesecesessssesesesseceseces 6 SPECIAL CASE INSERTION .......cccccccccssssesscsscscesessossscssssesesscscessesesecccsoserecesesaseseesses 7 SPECIAL CASE REMOVALS sisicssciissiisscics caaidcdediniadcsautaaaleucunties 7 PRIORITIZED INSERTION uuu ccccccccccccccssssecscssscesssscvaccesesssecesscsesesensacesevers 7 SHARCUING BY INAMEG cc oiiekctcic ete eiakitiees teers ane neeaes 8 NSU IVC TOSS tei ec cote ecsest sates cee Sas deca cetcis ceetsearataieeeeh bce tcaria eis iat 8 DOV ls 0S secretes testers ea tee eerie ates ene ec 9 SR Nh Se aaseshetetctec cect tcc a aseacdc sees esd eseasde atin Gutstecshcotiatiel elisa toast 10

ROD DUCE Bi PPS IS recat teat testis a Gereatiass acess dase tind taseacee teased masa 11

UU OGG GOL cicero cetacean Teer eet 12 SCHEDULING iiccaansateicictiedeuiidereiitia iether dala ntsc haaneaie 12 SIGS UA cieeet a caste vensececsceeaaes eo encase ase ica asadea dave eeceua train ccatnteede Se Siacslaaccs 13 TASK OUR UBS che saeces eesti tented te ademas ac sienna Nea ens 14 Maas aas recast tates teeta a claus ght teasdcta hoses deceecseen ae encattt cease 14 © PROG PUR Birch eiicieaieietsictaantacitesaltieoeeiatinearaacieataalaenes usecase ices 14

CA OM aac cate eee eee eee eee 15 iach cs acct tc cet eect hd enc ncesteg sea hat needs deoacseu actos aaa 18

POT TID NINA U LOM eee soeascseshh cee estes arc ga ci aac RT lta 18

BN MNCL Specks cte alice seaweed tae ate eens alee cea 19 PLL OCA TION aicctckesseectte tiene etirscteeetestaciiasiutatens slate a haier a acosntinicnsscacese 20 WAITING FOR A SIGNAL . BassScstlescalsslaedlptiaaeass eeneasessctserdashareidaitasges 21 GENERATING ‘A SIGNAD wins ccscienicedidacewicdticdticadcendel Kaaicinction 22

EC US) OLN cece ccc te eta Rs ae eter cee, 22

xii

DS ON att dt 23 SEVIER Bi iscosccivie sons tzesascsvsnecascacsincpuazapcasudesesicaastcsast ons sash psvenseaaicesisdstansessiccesaeeedasetoned 25 BCS UN OUNS exes chisel arab eaten ee a ea atc tea teeta te dete 25 TOR sce tcetacatscdsesecncstepcses tacts cotn techasstdaasstak cs ceases sans esee eae aa Shasta cadets te cea 26 PLA INDICES tssssoeessicisicecc actactucavvecivsstasicucs tsoucdeiosssdaeantesniaie catasiedan ea daaaean 27 TRAP INS TRUG TIONS sss cssccsscscsstiaciciesstepiccssee Sasesinsesssbsteschlseccscsnessessuassubsessseaietde 27 Chapter 3 MESSAGES AND PORTS ooiicssssssssssssssssssssssssssssssssesssssssssssssee 29 PIN UO GU CU (OU ne s5pcssacasssscecsacessectstasastatszacsecadshececatectbobstetessteadt ciducnctuaptaletibniaassa tame iceatie 29 Be OL GS iecascsses ca ceee Ss Sse etc scee a catecea eee ieeaceas eeSs 30 BE sea dassscesccsce su cdescascsasa cca cave icsessevennasatuussaansaseivaiteeiaucesen alas danas osaiaceuctatiuese 30 CREA EOIN, wi sc saceseGescncteseslseciviivsisa tiene tenes iovcaaeaadcnnsinnias sn vacteatasatdanseornadeceieecesemaies ees 32 DEEL LOIN: sacsccsssecissasssaasStecuseriaiiesesciaasssleavelipe oa igictiadecdetsclessi ces isda it a eaeeae 33 BRING LV OU pnsacaseeisceetsstecnestn basset uned aces yses acnssa vn sagan ccessnansiustdonabdanzacaaesapestal as loameioe 33 WieSS a8 6S sxe puncte eins ia decree aided aa enone ak 33 UE PENG: IMSS Gace cece tessaves ies taeda voce easandaesaasnnsedecaeedaentareteanteetensaetaeavots 34 WAITING FOR: A: MESSAGES sscsivicsscssciccsseiessinccessasstesSevissisasecncaseuesasisncaaneieaaieatecves 39 GETTING A MESSAGE cssgecsessstisasssiccsnsssactsvesscassvenelicheatosavsasssscsbostiondesabiabivebagaversoues 36

BOP E VIN cissstececassctuecsbcccestasceSesetccceicsaasnsastascsesbasavacsusn vast tedsnaescassaceeantb aasesuneaseanteseeees 3” Chapter 4 INPUT (OUTPUT cra tcca accumsan catia relied eleiea 39 MTVU OGG VOM chao ia sss ndasbchcsabctasasestcyestassdaauaisdstdeasbeerectaatececiuesst deisel a ceiatie 39 FREQUESE SUPUCE URC ised siciosn ccc stessdactiascesicitsise coasters castes ectesbuscaidiantlanasccigsiases 40 RGSS UT CUO NS acct toecpsnavcssss cass tscastas tecnica satsa a cicaststaantdtecests cast astaseatsmadeastiudiostesd 43 SAMO ATA: COMMAS sss asesteccsttoatasicterniansedteacubie tantra eanectetiinicis 44 PPO rtf Oca reteeicrcrectscetoreert etecl aa eens ah ccttaune te cea teieteeer aban: 45 PREPARA ION,: crescicsine iaisslectecsecastasenansndncecsaetnee cattle iladeahlaeie: 45 SYNCHRONOUS REQUESTS. ou. scssssescescesnscseees joueeetiedemeteatents 46 ASYNCHRONOUS REQUESTS .u...cssssssssssscssssssscessssssssessssesseesseecessesssseeesees Belseeaees 47 CONCLUSION raicsssecialescrelessisetreisctesetscns accent aleaecentesed tina datneaereieense 48 ati et eee se ea 48 UAT AEC V1 Ces sess ssszszzstucatsis cotsateatas esate ince Sactnsaidd cheat clap askelansaects esrheceamna nee 49 Chapter 0 TIN LE REUP US cisdaatievnstunninaienententeininne sens 51 TOC WOT oasis ac ale ie ses ees at tro cee ateelte te 51 SRQUENCE-OF EVEN US waxiceticcctticctirectictsrdceca nncausiininnncianinnniiedt 52 INTERRUPT PRIOR TS wctctssccscctessistieiiencrricndeciatiiacinbuiiineannens 3 NONMASKABLE INTERRUPT uuu... sscscessscecesecssrsscsecececnssssecscarasseseserassesersensacaes D0

OPV ICING PCCP IU) US ssesssceecseserscds trace sae cosestvasclenss ae tasastaberseta coaenuaeaiadnncemennien 55 DATA;:S FRUC TURE: svcctireaiicuniauiideeneiloneadangn ican aieensess 00 BINVIROINNIEIN Bi cesaiceiicctuntetentalasl cnacedestcants rant aoe aia 26 INTERRUPT PHANDLE HO iiiiintdiadddtatcine aan ee 36 INTERRUP DP SER VERS cccsiicctdanuicennteaunintncncnteranceannnnuniinnd 60

Ol UWAPe PL CCE IDO sess costs iste hotest eclectic ed cecal ania 62 Disabling Interrupts wascscecatiotcaccsictieinticalinnaeeicetenieinaactanccutiien 63

xiv -

Chapter 6 MEMORY ALLOCATION 0. ccssscsscccnnnnnssseececccsennssnsessesee 65

POUTOCUCUION ottscrcheccrciensnctecicinatitiameeranbitnstamiateratomueat: 100 Using Memory Allocation Routines wu... ccccsssssssssssssssssssssssssssssssssssssssssessssen 66 MEMORY ‘REQUIREMENTS scessiscccsesscccescictsstatuscvedsscwasssicsissscsasctsascdastsctwoascecepacssosbec 66 MEMORY HANDLING ROUTINES uuu. cssscsssssssssssessscessscssessccssesessesssesessesens 67 SAMPLE CALLS FOR ALLOCATING SYSTEM MEMORY ..............s000 68 SAMPLE FUNCTION CALLS FOR FREEING SYSTEM ITE IWOOT sesessecctsessasesccuseces cases thasian cae caeeeseds sks 68 ~ ALLOCATING MULTIPLE MEMORY BLOCKS. uc cccsscstssssesssssscseees 69 Memory Allocation and Tasks wo cccscssssssssssssssssssssssssssssessssssssssssssssssssssessssssses 71 MEMORY ALLOCATION AND MULTITASKING uc ccscscscssscesssessssees 72 MANAGING MEMORY WITH ALLOCATE() AND DEAL OC ATE) esis 0 ete cya ee at ea 72 CRG Pter 7 UB AS isssscssssacesstatsstitestieacnasteaadiava cease igcdsdssastacgatiaaiotsains atest ean 75 WV aG: Isa DIP ary § ccisiichsoiannntadendiasnutantaisiaiea eee anette 76 TIOW EO: A CCOSS DPA Yaa ccssceststnsecsaatetiasacxansttrcssnerecaaeenr A carheaeceabiatate a 76 OPENING A: LIBRARY ciieciesnice cd oicesk chested csiecatascialcscauceected Sh ceesseccetinects 76 USING A LIBRARY TO CALL A ROUTINE .Q...cccscssscsssscssssscsessessenss 77 USING A LIBRARY TO REFERENCE DATA .......ccccsssssssescssescsssssssescsesasees 78 CACHING LIBRARY POINTERS. ...... i ccsesssssssscssssessssesesscsssessersssesesessssssessseees 78 CLOSING A DIBRAR Y, ccesicesiccsecttescstcid cessccdrssessce Neotielbcctcsctt hiawsatisaacheatidetiaateh este 79 PAGING “A DEAE Y. we asaiceisitictnicdiceimncaitinieunadderesihsietiadataiaiidlubincnceie 79 MAKING A NEW LIBRARY uu... ccsccscscssessssssecsssssssssesessessssssesssssessessessceesseess 79 MINIMUM SUBSET OF LIBRARY CODE VECTORS cc ccsscscssesseeseees 80 STRUCTURE OF A LIBRARY NODE. uu. scccsssssssssessesessessssessessseesssssseess 81 CHANGING THE CONTENTS OF A LIBRARY 00.0... ecssssssssssescssssesseees 82 Relationship of Libraries to Devices oo..ccssssssssssssssssssssssssssssssssssssssssssseesssssssees 82 Chapter 8 ROM-WACK ouiiicccccssccsssssssssssssssssssessssssssssssesssssssssesesssssssssssseesssssssssssnseessset 83 TO UTOC MCC ION sees cette soca een heaters clcceie dl acca bnata tess 83 SOG HITS CO? WV ACK sacs cstacasescc arises tov ascahacacdass aces ivsisahaadals wa aestastestedanasteieecaeeociaeon 84 Keystrokes, Numbers, and Symbols i... ccssssssscssssssssesssssssssssecessessssssssseesseseseses 84 PROP ISUCR Ht PA MIC scisciecscictassishes pect nce aaidcectaeaseieaeiamactdead eeahas al sdstacdandanss 85 DISDIAY F PAWNCS istisssitalsteteeschest teenies ites sdnacbiesiatassdadttcanncniaiaien 86 FRELAUIVE OSI CLONING ci senses crc etetiie ace da cians cit ci cotadl chee aniieaieimids 87 PDSONICE: POST TNS 5 cssscessssscdeied ssissss tiaistcssycasteatsaaligdandessatiRestaasdestectcctesticasnestsicoess 88 PLCC Tiree CTO ry sacs ates sata hcee ceca reg acacia tectcesvthccseead 89 FXO CULION: COUPON scae.5 scsi tsk acc dccucan ech ch Gea chek Scns hbcasa ocala blstbdcSinstecvees 91 FSC OL GS ees al Selec Sota eae asia deena 91 Returning to Multitasking After a Crash... sssssssssssssssssssssssccsssssssecsssesen 91

Appendix A C EXEC INCLUDE FILES o.oisssmsnssnumnessssusnunusnen A-1

Appendix B OTHER ROUTINES ooicccccccssssssssssssssssssssssesssesssssssesssssssenee B-1 Exec Support Library Debug.lib Functions Amiga.lib Functions AmigaDOS General Information Interchange File Format

Appendix C MEMORY /DISK INFORMATION ues C-1 Software Memory Map Disk Format Information Appendix D LIBRARY BASE OFFSETS. uincccccccsssssssssssssssssssssssssseenen D-1 COM pees erect ta ieee se a are Index-1

Amiga ROM Kernel Reference Manual

Exec

Chapter 1

LISTS AND QUEUES

A thorough understanding of the basic elements of Exec lists and queues is necessary to write programs that deal properly with Exec. Subjects related to lists and queues include the node structure of lists, the linkage and initialization of list structures, and the list support functions and macros. Queues and priority sorted lists, which are

achieved through the use of the list functions applied in a certain order, are also important.

Lists and Queues 1

Introduction

The Amiga system software operates in a highly dynamic environment of control data structures. An early design goal of Exec was to keep the system flexible and open-ended by not creating artificial boundaries on the number of system structures used. Rather than using static sized system tables, Exec uses dynamically created structures that are attached to the system as needed. This concept is central to the design of Exec.

Exec uses lists to maintain its internal database of system structures. Tasks, interrupts, libraries, devices, messages, I/O requests, and all other Exec data structures are support- ed and serviced through the consistent application of Exec’s list mechanism. Lists have a common data structure, and a common set of functions is used for manipulating them. Because all of these structures are treated in a similar manner, only a small number of list handling functions need be supported by Exec.

List Structure

A list is composed of a header and a chain of linked elements called nodes. The header maintains memory pointers to the first and last nodes of the linked chain of nodes.. The address of the header serves as the handle to the entire list. When referring to a list, you refer to the address of its header. In addition, the header specifies the data type of the nodes in a list. Node data typing will be discussed later.

NODE STRUCTURE

A node is divided into two parts: list linkage and node content. The linkage part con- tains memory pointers to the node’s successor and predecessor nodes, the node data type, and the node priority. The content part stores the actual data structure of interest. As a C language structure, the linkage part of a node is defined as follows:

struct Node {

struct Node *In_ Succ; struct Node *In_ Pred; UBYTE In_Type; BYTE In_Pri; char *In_ Name;

2 Lists and Queues

where

In_Succ

points to the next node in the list (successor),

In_Pred

points to the previous node in the list (predecessor),

In_Type defines the type of the node,

In_Pri

specifies the priority of the node, and

In_Name

points to a printable name for the node. As usual, node refers to an instance of a node, and In is a pointer to a node.

The Exec Interrupt structure, a complete node, is defined as follows:

struct Interrupt {

struct Node is_Node; APTR is_Data; VOID (*is_Code)();

}

Here the is_Data and is_Code fields represent the useful content of the node.

NODE INITIALIZATION

Before you link a node into a list, you should initialize it. The initialization consists of setting the In_Type, In_Pri, and In_Name fields to their appropriate values. The In_Suce and In_Pred fields do not require initialization. The In_Type field contains the data type of the node. This indicates to Exec (and other interested subsystems) the type, and hence the structure, of the content portion of the node. Some of the standard system types are defined in the ezec/nodes.i and ezec/nodes.h include files. Some exam- ples of standard system types are NT_TASK, NT_LINTERRUPT, NT_DEVICE, and NT_MSGPORT. These are defined in exec/nodes.h.

Lists and Queues 3

The In_Pri field uses a signed numerical value ranging from -128 to +127 to indicate the priority of the node relative to other nodes in the same list. Higher-priority nodes have more positive values; for example, 127 is the highest priority, zero is nominal priority, and -128 is the lowest priority. Some Exec lists are kept sorted by priority order. In such lists, the highest-priority node is at the head of the list, and the lowest-priority node is at the tail of the list. For most Exec node types, priority is not used. In such cases it is a good practice to initialize the priority field to zero.

The In_Name field is a pointer to a null-terminated string of characters. Node names are used mostly to bind symbolic names to actual nodes. They are also useful for debug- ging purposes. It is always a good idea to provide every node with a name.

Here is a C example showing how you might initialize a node called myInt, which is an instance of the interrupt structure defined above:

struct Interrupt mylInt; mylInt.In_Type = NT_LINTERRUPT; myInt.lIn_Pri = 20;

myInt.In_Name = ”sample.interrupt”

HEADER STRUCTURE

As mentioned earlier, the header maintains memory pointers to the first and last nodes of the linked chain of nodes. This header also serves as a handle for referencing the en- tire list.

Here is the C-structure of a list header:

struct List { struct Node *lh_Head; struct Node *lh_Tail; struct Node *lh_TailPred;

UBYTE lh_Type; UBYTE lh_pad; }3 where: lIh_Head

points to the first node in the list,.

4 Lists and Queues

Ih_Tail

is always zero,

Ih_ TailPred

points to the last node in the list,

Ih_Type defines the type of nodes within the list, and

lh_pad

is merely a structure alignment byte (not used). As usual, list refers to an actual instance of a list, and lh is a pointer to a node.

One subtlety here should be explained further. The head and tail portions of the header actually overlap. This is best understood if you think of the head and tail as two separate nodes. The lh_Head field is the In_Succ field of the first node in the list, and the lh_Tail field is its n_Pred. The lh_Tail is set permanently to zero to indicate that this node is the first on the list —that is, it has no successors. A similar method is used for the tail node. The lh_Tail field is the lh_Succ field of the last node in the list and the Ih_TailPred field is its In_Pred. In this case, the zero lh_Tail indicates that the node is the last on the list —that is, it has no predecessors.

HEADER INITIALIZATION

List headers must be properly initialized before use. It is not adequate to initialize the entire header to zero. The head and tail entries must be set up correctly.

The header should be initialized as follows: 1. Assign the Ih_Head field to the address of lh_Tail. 2. Assign the lh_TailPred field to the address of Ih_Head. 3. Clear the lh_Tail field.

4. Set Ih_Type to the same data type as that of the nodes to be kept in this list.

In.C, an example initialization might look like this:

Lists and Queues 5

struct List list;

list.lh_Head = &list.lh_Tail; list.lh_TailPred = &list.lh_ Head; list.lh_T ail == 0;

list. lh_Type = NT_LINTERRUPTS;

In assembly code, only four instructions are necessary to initialize the header:

MOVE.L AO,(A0)

ADDQ.L #LH_TAIL,(AO)

CLR.L LH_TAIL(A0)

MOVE.L AO ,LH_TAILPRED(A0)

Note that this sequence of instructions is the same as is used in the macro NEWLIST, contained in the file exec/lists.t. The sequence performs its function without destroying the pointer to the list header in AO (which is why ADDQ.L is used). This function may also be accessed from C as a call to NewList(lh) where lh is the address of the list header. See the source code for CreatePort() in chapter 3, “Messages and Ports,” for one instance of its use.

List Functions

Exec provides a number of symmetric functions for handling lists. There are functions for inserting and removing nodes in lists, for adding and removing tail and head nodes in lists, for inserting nodes in a priority order, and for searching a list for a node with a particular name.

INSERTION AND REMOVAL

The Insert() function is used for inserting a new node into any position in a list. It al- ways inserts the node following a specified node that is already part of the list. For ex- ample, Insert(Ih,In,pred) inserts the node after pred in the specified list. If the pred node points to the list header or is null, the new node will be inserted at the head of the list. Similarly, if the pred node points to the list Ih_Tail field, the new node will be in- serted at the tail of the list. However, both of these actions can be better accomplished with the functions mentioned in the ‘“‘Special Case Insertion” section below.

6 Lists and Queues

The Remove() function is used to remove a specified node from a list. For example, Remove(In) will remove the specified node from whatever list it is in. Please note: to be removed, a node must actually be in the list. If you attempt to remove a node that is not in a list, you will cause serious system internal problems.

SPECIAL CASE INSERTION

Although the Insert() function allows new nodes to be inserted at the head and the tail of a list, the AddHead() and AddTail() functions will do so with higher efficiency. Adding to the head or tail of a list is common practice in queue type operations, as in first-in-first-out (FIFO) or last-in-first-out (LIFO or stack) operations. For example, AddHead(lh,In) would insert the node at the head of the specified list.

SPECIAL CASE REMOVAL

The two functions RemHead() and RemTail() are used in combination with AddHead() and AddTail() to create special list ordering. When you combine AddTail() and RemHead(), you produce a first-in-first-out (FIFO) list. When you combine AddHead() and RemHead() a last-in-first-out (LIFO or stack) list is pro- duced. RemTail() exists for symmetry. Other combinations of these functions can also be used productively. For example, RemTail(Ih) removes the last node from the specified list and returns a pointer to it as a result. If the list is empty, it returns a zero result.

PRIORITIZED INSERTION

None of the list functions discussed so far makes use of the priority field in the list data structure. The Enqueue() function makes use of this field and is equivalent to Insert() for a priority sorted list. It performs an insert on a priority basis, keeping the higher- priority nodes towards the head of the list. All nodes passed to this function must have their priority assigned prior to the call. For example, Enqueue(lh,In) inserts the node into the prioritized list after the last node of same or higher priority.

As mentioned earlier, the highest-priority node is at the head of the list, and the lowest- priority node is at the tail of the list. The RemHead() function will return the highest-priority node, and RemTail() will return the lowest-priority node.

Note that if you insert a node that has the same priority as another node in the list,

Enqueue() will use FIFO ordering. The new node is inserted following the last node of equal priority.

Lists and Queues 7

SEARCHING BY NAME

Because most lists contain nodes with symbolic names attached (via the n_Name field), it is possible to find a node by its name. This naming technique is used throughout Exec for such nodes as tasks, libraries, devices, and resources.

The FindName() function is provided to search a list for the first node with a given name. For example, FindName(lh, ‘“‘Furrbol’’) returns a pointer to the first node named ‘‘Furrbol.” If no such node exists, a zero is returned. The case of the name char- acters is significant; “‘foo” is different from ‘‘Foo.”’

To find multiple occurrences of nodes with identical names, the FindName() function is called multiple times. For example, if you want to find the second node with the “Furrbol’’ name:

struct List *lh; struct Node «ln, *FindName(); In = FindName(lh, “Furrbol’’); if (In != 0) { In = FindName(In, “‘Furrbol’’); }

Notice that the second search uses the node found by the first search. The FindName() function never compares the specified name with that of the starting node. It always begins the search with the successor of the starting node.

List Macros

Assembly code programmers may want to optimize their code by using assembly code list macros. Because these macros actually embed the specified list operation into the code, they result in slightly faster operations. The file ezxec/lists.2 contains the recom- mended set of macros. For example, the following instructions implement the REMOVE macro:

MOVE.L_ (A1),A0 * get successor MOVE.L LN_PRED(A1),A1 * get predecessor MOVE.L AQO,(A1) fix up predecessor’s succ pointer

MOVE.L A1,LN_PRED(AO) * fixup successor’s pred pointer

8 Lists and Queues

Empty Lists

It is often important to determine if a list is empty. This can be done in many ways, but only two are worth mentioning. If either the Ih_TailPred field is pointing to the list header or the In_Succ field of the lh_Head is zero, then the list is empty.

In C, for example, these methods would be written as follows:

if (list. lh_TailPred == list) { printf ("list is empty”); }

or

if (list .Jh_Head->In_Suce == 0) { printf ("list is empty” ); }

In assembly code, if AO points to the list header, these methods would be written as follows:

CMP.L LH_TAILPRED(A0O),AO0 BEQ list_is_empty

or

MOVE.L LH_HEAD(A0O),A1 TST.L LN_SUCC(A1) BEQ list_is_empty

Because LH_HEAD and LN_SUCC are both zero offsets, the second case can be simplified.

Scanning a List

Occasionally a program may need to scan a list to locate a particular node, find a node that has a field with a particular value, or just print the list. Because lists are linked in both the forward and backward directions, the list can be scanned from either the head or tail.

Lists and Queues 9

Here is an example of C code that uses a for loop to print the names of all nodes in a list:

struct List *lh;

struct Node *ln;

for (In = lh -> lh_Head; In -> In_Succ; In = In -> In_Succ) { printf ("node %lx is named %s’”, In, In -> In_name);

In assembly code, it is more efficient to use a lookahead cache pointer when scanning a list. In this example the list is scanned until the first zero-priority node is reached:

MOVE.L (A1),D1 * first node scan: MOVE.L D1,Al1 MOVE.L (A1),D1 * lookahead to next

BEQ.S _ not_found * end of list TST.B LN_PRI(A1)

BNE.S scan

ss * found one

not_found:

Important Note: It is possible to collide with other tasks when manipulating shared sys- tem lists. For example, if some other task happens to be modifying a list while your task scans it, an inconsistent view of the list may be formed. This can result in a cor- rupted system. Generally it is not permissible to read or write a shared system list without first locking out access from other tasks (and in some cases locking out access from interrupts). This technique of mutual exclusion is discussed in the ‘“Tasks” chapter.

10 Lists and Queues

Chapter 2

TASKS

The management of tasks on the Amiga involves task creation, termination, event sig- nals, traps, exceptions, and mutual exclusion. The discussions in this chapter assume that you have a basic understanding of lists (see chapter 1) and some understanding of multitasking principles.

Tasks 11

Introduction

Multitasking is one of the primary features supported by Exec. Multitasking is the abil- ity of an operating system to manage the simultaneous execution of multiple indepen- dent processor contexts. In addition, good multitasking does this in a transparent fashion: a task is not forced to recognize the existence of other tasks. In Exec this involves sharing the 68000 processor among a number of concurrent programs, providing each with its own virtual processor.

SCHEDULING

Exec accomplishes multitasking by multiplexing the 68000 processor among a number of task contexts. Every task has an assigned priority, and tasks are scheduled to use the processor on a priority basis. The highest-priority ready task is selected and receives processing until a higher-priority task becomes active, the running task exceeds a preset time period (a quantum) and there is another equal-priority task ready to run, or the task needs to wait for an external event before it can continue.

Task scheduling is normally preemptive in nature. The running task may lose the pro- cessor at nearly any moment by being displaced by another more urgent task. Later, when the preempted task regains the processor, it continues from where it left off.

It is also possible to run a task in a nonpreemptive manner. This mode of execution is generally reserved for system data structure access. It is discussed in the ‘‘Exclusion”’ section toward the end of this chapter.

In addition to the prioritized scheduling of tasks, teme-sliceng also occurs for tasks with the same priority. In this scheme a task is allowed to execute for a quantum (a preset time period). If the task exceeds this period, the system will preempt it and give other tasks of the same priority a chance to run. This will result in a time-sequenced round robin scheduling of all equal-priority tasks.

Because of the prioritized nature of task scheduling, tasks must avoid performing the busy wait technique of polling. In this technique, a piece of code loops endlessly waiting for a change in state of some external condition. Tasks that use the busy wait technique waste the processor and eat up all its spare power. In most cases this prevents lower- priority tasks from receiving any processor time. Because certain devices, such as the keyboard and the disk, depend on their associated tasks, using a busy wait at a high priority may defer important system services. Busy waiting can even cause system deadlocks.

12 Tasks

When there are no ready tasks, the processor is halted and only interrupts will be ser- viced. Because task multiplexing often occurs as a result of events triggered by system interrupts, this is not a problem. Halting the processor often helps improve the perfor- mance of other system bus devices.

TASK STATES

For every task, Exec maintains state information to indicate its status. A normally operating task will exist in one of three states:

running

ready

waiting

A task that is running is one that currently owns the processor. This usually means that the task is actually executing, but it is also possible that it has been temporarily displaced by a system interrupt.

A task that is ready is one that is not currently executing but that is scheduled for the processor. The task will receive processor time based on its priority relative to the priorities of other running and ready tasks.

A task that is waiting is in a paused state waiting for an external event to occur. Such a task is not scheduled to use the processor. The task will be made ready only when one of its external events occurs (see the “Signals” section below).

A task may also exist in a few transient states:

added

removed

exception

A task in the added state has just been added to Exec and has not yet been scheduled for processing.

A task in the removed state is being removed. Tasks in this state are effectively terminated and are usually undergoing clean-up operations.

A task in the exception state is scheduled for special exception processing.

Tasks 13

TASK QUEUES

Tasks that are not in the running state are linked into one of two system queues. Tasks that are marked as ready to run but awaiting an opportunity to do so are kept in the ready queue. This queue is always kept in a priority sorted order with the highest priori- ty task at the head of the queue. A waiting queue accounts for tasks that are awaiting external events. Unlike the ready queue, the waiting queue is not kept sorted by priori- ty. New entries are appended to the tail of the queue. A task will remain in the waiting queue until it is awakened by an event (at which time it is placed into the ready queue).

PRIORITY

A task’s priority indicates its importance relative to other tasks. Higher-priority tasks receive the processor before lower-priority tasks do. Task priority is stored as a signed number ranging from -128 to +127. Higher priorities are represented by more positive values; zero is considered the neutral priority. Normally, system tasks execute some- where in the range of +20 to -20.

It is not wise to needlessly raise a task’s priority. Sometimes it may be necessary to carefully select a priority so that the task can properly interact with various system tasks. The ChangePri() Exec function is provided for this purpose.

STRUCTURE

Exec maintains task context and state information in a task-control data structure. Like most Exec structures, these structures are dynamically linked onto various task queues through the use of a prepended list Node structure. The C-language form of this struc- ture is defined in the exec/task.h include file as follows:

14 Tasks

extern struct Task {

struct UBYTE UBYTE BYTE BYTE ULONG ULONG ULONG ULONG UWORD UWORD APTR APTR APTR APTR APTR APTR APTR VOID VOID struct APTR

}3

Node tc_Node; tce_F lags; tc_State; tc_IDNestCnt; tce_TDNestCnt; tce_SigAlloc; tc_Sig Wait; tc_SigRecvd; tc_SigExcept; te_TrapAlloc; tc_TrapAble; tce_ExceptData; tc_ExceptCode; tc_TrapData; tc_TrapCode; tc_SPReg; tc_SPLower; tce_SPUpper; (*tc_Switch)(); (*tc_Launch)();

List tc_MemEntry;

tc_UserData;

/* intr disabled nesting */ /* task disabled nesting */ /* sigs allocated */

/* sigs we are waiting for */ /* sigs we have received */ /* sigs we will take excepts for */ /* traps allocated */

/* traps enabled */

/* points to except data */ /* points to except code */ /* points to trap code */

/* points to trap data */

/* stack pointer */

/* stack lower bound */

/* stack upper bound + 2*/ /* task losing CPU */

/* task getting CPU */

/* allocated memory */

/* per task data */

A similar assembly code structure is available in the ezec/tasks.7 include file.

Most of these fields are not relevant for simple tasks; they are used by Exec for state and administrative purposes. A few fields, however, are provided for the advanced pro- grams that support higher level environments (as in the case of processes) or require pre- cise control (as in devices). The following sections explain these fields in more detail.

Creation

To create a new task you must allocate a task structure, initialize its various fields, and then link it into Exec with a call to AddTask(). The task structure may be allocated by calling the AllocMem() function with the MEMF_CLEAR and MEMF_PUBLIC allocation attributes. These attributes indicate that the data structure is to be pre-

initialized to zero and that the structure is shared.

Tasks 15

The Task fields that require initialization depend on how you intend to use the task. For the simplest of tasks, only a few fields must be initialized:

tc_Node

The task list node structure. This includes the task’s priority, its type, and its name (refer to the “Lists and Queues” chapter).

tc_SPLower The lower memory bound of the task’s stack

tc_SPUpper The upper memory bound of the task’s stack

tc_SPReg

The initial stack pointer. Because task stacks grow downward in memory, this field is usually set to the same value as tc_SPUpper.

Zeroing all other unused fields will cause Exec to supply the appropriate system default values. Allocating the structure with the MEMF_CLEAR attribute is an easy way to be sure that this happens.

Once the structure has been initialized, it must be linked to Exec. This is done with a call to AddTask() in which the following parameters are specified:

task

initialP C

finalPC

A pointer to an initialized task structure.

The entry point of your task code. This is the address of the first in- struction the new task will execute.

The finalization code for your task. This is a code fragment that will receive control if the initiaIPC routine ever performs a return (RTS). This exists to prevent your task from being launched into random memory upon an accidental return. The finalPC routine should usu- ally perform various program-related clean-up duties and should then remove the task. If a zero is supplied as this parameter, Exec will use its default finalization code (which simply calls the RemTask() function)

Depending on the priority of the new task and the priorities of other tasks in the sys- tem, the newly added task may immediately begin execution.

Here is an example of simple task creation:

16 Tasks

#include ”exec/types.h” #include ”exec/memory.h” #include ”exec/tasks.h” #define STACK_SIZE 1000 extern APTR AllocMem(); extern EntryPoint();

SimpleTask() {

struct Task *tc; APTR stack; stack = (APTR) AllocMem (STACK_SIZE, MEMF_CLEAR ); if (stack === 0) { printf ("not enough memory for task stack”); return(0);

}

te = (struct Task *) AllocMem (sizeof(struct Task), MEMF_CLEAR | MEMF_PUBLIC);

if (tc == 0) { printf ("not enough memory for task control structure” ); FreeMem (stack, STACK_SIZE); return(0);

task = (struct Task *) AllocMem (sizeof(struct Task), MEMF_CLEAR | MEMF_PUBLIC);

if (tc == 0) { printf ("not enough memory for task name”); FreeMem (stack, STACK_SIZE); return(0);

j

tc -> tc_SPLower = (APTR) stack;

tc -> tc_SPUpper = (APTR) (STACK_SIZE + (ULONG) stack); te -> tc_SPReg = tc-> tc_SPUpper;

te -> tc_Node.lIn_Type = NT_TASK; tc -> tc_Node.lIn_Name = ”example.task”;

AddTask (tc, EntryPoint, 0);

Tasks 17

STACK

Every task requires a stack. All task stacks are user mode stacks (in the language of the 68000) and are addressed through the A7 CPU register. All normal code execution oc- curs on this task stack. Special modes of execution (processor traps and system inter- rupts for example) execute on a single supervisor mode stack and do not directly affect task stacks.

Task stacks are normally used to store local variables, subroutine return addresses, and saved register values. Additionally, when a task loses the processor, all of its current re- gisters are preserved on this stack (with the exception of the stack pointer itself, which must be saved in the task structure).

The amount of stack used by a task can vary widely. The minimum stack size is 70 bytes, which is the number required to save 17 CPU registers and a single return ad- dress. Of course, a stack of this size would not give you adequate space to perform any subroutine calls (because the return address occupies stack space). On the other hand, a stack size of 1I< would suffice to call most system functions but would not allow much in the way of local variable storage.

Because stack-bounds checking is not provided as a service of Exec, it is important to provide enough space for your task stack. Stack overflows are always difficult to debug and may result not only in the erratic failure of your task but also in the mysterious malfunction of other Amiga subsystems.

Termination

Task termination may occur as the result of a number of situations:

1. A program returning from its initialPC routine and dropping into its finalPC routine or the system default finalizer.

bo

A task trap that is too serious for a recovery action. This includes traps like processor bus error, odd address access errors, etc.

3. A trap that is not handled by the task. For example, the task might be ter- minated if your code happened to encounter a processor TRAP instruction and you did not provide a trap handling routine.

4. An explicit call to the Exec RemTask() function.

18 Tasks

Task termination involves the deallocation of system resources and the removal of the task structure from Exec. The most important part of task termination is the dealloca- tion of system resources. A task must return all memory that it allocated for its private use, it must terminate any outstanding I/O commands, and it must close access to any system libraries or devices that it has opened.

It is wise to adopt a strategy for task clean-up responsibility. You should decide wheth- er resource allocation and deallocation is the duty of the creator task or the newly creat- ed task. Sometimes it is easier and safer for the creator to handle the necessary resource allocation and deallocation on behalf of its offspring. On the other hand, if you expect the creator to terminate before its offspring, it would not be able to handle resource deallocation. In such a case, each of its child tasks would need to deallocate its own resources.

Signals

Tasks often need to coordinate with other concurrent system activities (other tasks and interrupts). Such coordination is achieved through the synchronized exchange of specific event indicators called signals. This is the primary mechanism responsible for all inter- task communication and synchronization on the Amiga.

The signal mechanism operates at a low level and is designed for high performance. Sig- nals often remain hidden from the user program. The message system, for instance, may use signals to indicate the arrival of a new message. The message system is described in more detail in chapter 3.

The signal system is designed to support independent simultaneous events. Signals may be thought of as occurring in parallel. Each task may define up to 32 independent sig- nals. These signals are stored as single bits in a few fields of the task control structure, and one or more signals can occur at the same time.

All of these signals are considered task relative: a task may assign its own significance to a particular signal. Signals are not broadcast to all tasks; they are directed only to indi- vidual tasks. A signal has meaning to the task that defined it and to those tasks that have been informed of its meaning. For example, signal bit 12 may indicate a timeout event to one task, but to another task it may indicate a message arrival event.

Tasks 19

ALLOCATION

As mentioned above, a task assigns its own meaning to a particular signal. Because cer- tain system libraries may occasionally require the use of a signal, there is a convention

for signal allocation. It is unwise ever to make assumptions about which signals are ac- tually in use.

Before a signal can be used, it must be allocated with the AllocSignal() function. This marks the signal as being in use and prevents the accidental use of the same signal for more than one event. You may ask for either a specific signal number or the next free signal. The state of the newly allocated signal is cleared (ready for use). Generally it is best to let the system assign you the next free signal. Of the 32 available signals, the lower 16 are usually reserved for system use. This leaves the upper 16 signals free for the user. Other subsystems that you may call depend on AllocSignal().

The following C example asks for the next free signal to be allocated for its use:

signal = AllocSignal(-1);

if (signal == -1) { printf(”no signal bits available”); return;

else {

printf(” allocated signal number %ld”, signal);

Note that the value returned by AllocSignal() is a signal bit number. This value can-

not be used directly in calls to signal-related functions without first being converted to a mask:

mask = 1 << signal; When a signal is no longer needed, it should be freed for reuse with FreeSignal().

It is important to realize that signal bit allocation is relevant only to the running task. You cannot allocate a signal from another task.

20 Tasks

WAITING FOR A SIGNAL

Signals are most often used to wake up a task upon the occurrence of some external event. This happens when a task is in its wait state and another task (or a system in- terrupt) causes a signal. The Wait() function specifies the set of signals that will wake up the task and then puts the task to sleep (into the waiting state). Any one signal or any combination of signals from this set are sufficient to awake the task. Wait() returns a mask indicating which signals from this set satisfied the wait. The Wait() function implicitly clears those signals that satisfied the wait. This effectively resets those signals for reuse.

Because tasks (and interrupts) normally execute asynchronously, it is often possible to receive a particular signal before a task actually waits for it. To avoid missing any events, programs should hold signals until the Wait() function is called, or until it is ex- plicitly cleared (with SetSignal()). In such cases a wait will be immediately satisfied, and the task will not be put to sleep.

As mentioned earlier, a task may wait for more than one signal. When the task returns from the wait, the actual signal mask is returned. Usually the program must check which signals occurred and take the appropriate action. The order in which these bits are checked is often important. Here is a hypothetical example:

signals == Wait (newCharSig | cancelSig | timeOutSig); if (signals & cancelSig) { printf (”canceled” );

if (signals & newCharSig) { printf ("new character” );

} if (signals & timeOutSig) { printf (”"timeout” );

This will put the task to sleep, waiting for a new character, a cancel event, or the ex- piration of a time period. Notice that this code checks for a cancel signal before check- ing for a new character or a timeout. Although a program can check for the occurrence of a particular event by checking whether its signal has occurred, this may lead to busy wait polling. Such polling is wasteful of the processor and is usually detrimental to the proper function of the system.

Tasks 21

GENERATING A SIGNAL

Signals may be generated from both tasks and system interrupts with the Signal() func- tion. For example Signal(tc,mask) would signal the task with the mask signals. More than one signal can be specified in the mask.

Exclusion

From time to time the advanced system program may find it necessary to access global system data structures. Because these structures are shared by the system and by other tasks that execute asynchronously to your task, it is wise for you to exclude simultane- ous access to these structures. This can be accomplished by forbidding or disabling, or with the use of semaphores. A section of code that requires the use of any of these mechanisms to lock out access by others is termed a critical section.

FORBIDDING

Forbidding is used when a task is accessing shared structures that might also be accessed at the same time from another task. It effectively eliminates the possibility of simultane- ous access by imposing nonpreemptive task scheduling. This has the net effect of disa- bling multitasking for as long as your task remains in its running state. While forbid- den, your task will continue running until it performs a call to Wait() or exits from the forbidden state. Interrupts will occur normally, but no new tasks will be dispatched, re- gardless of their priorities.

When a task running in the forbidden state calls the Wait() function, it implies a tem- porary exit from its forbidden state. While the task is waiting, the system will perform normally. When the task receives one of the signals it is waiting for, it will again reenter the forbidden state. To become forbidden, a task calls the Forbid() function. To es- cape, the Permit() function is used. The use of these functions may be nested with the expected affects; you will not exit the forbidden mode until you call the outermost Per- mit/().

As an example, Exec memory region lists should be accessed only when forbidden. To access these lists without forbidding jeopardizes the integrity of the entire system.

22 Tasks

struct ExecBase *eb; struct MemHeader *mh; APTR firsts[ARRAYSIZE]; int count; :

Forbid(); for (mh = (struct MemHeader *) eb -> MemList.lh_Head; mh -> mh_Node.lIn_Suce; mh = mh -> mh_Node.In_Succ) { firsts[count+-+] = mh -> mh_First;

Permit();

As this program traverses down the memory region list, it remains forbidden to prevent the list from changing as it is being accessed.

DISABLING

Disabling is similar to forbidding, but it also prevents interrupts from occurring during a critical section. Disabling is required when a task accesses structures that are shared by interrupt code. It eliminates the possibility of an interrupt accessing shared structures by preventing interrupts from occurring.

To disable interrupts you can call the Disable() function. If you are writing in assem- bly code, the DISABLE macro is more efficient (but consumes more code space). To enable interrupts again, use the Enable() function and ENABLE macros.

Like forbidden sections, disabled sections can be nested. Also like forbidden sections, the Wait() function implies an Enable() until the task again regains the processor.

It is important to realize that there is a danger in using disabled sections. Because the software on the Amiga depends heavily on its interrupts occurring in nearly real time, you cannot disable for more than a very brief instant. A rule of thumb is to disable for no more than 250 microseconds.

Masking interrupts by changing the 68000 processor interrupt priority levels with the MOVESR instruction can also be dangerous and is generally discouraged. The disable- and enahle-related functions and macros control interrupts through the 4703 custom chip and not through the 68000 priority level. In addition, the processor priority level can be altered only from supervisor mode (which means this process is much less efficient).

Tasks 23

It is never necessary to both disable and forbid. Because disable prevents interrupts, it also prevents preemptory task scheduling. Many Exec lists can only be accessed while disabled. Suppose you want to print the names of all waiting tasks. You would need to access the task list from a disabled section. In addition, you must avoid calling certain system functions that require multitasking to function properly (printf() for example). In this example, the names are gathered into a name array while the code section is dis- abled. Then the code section is enabled and the names are printed.

#include ”exec/types.h”

#include ”exec/execbase.h” #include ”exec/tasks.h”

extern struct ExecBase *SysBase;

main()

}

struct Task *task; char *names([20]; int count, i;

count = 0; Delay(50); Disable();

for (task = (struct Task *)SysBase->Task Wait.lh_Head; task- >tc_Node.In_Suce; /* stop when Successor node == 0 */ task = (struct Task *)task->tc_Node.In_Succ) { names[count+-+] == task->tc_Node.In_Name; Enable(); for (i = 0; i < count; i++)

printf (” %s\n ”, names{i]);

Of course, the code in this example will have problems if a waiting task is removed be- fore its name is printed. If this were to happen, the name-string pointer would no longer

be valid.

To avoid such problems it is a good programming practice to copy the entire

name string into a temporary buffer.

24 Tasks

SEMAPHORES

Messages and message ports can be used as semaphores for the purposes of mutual exclu- sion. With this method of locking, all tasks agree on a locking convention before access- ing shared data structures. Tasks that do not require access are not affected and will run normally, so this type of exclusion is considered preferable to forbidding and disa- bling. Unfortunately, semaphores also represent a considerable amount of overhead for simple system operations and are not used internal to Exec for efficiency reasons. This form of exclusion is explained in more detail in the ‘‘Messages and Ports” chapter.

Exceptions

Tasks can specify that certain asynchronous events cause exceptions, which are task- private interrupts that redirect a task’s flow of control: The task essentially suspends what it is doing and enters a special routine to process its exceptional event.

Exceptions are driven by the task signal mechanism described earlier in this chapter. In- stead of waiting for a signal to occur, you indicate that it is an exception signal with the SigExcept() function. When the signal occurs, the task will be “interrupted” from its normal execution and placed in a special exception handler.

The tc_ExceptCode and tc_ExceptData task fields are used to establish the excep- tion handler. The field tec_ExceptCode points to the routine that will handle the ini- tial processing of all exceptions. If this field is zero, Exec will ignore all exceptions. The te_ExceptData field can be used to provide a pointer to related data structure.

On entry to the exception code, the system passes certain parameters in the processor re- gisters. DO contains a signal mask indicating which exception has just occurred, and Al points to the related exception data (from te_ExceptData). In addition, the previous task context is pushed onto the task’s stack. This includes the previous PC, SR, DO-D7, and AO-AG registers. You can think of an exception as a subtask outside of your normal task. Because task exception code executes in user mode, however, the task stack must be large enough to supply the extra space consumed during an exception.

While processing a given exception, Exec prevents that exception from occurring recur- sively. At exit from your exception-processing code you should return the same value in DO to re-enable that exception signal. When the task executes the RTS at the end of the handler, the system restores the previous contents of all of the task registers and resumes the task at the point where it was interrupted by the exception signal. When two or more exception codes occur simultaneously, the exception-processing code determines the order in which they are handled by the order in which the signal bits are examined.

Tasks 25

Traps

Task traps are synchronous exceptions to the normal flow of program control. They are always generated as a direct result of an operation performed by your program’s code. Whether they are accidental or purposely generated, they will result in your program be- ing forced into a special condition in which it must immediately handle the trap. Ad- dress error, privilege violation, zero divide, and trap instructions all result in task traps. They may be generated directly by the 68000 processor (Motorola calls them ‘“‘excep- tions”) or simulated by software.

A task that incurs a trap has no choice but to respond immediately. The task must have a module of code to properly handle the trap. Your task may be aborted if a trap occurs and no means of handling it has been provided.

You may choose to do your own processing of traps. The tc_TrapCode field is the ad- dress of the handler that you have designed to process the trap. The tc_TrapData field is the address of the data area for use by the trap handler.

The 68000 traps of interest are:

2 Bus error

3 Address error

4 Illegal instruction

5 Zero divide

6 CHIC instruction

7 TRAPYV instruction 8 Privilege violation 9 Trace

10 Line 1010 emulator 11 Line 1111 emulator

32-47 Trap instructions

26 Tasks

The actual stack frames generated for these traps are processor-dependent. The 68010 and 68020 processors will generate a different type of stack frame than the 68000. If you plan on having your program handle its own traps, you should not make assumptions about the format of the supervisor stack frame. Check the flags in the AttnF lags field of the ExecBase structure for the type of processor in use and process the stack frame accordingly.

HANDLERS

For compatibility with the 68000, Exec performs trap handling in supervisor mode. This means that all task switching is disabled during trap handling. At entry to the task’s trap handler, the system stack does contain the trap frame as defined in the 68000 manual. A longword exception number is added at the bottom of this frame. That is, when a handler gains control, the top of stack contains the exception number and the 68000 frame immediately follows.

To return from trap processing, remove the exception number from the stack (note that this is the supervisor stack, not the user stack) and then perform a return from excep- tion (RTE).

Because trap processing takes place in supervisor mode, with task dispatching disabled, it is strongly urged that you keep trap processing as short as possible or switch back to user mode from within your trap handler. If a trap handler already exists when you add your own trap handler, it is smart to propagate any traps that you do not handle down to the previous handler. This can be done by saving the previous te_TrapCode and tc_TrapData for use by your handler.

TRAP INSTRUCTIONS

The TRAP instructions in the 68000 generate traps 32-47. Because many independent pieces of system code may desire to use these traps, the AllocTrap() and FreeTrap() functions are provided. These work in a fashion similar to that used by AllocSignal() and FreeSignal(), mentioned above.

Allocating traps is simply a bookkeeping job within a task. It does not affect how the system calls the trap handler; it helps coordinate who owns what traps. Exec does noth- ing to determine whether or not the task is prepared to handle this particular trap. It simply calls your code. It is up to your program to handle the trap.

Tasks 27

To allocate any trap, you can use the following code:

trap = AllocTrap(-1);

if (trap == -1) { printf(”all trap instructions are in use” ); return;

}

or you can select a specific trap using this code:

trap = AllocTrap(3);

if (trap == -1) { printf("trap #3 is in use”); return;

}

To free a trap you use FreeTrap().

28 Tasks

Chapter 3

MESSAGES AND PORTS

Introduction

For intersystem communication, Exec provides a consistent, high-performance mechan- ism of messages and ports. This mechanism is used to pass message structures of arbi- trary sizes from task to task, interrupt to task, or task to software interrupt. In addi-

tion, messages are often used to coordinate operations between a number of cooperating tasks.

Messages and Ports 29

A message data structure has two parts: system linkage and message body. The sys- tem linkage is used by Exec to attach a given message to its destination. The message body contains the actual data of interest. The message body is any arbitrary data block less than 64KX bytes in size.

Messages are always sent to a predetermined destination port. At a port, incoming mes- sages are queued in a first-in-first-out (FIFO) order. There are no system restrictions on the number of ports or the number of messages that may be queued to a port (other than the amount of available system memory).

Messages are always queued by reference. For performance reasons message copying is not performed. In essence, a message between two tasks is a temporary license for the receiving task to use a portion of the memory space of the sending task—that portion being the message itself. This means that if task A sends a message to task B, the mes- sage is still part of the task A context. Task A, however, should not access the message until it has been replied —that is, until task B has sent the message back, using the ReplyMsg() function. This technique of message exchange imposes important restric- tions on message access.

Ports

Ports are rendezvous points at which messages are collected. A port may contain any number of outstanding messages from many different originators. When a message arrives at a port, the message is appended to the end of the list of messages for that port, and a prespecified arrival action is invoked. This action may do nothing, or it may cause a predefined task signal or software interrupt (see the “Interrupts” chapter).

Like many Exec structures, ports may be given a symbolic name. Such names are par- ticularly useful for tasks that must rendezvous with dynamically created ports. They are also useful for debugging purposes.

STRUCTURE

A message port consists of a MsgPort structure as defined in the exec/ports.h and exec/ports.i include files. The C structure for a port is as follows:

30 Messages and Ports

struct MsgPort { struct Node mp_Node; UBYTE mp_Flags; UBYTE mp_SigBit; struct Task *mp_SigTask; struct List mp_MsgList;

}3 where

mp_Node is a standard Node structure. This is useful for tasks that might want to rendezvous with a particular message port by name.

mp_Flags are used to indicate message arrival actions. See the explanation below.

mp_SigBit is the signal bit number when a port is used with the task signal arrival action.

mp_SigTask is a pointer to the task to be signaled. If a software-interrupt arrival action is specified, this is a pointer to the interrupt structure.

mp_MsgList

is the list header for all messages queued to this port. (See the “Lists and Queues” chapter).

The mp_Flags field contains a subfield indicated by the PF_ACTION mask. This sub-field specifies the message arrival action that occurs when a port receives a new mes- sage. The possibilities are as follows:

PA_SIGNAL This subfield tells the program to signal the specified task on the arrival of a new message. Every time a message is put to the port another signal will occur regardless of how many messages have been queued to the port.

PA_SOFTINT This subfield causes the specified software interrupt. Like PA_SIGNAL, PA_SOFTINT will cause the software interrupt to be posted every time a message is received.

Messages and Ports 31

PA_IGNORE This subfield tells the program to perform no operation other than queuing the message. This action is often used to stop signaling or software inter- rupts without disturbing the contents of the mp_SigTask field.

It is important to realize that a port’s arrival action will occur for each new message queued, and that there is not a one-to-one correspondence between messages and signals. Task signals are only single-bit flags so there is no record of how many times a particu- lar signal occurred. There may be many messages queued and only a single task signal. All of this has certain implications when designing code that deals with these actions. Your code should not depend on receiving a signal for every message at your port. All of this is also true for software interrupts.

CREATION

To create a new message port, you must allocate and initialize a MsgPort structure. If you desire to make the port public, you will also need to call the AddPort() function. Port structure initialization involves setting up a Node structure, establishing the mes- sage arrival action with its parameters, and initializing the list header. The following example of port creation is equivalent to the CreatePort() function as supplied in amiga. (2b:

extern APTR AllocMem(); extern UBYTE AllocSignal(); extern struct Task *FindTask();

struct MsgPort * CreatePort (name, pri) char *name; BYTE pri; {

int sigBit;

struct MsgPort *mp;

if ((sigBit = AllocSignal (-1)) == -1) return ((struct MsgPort *) 0);

port = AllocMem (sizeof(*port), MEMF_CLEAR | MEMF_PUBLIC); if (port == 0) {

FreeSignal (sigBit);

return ((struct MsgPort *) (0));

32 Messages and Ports

mp->mp_Node.In_Name = name; mp->mp_Node.In_Pri = pri; mp->mp_Node.lIn_Type = NT_MSGPORT;

mp->mp_Flags = PA_SIGNAL; mp->mp_SigBit = sigBit; mp->mp_SigTask = FindTask (0);

if (name != 0) { AddPort (mp); } else { NewList (&mp->mp_MsgList);

return (mp);

DELETION

Before a message port is deleted, all outstanding messages from other tasks must be returned. This is done by replying to each message until the message queue is empty. Of course, there is no need to reply to messages owned by the current task (the task per- forming the port deletion). Public ports attached to the system with AddPort() must be removed from the system with RemPort().

RENDEZVOUS

The FindPort() function provides a means of finding the address of a public port given its symbolic name. For example, FindPort(‘“‘Spyder”’) will return either the address of the message port or a zero indicating that no such public port exists. Names should.be made rather unique to prevent collisions among multiple applications. It is a good idea to use your application name as a prefix for your port name.

Messages

As mentioned earlier, a message contains both system header information and the actual message content. The system header is of the Message form defined in ezec/ports.h and ezec/ports.t. In C this structure is as follows:

Messages and Ports 33

struct Message { struct Node mn_Node; struct MsgPort *mn_ReplyPort; UWORD mn_Length;

}3 where

mn_Node is a standard Node structure used for port linkage.

mn_ReplyPort is used to indicate a port to which this message will be returned when a reply is necessary.

mn_Length indicates the length of the message body in bytes.

This structure is always attached to the head of all messages. For example, if you want a message structure that contains the x and y coordinates of a point on the screen, you could define it as follows:

struct XYMessage { struct Message xy_Msg; UWORD «x,y;

}

For this structure, the mn_Length field should be set to sizeof X YMessage.

PUTTING A MESSAGE

A message is delivered to a given destination port with the PutMsg() function. The message is queued to the port, and that port’s arrival action is invoked. If the action specifies a task signal or a software interrupt, the originating task may temporarily lose the processor while the destination processes the message. If a reply to the message is required, the mn_ReplyPort field must be set up prior to the call to PutMsg().

Here is a simple program fragment for putting a message to a public port:

34 Messages and Ports

struct MsgPort *mp, *replymp; struct XYMessage *xymsg;

xymsg = (struct XYMessage*) AllocMem (sizeof(*xymsg), MEMF_PUBLIC); if (xymsg === 0) {

printf ("not enough memory for message”);

return;

}

replymp = CreatePort (”xyreplyport” ,O); /* as defined earlier in this chapter */

if (replymp == 0) { printf ("could not create the reply port” ); FreeMem (xymsg, sizeof(*xymsg)); return;

}

xymsg -> xy_Msg.mn_Node.In_Type = NT_MESSAGE; xymsg -> xy_Msg.mn_ReplyPort = replyport;

mp = FindPort (”"Spyder”);

if (mp == 0) { printf ("Spyder port not found”); return;

}

PutMsg (mp, xymsg);

WAITING FOR A MESSAGE

A task may go to sleep waiting for a message to arrive at one or more ports. This tech- nique is widely used on the Amiga as a general form of event notification. For example, it is used extensively by tasks for I/O request completion.

To wait for the arrival of a message, the message port must be properly initialized. In particular, the mp_SigTask field must contain the address of the task to be signaled and mp_SigBit must contain a preallocated signal number (as described in the ‘“Tasks” chapter). You can call the WaitPort() function to wait for a message to arrive at a port. This function will return the first message queued to a port. If the port is empty,

your task will go to sleep waiting for the first message. If the port is not empty, your task will not go to sleep.

Messages and Ports 35

A more general form of waiting for a message involves the use of the Wait() function (see the “Tasks” chapter). This function waits for task event signals directly. If the signal assigned to the message port occurs, the task will awaken. Using the Wait() function is more general because you can wait for more than just a single message port. For example, you may want to wait for a message and a timeout signal. The Wait() function lets you specify a mask containing the signals associated with your message port and your timeout signal.

Here’s an example using WaitPort():

struct MsgPort *mp; struct Message *msg, *WaitPort(); int SigBit;

SigBit = AllocSignal (-1);

if (SigBit == -1) { printf (”no free signal bits” ); return;

} mp -> mp_Flags |= PA_signal; mp -> mp_SigBit = SigBit; mp -> mp_SigTask = FindTask (0); /* self */ msg = WaitPort (mp); Note that WaitPort() only returns a pointer to the first message in a port. It does not

actually remove the message from the port queue.

GETTING A MESSAGE

Messages are usually removed from ports with the GetMsg() function. This function removes the next message at the head of the port queue and returns a pointer to it. If there are no messages in a port, this function returns a zero.

The example below illustrates the use of GetMsg() to print the contents of all messages in a port:

while ((msg = GetMsg (mp)) != 0) { printf ("x—%ld y=%ld”, msg->x, msg->y)} }

36 Messages and Ports

Certain messages may be more important than others. Because ports impose FIFO ord- ering, these important messages may get queued behind other messages regardless of their priority. If it is necessary to recognize more important messages, it is easiest to create another port for these special messages.

REPLYING

When the operations associated with receiving a new message are finished, it is usually necessary to send the message back to the originator. The receiver replies the message by returning it to the originator using the ReplyMsg() function. This is important because it notifies the originator that the message can be reused or deallocated. The ReplyMsg() function serves this purpose. It returns the message to the port specified in the mn_ReplyPort field of the message. If this field is zero, no reply is returned.

The previous example can be enhanced to reply to each of its messages:

while ((msg = GetMsg (mp)) != 0) { printf ("x=%ld y=%ld”, msg->x, msg->y); ReplyMsg (msg);

Notice that the reply does not occur until after the message values have been used.

Often the operations associated with receiving a message involve returning results to the originator. Typically this is done within the message itself. The receiver places the results in fields defined (or perhaps reused) within the message body before replying the message back to the originator. Receipt of the replied message at the originator’s reply port indicates it is once again safe for the originator to use or change the values found within the message.

Messages and Ports 37

Chapter 4

INPUT/OUTPUT

Introduction

One of the primary purposes of Exec is to provide a standard form for all device input/output (I/O). This includes the definition of a standard device interface, the for- mat for I/O requests, and the establishment of rules for normal device/task interaction, In addition, the guidelines for nonstandard device I/O are also defined. In the design of the Amiga I/O system, great care has been taken to avoid dictating the form of imple- mentation or the internal operational characteristics of a device.

Input/Output 39

In its purest sense, a device is an abstraction that represents a set of well-defined interactions with some form of physical media. This abstraction is supported by a stan- dard Exec data structure and an independent system code module. The data structure provides the external interface and maintains the current device state. The code module supplies the operations necessary to make the device functional. (In many operating sys- tems, this code module is referred to as a device driver. See Amiga ROM Kernel Refer- ence Manual: Libraries and Devices for the source assembly language code for a disk- resident device driver with its own task for handling I/O requests.)

A device unit is an instance of a device. It shares the same device data structure and code module with all other units of the same device; however, it operates in an indepen- dent fashion. Often units correspond to separate physical subsystems of the same gen- eral device class. For example, each Amiga floppy disk drive is an independent unit of the same device. There is only one device data structure and one code module to sup- port all of these units.

Exec I/O is often performed using the message system described in the chapter 3. Most aspects of message passing are concealed within the Exec I/O support routines. How- ever, it is important to realize that I/O request blocks, once issued, must not be modified or reused until they are returned to your program’s control by Exec.

Request Structure

An I/O request is always directed to a device unit. This request is organized as a control block and contains a command to be performed on a specified unit. It is passed through a standard device interface function, where it is processed and executed by the device’s code module. All request parameters are included in the request control block, and I/O request results are returned in the same control block.

Every device unit responds to a standard set of commands, and may optionally provide a nonstandard set of commands as well. The standard commands are explained later in this chapter. Nonstandard commands are discussed in the documentation pertaining to the particular device involved.

An I/O request always includes at least an IORequest data structure. This is a stan-

dard header used for all I/O requests. It is defined in the ezec/io.h and ezec/to.z include files as follows:

40 Input/Output

struct IORequest {

struct Message *io_Message; struct Device *io_Device; struct Unit *io_Unit;

UWORD = io_Command; UBYTE _io_Flags; BYTE io_Error;

where

io_Message is a message header (see the “Messages and Ports” chapter). This header is used by the device to return I/O requests upon completion. It is also used by devices internally for I/O request queuing. This header must be prop- erly initialized for I/O to work correctly.

io_ Device is a pointer to the device data structure node. This field is automatically set up by an Exec function when the device is opened. |

io_Unit specifies a unit to the device internally. This is a device private field and should not be accessed by the user. The format of this field is device dependent and is set up by the device during the open sequence.

io_ Command is the command requested. This may be either one of the system standard commands or a device-specific command.

io_F lags is used to indicate special request options and state. This field is divided into two subfields of four bits each. The lower four bits are for use by Exec and the upper four bits are available to the device.

io_Error is an error or warning number returned upon request completion.

The io_Device, io_Unit, and io_Command fields are not affected by the servicing of the request. This permits repeated I/O using the same request.

Input/Output 41

The standard I/O requests use an expanded form of the IORequest structure:

struct IOStdReq {

struct Message io_Message; struct Device *io_Device; struct Unit *io_Unit;

UWORD _io_Command; UBYTE _io_Flags; BYTE io_Error; ULONG __io_Actual; ULONG _io_Length; APTR io_ Data; ULONG __io_Offset;

}

where the additional fields are used as follows:

io_Actual

indicates the actual number of bytes transferred. This field is valid only upon completion.

io_Length is the requested number of bytes to transfer. This field must be set up

prior to the request. A special length of -1 is often used to indicate variable-length transfers.

io_Data is a pointer to the transfer data buffer.

io_ Offset

indicates a byte offset (for structured devices). For block-structured devices

(such as a floppy disk device) this number must be a multiple of the block size.

Devices with nonstandard commands may add their own special fields to the I/O request structure as needed. Such extensions are device specific.

42 Input/Output

Interface Functions

Four Exec functions are responsible for interfacing I/O requests to actual device drivers. These functions operate independently of the particular device command requested. They deal with the request block as a whole, ignoring its command and its command parameters.

DoIO() is the most commonly used I/O function. It initiates an I/O request and waits for its completion. This is a synchronous form of device I/O; control is not returned to the caller until completion.

SendIO() is used to initiate an I/O request without waiting for completion. This is an asynchronous form of device I/O; control is returned even if the request has not completed.

WaitIO() is used to wait for the completion of a previously initiated asynchronous 1/O request. This function will not return control until the request has completed (successfully or unsuccessfully).

CheckIO() is used to see if an asynchronous I/O request has completed.

In addition to the above Exec functions, there are two I/O related functions that are actually direct entries into the device driver itself. These functions are part of the actual device driver interface to the system and should be used with care. They incur slightly less overhead but require more knowledge of the I/O system internals (you must know how quick I/O works, for instance):

BeginIO() initiates an IO request. The request will be synchronous or asynchronous depending on the device driver.

AbortIO() attempts to cancel a previous I/O request. This function is easily accessed as an assembly code macro ABORTIO or through the C library Exec sup- port function AbortIO().

Input/Output 43

Standard Commands

There are eight standard commands to which all devices are expected to respond. If the device is not capable of performing one of these commands, it will at least return an error indication that the command is not supported. These commands are defined in the ezec/to.h and ezxec/to.2 include files.

CMD_RESET This command resets the device unit. It completely initializes the device unit, returning it to its default configuration, aborting all of its pending

I/O, cleaning up any internal data structures, and resetting any related hardware.

CMD_READ This command reads a specified number of bytes from a device unit into the data buffer. The number of bytes to be read is specified in the

io_Length field. The number of bytes actually read is returned in the io_Actual field.

CMD_WRITE This command writes a specified number of bytes to'a device unit from a data buffer. The number of bytes to be written is specified in the

io_Length field. The number of bytes actually written is returned in the io_Actual field.

CMD_UPDATE This command forces out all internal buffers, causing device internal memory buffers to be written out to the physical device unit. A device will transparently perform this operation when necessary, but this command allows you to request explicitly that such an action take place. It is useful for devices that maintain internal caches, such as the floppy disk device.

CMD_CLEAR This command clears all internal buffers. It deletes the entire contents of a device unit’s internal buffers. No update is performed; all data is lost.

CMD_STOP This command stops the device unit immediately (at the first opportunity). All I/O requests continue to queue, but the device unit stops servicing: them. This command is useful for devices that may require user interven- tion (printers, plotters, data networks, etc.).

44 Input/Output

CMD_START This command causes the device unit to continue after a previous CMD_STOP command. The device resumes from where it was stopped.

CMD_FLUSH This command aborts all 1/O requests, returning all pending I/O requests with an error message.

CMD_NONSTD Any nonstandard commands begin here. Non standard commands are designated as CMD_NONSTD+0, CMD_NONSTD-+1, and so on.

CMD_INVALID This is a command to which the device should not respond.

Performing I/O

In Exec, I/O is always performed using I/O request blocks. Before I/O is performed, the request block must be properly initialized by both the system and the user. Once this has been done, normal I/O may commence.

PREPARATION

Devices are identified within the system by name (a null-terminated character string). Device units are usually identified by number. The OpenDevice() function maps the device name to an actual device and then calls the device to perform its initialization. The device will map the unit number into an internal form for later use. Both Exec and the device driver will initialize the I/O request passed to OpenDevice().

For example, OpenDevice(‘“‘trackdisk.device’’,1,ior,0) will attempt to open unit one of the floppy disk device, mapping its symbolic name into the address of a device data structure. It also sets up a few internal fields of the request. OpenDevice() will return a zero if it was successful and a nonzero error number if it was not.

Input/Output 45

SYNCHRONOUS REQUESTS

Synchronous I/O requests are initiated with the DoIO() function mentioned earlier. DoIO() will not return control until the request has completed. Because the device may respond to a request immediately or queue it for later action, an undetermined amount of time may pass before control is returned. With this type of I/O, only one request is serviced at a time.

To perform synchronous I/O, the I/O request block must be prepared as described in the previous section. In addition, io_Message, io_Command, and perhaps other fields must be initialized.

The io_Message field is set up in the same manner as a message. This is described in the “‘Messages and Ports” chapter.

The io_Command field is set to the desired command. For example:

ior->io_Command = CMD_RESET; DolO (ior);

performs a reset command.

More involved commands require other fields to be initialized. For example, the com- mands to read a sector from a disk might look something like the following:

ior->io_Command = CMD_READ; ior->io_Length = TD_SECTOR; ior->io_Offset = 20 * TD_SECTOR; ior->io_Data = buffer;

DolIO (ior);

When the request has completed, the request block is returned with the command results. If an error occurred, DoIO() will return the error number. The error number is also indicated in the io_Error field of the request.

46 Input/Output

ASYNCHRONOUS REQUESTS

More efficient programs can take advantage of the multitasking characteristics of the I/O system by using asynchronous I/O, which allows many requests to be performed at the same time. This type of I/O is supported by the SendIO(), WaitIO(), CheckIO(), BeginIO(), and AbortIO() functions. Asynchronous I/O requests will return almost immediately to the user regardless of whether the request has actually completed. This lets the user maintain control while the I/O is being performed. Multi- ple I/O requests can be posted in this fashion.

In the disk read example above, asynchronous I/O could be performed by changing the DoIO() call to a SendIO():

ior->io_Command = CMD_READ; ior->io_Length = TD_SECTOR; ior->io_Offset = 20 * TD_SECTOR; ior->io_Data = buffer;

SendIO (ior);

From the time the I/O has been initiated to the time it completes, the request block should not be directly accessed by the program. The device can be said to “own” the request block. Only after the request has completed or successfully aborted should your program access it.

When the I/O completes, the device will return the I/O request block to the reply port specified in its io_Message field. After this has happened, you know that the device has finished the I/O. The reply port used to receive the returned request can be set up to cause a task signal when the reply arrives. This technique lets a task sleep until the the request is complete. The WaitIO() function can be called to wait for the comple- tion of a previously initiated request.

WaitIO() will handle all of the interaction with the message reply port automatically. If you are using just the Wait() function, do not forget to remove the I/O request from your reply port with GetMsg(). Once this is done, the request may be reused.

The CheckIO() function is handy to determine if a particular I/O request has been satisfied. This function deals with some of the subtleties of I/O in the proper manner.

If you wish to queue several I/O requests to a device, you must issue multiple SendIO() requests, each with its own separately-opened request structure. This type of I/O is sup- ported by most devices. A task can also request I/O from a number of devices and then check later for their completion.

Input/Output 47

Exec also allows for certain types of optimization in device communication. One form of optimization, in which you call the device driver directly, is called quick I/O. This con- cept is discussed later in this chapter.

CONCLUSION

When a request has completed its I/O, access to the device should be concluded with CloseDevice(). This function will inform the device that no further I/O is to be per-

formed with this request. For every OpenDevice() there must be a corresponding CloseDevice().

QUICK 1/0

For some types of I/O, the normal internal mechanisms of I/O may present a large amount of overhead. This is mostly true for character-oriented I/O, in which each char- acter might be transferred with a separate I/O request. The overhead for such requests could significantly overload the I/O system, resulting in a loss of efficiency for the overall system.

To allow devices to optimize their I/O handling, a mechanism called quick I/O was created. In the IORequest data structure, one of the io_flags is reserved for quick I/O. When set prior to an I/O request, this flag indicates that the device is allowed to handle the I/O in a special manner. This enables some devices to take certain ‘‘short-cuts” when it comes to performing and completing the request.

The quick I/O bit (IOB_QUICK) allows the device to avoid returning the I/O request to the user via the message system (for example, via ReplyMsg()) if it can complete the request immediately. If the IOB-QUICK bit is still set at the end of the BeginIO() call, the request has already completed and the user will not find the I/O request on his reply port.

The DoIO() function normally requests the quick I/O option, whereas the SendIO() function does not. Complete control over the mode for quick I/O is possible by calling a device’s BeginIO() entry directly.

It is up to the device to determine whether it can handle a request marked as quick I/O. If the quick I/O flag is still set when the request has completed, the I/O was performed quickly. This means that no message reply occurred, so the message has not been queued to the reply port.

48 Input/Output

Standard Devices

The following standard system devices are normally available when the Amiga starts up. Each of these devices is described in Amiga ROM Kernel Reference Manual: Libraries and Devices.

Timer Provides a flexible way of causing task signals or interrupts at second and microsecond intervals.

Trackdisk Provides direct access to the 3 1/2-inch and 5 1/4-inch floppy disk drives. Among the functions provided are format, seek, read, and write. Nor- mally, trackdisk is used only by AmigaDOS; its functions are enumerated here for direct access where required.

Keyboard Handles raw information from the keyboard and converts it into input events that can be retrieved and interpreted. Keyboard input events are queued so that no keystrokes will be missed.

Gameport Handles raw information from the mouse or a joystick device. Gameport events are queued so that no movements will be missed. You can tell the system what type of device is connected and how often to check and report the current status of the device.

Input The input device combines requests from both the keyboard and the gameport device. Input events from both are merged into a single input event stream on a first-in-first-out basis.

Console The console device receives its input from the input device. The input por- tion of the console device is simply a handler for input events filtered by Intuition. It provides what might be called the “traditional’’ user

interface. Audio The audio device is provided to control the use of the audio channels. Narrator The narrator device is loaded from disk and uses the audio device to pro-

duce humanlike synthesized speech.

Serial The serial device is loaded from disk and initialized on being loaded. It controls serial communications buffering of the input/output, baud rate, and so on.

Input/Output 49

Parallel The parallel device is loaded from disk and initialized on being loaded. It controls parallel communications. The parallel device is most often used by a parallel printer driver.

Printer The printer device driver is loaded from disk. Printers that are supported as of this writing are specified in the ‘‘Printer Device Support Code” appendix of the Amiga ROM Kernel Reference Manual: Libraries and Devices.

Clipboard The clipboard device provides a means of “cutting” data from and “past- ing”’ data into applications.

50 Input/Output

Chapter 5

INTERRUPTS

Introduction

Exec manages the decoding, dispatching, and sharing of all system interrupts. This includes control of hardware interrupts, software interrupts, task-relative interrupts (see the “Tasks” chapter), and interrupt disabling/enabling. In addition, Exec supports a more extended prioritization of interrupts than that provided in the 68000.

Interrupts 51

The proper operation of multitasking depends heavily on the consistent management of the interrupt system. Task activities are often driven by intersystem communication that is originated by various interrupts. ,

SEQUENCE OF EVENTS

Before useful interrupt handling code can be executed, a considerable amount of hardware and software activity must occur. Each interrupt must propagate through several hardware and software interfaces before application code is finally dispatched:

1.

A hardware device decides to cause an interrupt and sends a signal to the interrupt control portions of the 4703 custom chip.

The 4703 interrupt control logic notices this new signal and performs two pri- mary operations. First, it records that the interrupt has been requested by set- ting a flag bit in the INTREQ register. Second, it examines the INTENA regis- ter to determine whether the corresponding interrupt and the interrupt master are enabled. If both are enabled, the 4703 generates a set of three 68000 interrupt request signals. See the Amiga Hardware Reference Manual for a more complete explanation of how this is done.

These three signals correspond to seven interrupt priority levels in the 68000. If the priority of the new interrupt is greater than the current processor priority, an interrupt sequence is initiated. The priority level of the new interrupt is used to index into the top seven words of the processor address space. The odd byte (a vector number) of the indexed word is fetched and then shifted left by two to create a low memory vector address.

The 68000 then switches into supervisor mode (if it is not already in that mode), and saves copies of the status register and program counter (PC) onto the top of the system stack. The processor priority is then raised to the level of the active interrupt.

From the low memory vector address (calculated in step three above), a 32-bit autovector address is fetched and loaded into the program counter. This is an entry point into Exec’s interrupt dispatcher.

Exec must now further decode the interrupt by examining the INTREQ and INTENA 4703 chip registers. Once the active interrupt has been determined, Exec indexes into an ExecBase array to fetch the interrupt’s handler entry point and handler data pointer addresses.

52 Interrupts

7. Exec now turns control over to the interrupt handler by calling it as if it were a subroutine. This handler may deal with the interrupt directly or may pro- pagate control further by invoking interrupt server chain processing.

You can see from the above discussion that the interrupt autovectors should never be altered by the user. If you wish to provide your own interrupt handler, you must use the Exec SetIntVector() function. Changing the content of any autovector location violates the design rules of the Multitasking Executive.

Task multiplexing usually occurs as the result of an interrupt. When an interrupt has finished and the processor is about to return to user mode, Exec determines whether task-scheduling attention is required. If a task was signaled during interrupt processing, the task scheduler will be invoked. Because Exec uses preemptive task scheduling, it can be said that the interrupt subsystem is the heart of task multiplexing. If, for some rea-

son, interrupts do not occur, a task might execute forever because it cannot be forced to relinquish the CPU.

INTERRUPT PRIORITIES

Interrupts are prioritized in hardware and software. The 68000 CPU priority at which an interrupt executes is determined strictly by hardware. In addition to this, the software imposes a finer level of pseudo-priorities on interrupts with the same CPU priority. These pseudo-priorities determine the order in which simultaneous interrupts of the same CPU priority are processed. Multiple interrupts with the same CPU priority but a different pseudo-priority will not interrupt one another.

Table 5-1 summarizes all interrupts by priority.

Interrupts 53

Table 5-1: Interrupts by Priority

4703 CPU Pseudo

Name Priority Priority Purpose

NMI 7 15 Nonmaskable

INTEN 6 14 Special (Copper) EXTER 6 13 8520B, external level 6 DSKSYNC 5 12 Disk byte

RBF 5 11 Serial input

AUD1 4 ' 10 Audio channel 1 AUD3 4 9 Audio channel 3 AUDO 4 8 Audio channel 0 AUD2 4 7 Audio channel 2

BLIT 3 6 Blitter done

VERTB 3 5 Vertical blank COPER 3 4 Copper

PORTS 2 3 8520A, external level 2 TBE 1 2 Serial output DSKBLK 1 1 Disk block done SOFTINT 1 0 Software interrupts

The 8520s {also called CIAs) are peripheral interface adapter chips. For more informa- tion about them, see Amiga Hardware Reference Manual.

As described in the Motorola 68000 programmer’s manual, interrupts may nest only in the direction of higher priority. Because of the time-critical nature of many interrupts on the Amiga, the CPU priority level must never be lowered by user or system code. When the system is running in user mode (multitasking), the CPU priority level must remain set at zero. When an interrupt occurs, the CPU priority is raised to the level appropriate for that interrupt. Lowering the CPU priority would permit unlimited interrupt recursion on the system stack and would “‘short-circuit”’ the interrupt-priority scheme.

Because it is dangerous on the Amiga to hold off interrupts for any period of time, higher-level interrupt code must perform its business and exit promptly. If it is neces- sary to perform a time-consuming operation as the result of a high-priority interrupt, the operation should be deferred either by posting a software interrupt or by signalling a task. In this way, interrupt response time is kept to a minimum. Software interrupts are described in a later section.

54 Interrupts

NONMASKABLE INTERRUPT

The 68000 provides a nonmaskable interrupt (NMI) of CPU priority 7. Although this interrupt cannot be generated by the Amiga hardware itself, it can be generated on the expansion bus by external hardware. Because this interrupt does not pass through the 4703 interrupt controller circuitry, it is capable of violating system code critical sections. In particular, it short-circuits the DISABLE mutual-exclusion mechanism. Code that uses NMI must not assume that it can access system data structures.

Servicing Interrupts

Interrupts are serviced on the Amiga through the use of interrupt handlers and servers. An interrupt handler is a system routine that exclusively handles all processing related to a particular 4703 interrupt. An interrupt server is one of possibly many system rou- tines that are invoked as the result of a single 4703 interrupt. Interrupt servers provide a means of interrupt sharing. This concept is useful for general-purpose interrupts. such as vertical blanking.

At system start, Exec designates certain 4703 interrupts as handlers and others as server chains. The PORTS, COPER, VERTB, BLIT, EXTER, and NMI interrupts are initial- ized as server chains; hence, each of these may execute multiple interrupt routines per each interrupt. All other interrupts are designated as handlers and are always used exclusively.

DATA STRUCTURE

Interrupt handlers and servers are defined by the Exec Interrupt structure. This struc- ture specifies an interrupt routine entry point and data pointer. The C definition of this structure is as follows:

struct Interrupt { struct Node is_Node; APTR is_Data; VOID (*is_Code)(); }5

Once this structure has been properly initialized, it can be used for either a handler or a server.

Interrupts 55

ENVIRONMENT

Interrupts execute in an environment different from that of tasks. All interrupts execute in supervisor mode and utilize a single system stack. This stack is large enough to han- dle extreme cases of nested interrupts (of higher priorities). Obviously, interrupt pro- cessing has no effect on task stack usage.

All interrupt processing code, both handlers and servers, is invoked as assembly code subroutines. Normal assembly code CPU register conventions dictate that the DO, D1, AO, and Al registers be free for scratch use. In the case of an interrupt handler, some of these registers also contain data that may be useful to the handler code. See the section on handlers below.

Because interrupt processing executes outside the context of most system activities, cer- tain data structures will not be self-consistent and must be considered off limits for all practical purposes. This happens because certain system operations are not atomic in nature and may be interrupted only after executing part of an important instruction sequence. For example, memory allocation and deallocation routines forbid task switch- ing but do not disable interrupts. This results in the finite possibility of interrupting a memory-related routine. In such a case, a memory linked list may be inconsistent when examined from the interrupt code itself. To avoid serious problems, the interrupt rou- tine must not use any of the memory allocation or deallocation functions.

INTERRUPT HANDLERS

As described above, an interrupt handler is a system routine that exclusively handles all processing related to a particular 4703 interrupt. There can only be one handler per 4703 interrupt. Every interrupt handler consists of an Interrupt structure (as defined above) and a single assembly code routine. Optionally, a data structure pointer may also be provided. This is particularly useful for ROM-resident interrupt code.

An interrupt handler is passed control as if it were a subroutine of Exec. Once the handler has finished its business, it must return to Exec by executing an RTS (return from subroutine) instruction rather than an RTE (return from exception) instruction. Interrupt handlers should be kept very short to minimize service-time overhead and thus minimize the possibilities of interrupt overruns. As described above, an interrupt handler has the normal scratch registers at its disposal. In addition, AS and A6 are free for use. These registers are saved by Exec as part of the interrupt initiation cycle.

For the sake of efficiency, Exec passes certain register parameters to the handler (see the

list below). These register values may be utilized to trim a few microseconds off the exe- cution time of a handler.

56 Interrupts

DO

D1

AO

Al

A5

A6

is scratch and contains garbage.

is scratch but contains the 4703 INTENAR and INTREQR registers values ANDed together. This results in an indication of which interrupts are enabled and active.

points to the base address‘of the Amiga custom chips. This information is use- ful for performing indexed instruction access to the chip registers.

points to the data area specified by the is_Data field of the Interrupt struc- ture. Because this pointer is always fetched (regardless of whether you use it), it is to your advantage to make some use of it.

is used as a vector to your interrupt code. It is free to be used as a scratch register, and it is not necessary to restore its value prior to returning.

points to the Exec library base (SysBase). You may use this register to call Exec functions or set it up as a base register to access your own library or dev- ice. It is not necessary to restore this register prior to returning.

Interrupt handlers are established by passing the Exec function SetIntVector() your initialized Interrupt structure and the 4703 interrupt bit number of interest. The parameters for this function are as follows:

INTB_RBF

This is the bit number for which this interrupt server is to respond. Other pos- sible bits for interrupts are defined in hardware/intbits.h.

RBF Interrupt

This is the address of an interrupt server node as described earlier in this chapter.

Keep in mind that certain interrupts are established as server chains and should not be accessed as handlers.

Here is a C code example of proper handler initialization and set-up:

Interrupts 57

#include ”exec/types.h” #include ”exec/memory.h” #include ”exec/interrupts.h” #include ”hardware/custom.h” #include ”hardware/intbits.h”

extern void RBFHandler();

extern struct Interrupt *SetIntVector();

extern struct Custom custom; /* get base of custom chips */ struct Interrupt *RBFInterrupt, *PriorInterrupt;

/* To try this, user must have a 9600 baud terminal connected to serial * port and run this from a newcli window and must have a separate

* way to view buffer contents as characters arrive. The Wait(0) is

* used merely to make this a runnable example demonstrating setting

* the vector. If the setup routine ever exits, various pointers

-* become invalid (Buffer, In_Name) and there is no checking for

buffer overflow included either. */

main() setup();

setup()

short *Buffer; /* allocate an Interrupt node structure: */

RBF interrupt = (struct Interrupt *) AllocMem (sizeof(struct Interrupt), MEMF_PUBLIC); if (RBF Interrupt == 0) { printf ("not enough memory for interrupt handler”); exit (100);

/* allocate an input buffer: */

Buffer = (short *)AllocMem (512, MEMF_PUBLIC);

if (Buffer == 0) { FreeMem (RBF Interrupt, sizeof(struct Interrupt)); printf ("not enough memory for data buffer”); exit (100);

printf(” Address of buffer is: %lx\n”, Buffer);

/* initialize the Interrupt node: */

58 Interrupts

RBFinterrupt->is_Node.In_Type = NT_LINTERRUPT; RBFInterrupt->is_Node.In_Pri = 0; RBFinterrupt->is_Node.In_Name = ”RBF-example’; RBFlinterrupt->is_Data = (APTR)&Buffer; RBFIinterrupt->is_Code = RBFHandler;

/* enable the RBF interrupt if not already enabled */ custom.intena = INTF_SETCLR | INTF_RBF;

/* put the new interrupt handler into action: * / PriorInterrupt = SetIntVector (INTB_RBF, RBF Interrupt);

if (PriorInterrupt != 0) { printf ("we just replaced the %s interrupt handler”, PriorInterrupt->is_Node.lIn_Name);

Wait(0); /* wait forever, ("illustrative example only”... if it exits, * pointer to Buffer and In_Name will become invalid) */ }

In this example, note the correct initialization of the Node structure.

The external interrupt handler code used above, RBF Handler, grabs the input charac- ter from the serial port and stores it into the buffer. Notice that the address of the buffer is passed to the handler (shown below) via the is_Data pointer. This pointer is updated for every character stored.

XDEF _RBFHandler

_RBF Handler: MOVE.L (A1),A5 sset buffer pointer MOVE.W SERDATR(AO),(A5)+ sstore the input word MOVE.W #INTF_RBF,INTREQ(AO) sclear the interrupt MOVE.L A5,(A1) srestore new buffer pointer RTS sreturn to exec END

In this example, the buffer holds complete 4703 serial data words that contain not only the input character, but special serial input flags as well (for example, data overrun). This data word is deposited directly into the buffer, and the 4703 RBF interrupt request is cleared. A more sophisticated example might perform various tests on the input word prior to storing it into the buffer.

Interrupts 59

INTERRUPT SERVERS

As mentioned above, an interrupt server is one of possibly many system interrupt rou- tines that are invoked as the result of a single 4703 interrupt. Interrupt servers provide an essential mechanism for interrupt sharing.

Interrupt servers must be used for PORTS, COPER, VERTB, BLIT, EXTER, or NMI interrupts. For these interrupts, all servers are linked together in a chain. Every server in the chain will be called until one returns with the Z bit of the 68000’s condition code register clear (indicating a non-zero result). If the interrupt was specifically for your server, you should return to Exec with the Z bit of the condition codes clear so that the whole chain does not have to be searched to find the interrupt. Note that VERTB servers (that is, servers that are bound to vertical blank) should always return with the Z bit set. Note that this is different from the normal calling convention (with the result in DO) to save time during time-critical] interrupts.

The easiest way to set the condition code register is to do an immediate move to the DO register as follows:

InterruptNotProcessed: MOVEQ #0,D0 RTS

InterruptProcessed: MOVEQ #1,D0 RTS

The same Exec Interrupt structure used for handlers is also used for servers. Also, like interrupt handlers, servers must terminate their code with an RTS instruction.

Interrupt servers are called in priority order. The priority of a server is specified in its is_Node.In_Pri field. Higher-priority servers are called earlier than lower-priority servers. Adding and removing interrupt servers from a particular chain is accomplished with the Exec AddIntServer() and RemIntServer() functions. These functions require you to specify both the 4703 interrupt number and a properly initialized Interrupt structure.

Servers have different register values passed than handlers do. A server cannot count on

the DO, D1, or A6 registers containing any useful information. A server is free to use DO-D1 and AO-A1/A5 as scratch.

60 Interrupts

In a server chain, the interrupt is cleared automatically by the system. Having a server clear its interrupt is not recommended and not necessary (clearing could cause the loss of

an interrupt on PORTS or EXTER).

Here is an example of a program to set up and clean up a low-priority vertical blank interrupt server:

/* vertb.c */

#include ”exec/types.h” #include ”exec/memory.h” #include ”exec/interrupts.h” #include ”hardware/custom.h” #include ”hardware/intbits.h”

struct Interrupt *VertBIntr; long count; /* To try this program, save as “‘vertb,”? then type “‘vertb” to run it. * If you type “run vertb,” the program won’t be connected to a CLI and * it will not be possible to send “q’’ to it to stop the program. * Compiling info: lc2 -v (disable stack checking so no need to use Ic.lib) * Linking info: Astartup.obj, vertb.c, vb.obj, amiga.lib * main()

extern void VertBServer();

/* allocate an Interrupt node structure: */ VertBintr = (struct Interrupt *) AllocMem (sizeof(struct Interrupt), MEMF_PUBLIC); if (VertBIntr == 0) { printf ("not enough memory for interrupt server” ); exit (100);

/* initialize the Interrupt node: */ VertBintr->is_Node.In_Type = NT_LINTERRUPT; VertBlIntr->is_Node.lIn_Pri = -60; VertBIntr->is_Node.In_Name = ”VertB-example’; VertBintr->is_Data = (APTR)&count; VertBlintr->is_Code = VertBServer;

/* put the new interrupt server into action: */

AddIntServer (INTB_VERTB, VertBlIntr); printf(” Type q to quit... reports how many vblanks since start\n”);

Interrupts 61

while (getchar () !=’q’); /* wait for user to type ’q’ */

RemIntServer (INTB_VERTB, VertBiIntr); printf (”%ld vertical blanks occurred\n”, count); FreeMem (VertBiIntr, sizeof(struct Interrupt));

}

The VertBServer might look something like this:

XDEF _VertBServer

_VertBServer: MOVE.L A1,A0 ___ sget address of count ADDQ.L #1,(A0) ;increment value of count MOVEQ.L +#0,D0 _ ;continue to process other vb-servers RTS END

Software Interrupts

Exec provides a means of generating software interrupts. This type of interrupt is useful for creating special-purpose asynchronous system contexts. Software interrupts execute at a priority higher than that of tasks but lower than that of hardware interrupts, so they are often used to defer hardware interrupt processing to a lower priority. Software interrupts use the same Interrupt data structure as hardware interrupts. As described above, this structure contains pointers to both interrupt code and data.

A software interrupt is usually activated with the Cause() function. If this function is called from a task, the task will be interrupted and the software interrupt will occur. If it is called from a hardware interrupt, the software interrupt will not be processed until the system exits from its last hardware interrupt. If a software interrupt occurs from within another software interrupt, it is not processed until the current one is completed.

Software interrupts are prioritized. Unlike interrupt servers, software interrupts have only five priority levels: -32, -16, 0, +16, and +32. The priority should be put into the In_Pri field prior to calling Cause().

Software interrupts can also be caused by message port arrival actions. See the “Mes- sages and Ports” chapter.

62 Interrupts

Disabling Interrupts

As mentioned in the “‘Tasks” chapter, it is sometimes necessary to disable all interrupts when examining or modifying certain shared system data structures. Interrupt disabling is controlled with the DISABLE and ENABLE macros and the Disable() and Enable() C functions.

In some system code, there are nested disabled sections. This type of code requires that interrupts be disabled with the first DISABLE and not re-enabled until the last ENABLE. The system enable/disable macros and functions are designed to permit this sort of nesting. For example, if there is a section of system code that should not be interrupted, the DISABLE macro is used at the head and the ENABLE macro is used at the end.

Here is an assembly-code macro definition for DISABLE. This routine assumes that A6 holds a pointer to the base of the Exec library.

DISABLE MACRO MOVE.W #$4000,_intena ADDQ.B #1,IDNestCnt(A6) ENDM

DISABLE increments a counter, IDNestCnt, that keeps track of how many levels of disable have been issued up to now. Only 126 levels of nesting are permitted. Notice that interrupts are disabled before the [DNestCnt variable is incremented.

Similarly, the ENABLE macro will reenable macros if the last disable level has just been exited:

ENABLE MACRO SUBQ.B #.1,IDNestCnt(A6) BGE.S ENABLE@ MOVE.W #$C000,_intena ENABLE®Q: MEND

ENABLE decrements the same counter that DISABLE increments. Notice that interrupts are enabled after the IDNestCnt variable is decremented.

See the “Tasks” chapter for a better explanation of mutual exclusion using interrupt disabling.

Interrupts 63

Chapter 6

MEMORY ALLOCATION

Introduction

Areas of free memory are maintained as a special linked list of free regions. Each memory allocation function returns the starting address of a block of memory at least as large as the size that you requested to be allocated. Any memory that is linked into this system free list can be allocated by the memory allocation routines. The allocated memory is not tagged or initialized in any way unless you have specified, for example,

Memory Allocation 65

MEMF_CLEAR. Only the free memory area is tagged to reflect the size of the chunk that has been freed.

You should return allocated memory to the system when your task completes. As noted above, the system only keeps track of available system memory and has no idea which task may have allocated memory and not returned it to the system free list. If your pro- gram does not return allocated memory when its task exits, that memory is unavailable until the system is powered down or reset. This can be critical, especially when using graphics routines that often need large blocks of contiguous RAM space. Therefore, if you dynamically allocate RAM, make sure to return it to the system by using the FreeMem() or FreeEntry() routines described below.

When you ask for memory to be allocated, the system always allocates blocks of memory in even multiples of eight bytes. If you request more or less than eight bytes, your request is always rounded up to the nearest multiple of eight. In addition, the address at which the memory deallocation is made is always rounded down to the nearest even multiple of eight bytes.

Compatibility Note: Do not depend on this size! Future revisions of the system may require a different size to guarantee alignment of the requested area to a specific boun- dary. You can depend upon allocation being aligned to at least a longword boundary.

Using Memory Allocation Routines

Note: Do not attempt to allocate or deallocate system memory from within interrupt code. The “Interrupts” chapter explains that an interrupt may occur at any time, even during a memory allocation process. As a result, system data structures may not neces- sarily be internally consistent.

MEMORY REQUIREMENTS

You must tell the system about your memory requirements when requesting a chunk of memory. There are four memory requirement possibilities. Three of these tell where within the hardware address map memory is to be allocated. The fourth, MEMF_CLEAR, tells the allocator that this memory space is to be zeroed before the allocator returns the starting address of that space.

66 Memory Allocation

The memory requirements that you can specify are listed below:

MEMF_CHIP This indicates a memory block that is within the range that the special- purpose chips can access. As of this writing, this is the lowest 512K of the Amiga.

MEMF_FAST This indicates a memory block that is outside of the range that the special purpose chips can access. “FAST” means that the special-purpose chips cannot cause processor bus contention and therefore processor access will

likely be faster. The special-purpose chips cannot use memory allocated in this way.

MEMF_PUBLIC This indicates that the memory requested is to be used for different tasks or interrupt code, such as task control blocks; messages, ports, and so on. The designation MEMF_PUBLIC should be used to assure compatibility with future versions of the system.

MEMF_CLEAR This indicates that memory is to be cleared before returning.

If no preferences are specified, MEMF_FAST is assumed first, then MEMF_CHIP.

MEMORY HANDLING ROUTINES

Exec has the following memory allocation routines:

AllocMem() and FreeMem()

These are system-wide memory allocation and deallocation routines. They use a memory free-list owned and managed by the system.

AllocEntry() and FreeEntry() These are routines for allocating and freeing different-size and different-type memory blocks with a single call.

Allocate() and Deallocate()

These are routines that may be used within a user-task to locally manage a system-allocated memory block. You use these routines to manage memory yourself, using your own memory free lists.

Memory Allocation 67

SAMPLE CALLS FOR ALLOCATING SYSTEM MEMORY The following examples show how to allocate memory.

APTR mypointer,anotherptr; my pointer = (APTR)AllocMem(100, 0);

AllocMem() returns the address of the first byte of a memory block that is at least 100 bytes in size or null if there is not that much free memory. Because the requirement field is specified as 0, memory will be allocated from any one of the system-managed memory regions.

anotherptr = (APTR)AllocMem(1000,MEMF_CHIP | MEMF_CLEAR);

Memory is allocated only out of chip-accessible memory; zeroes are filled into memory space before the address is returned. If the system free-list does not contain enough con- tiguous memory bytes in an area matching your requirements and of the size you have requested, AllocMem() or Allocate() returns a zero.

SAMPLE FUNCTION CALLS FOR FREEING SYSTEM MEMORY

The following examples free the memory chunks shown in the earlier call to the system. allocation routines.

FreeMem(mypointer,100); FreeMem(anotherptr,1000);

Note: Because of the internal operations of the allocator, your allocation request may result in an allocation larger than the number of bytes you requested in the first place. However, the FreeMem() routine adjusts the request to free memory in the same way as AllocMem() adjusts the size, thereby maintaining a consistent memory free-list.

The routine FreeMem() returns no status. However, if you attempt to free a memory

block in the middle of a chunk that the system believes is already free, you will cause a system crash.

68 Memory Allocation

ALLOCATING MULTIPLE MEMORY BLOCKS

Exec provides the routines AllocEntry() and FreeEntry() to allocate multiple memory blocks in a single call. AllocEntry() accepts a data structure called a MemList, which contains the information about the size of the memory blocks to be allocated and the requirements, if any, that you have regarding the allocation. The MemList structure is found in the include file exrec/memory.h and is defined as follows:

struct MemList {

struct Node ml_Node; UWORD ml_NumEntries; /* number of MemEntrys */ struct MemEntry ml_me[1]; /*where the MemEntrys begin*/ }3 where: Node

allows you to link together multiple MemLists. However, the node is ignored by the routines AllocEntry() and FreeEntry().

ml_NumEntries tells the system how many MemEntry sets are contained in this MemList. Notice that a MemList is a variable-length structure and can contain as many sets of entries as you wish.

The MemEntry structure looks like this:

struct MemEntry {

union { ULONG meu_Regqs;/* the AllocMem requirements */ APTR meu_Addr;/* address of your memory */ }me_Un;

ULONG me_Length; /* the size of this request */

}s

#:define me_Reqs me_Un.meu_Reqs #-define me_Addr me_Un.meu_Addr

Memory Allocation 69

Sample Code for Allocating Multiple Memory Blocks

#include ”exec/types.h” #include ”exec/memory.h”

struct MemList *mymemlist; /* pointer to a MemList */ /* define new structure because C cannot initialize unions */ struct myneeds {

struct MemList mn_head; /* one entry in the header */ struct MemEntry mn_body/{3}; /* additional entries follow

* directly as part of * same data structure */ } myneeds;

myneeds.mn_head.ml_NumEntries = 3; myneeds.mn_body([0].me_Reqs = MEMF_PUBLIC; myneeds.mn_body(0].me_Length = 104; myneeds.mn_body[1].me_Reqs=MEMF_FAST|MEMF_CLEAR; myneeds.mn_body[1].me_Length = 8000; myneeds.mn_body([2].me_Reqs=MEMF_CHIP | MEMF_CLEAR; myneeds.mn_body([2].me_Length = 256;

mymemlist = (struct MemList*)AllocEntry( &myneeds );

/* saying "struct MemEntry mn_body{3]” is simply * a way of adding extra MemEntry structures

* contiguously at the end of the first such

* structure at the end of the MemList. Thus

* members of the MemList of type MemEntry can * be referenced in C as additional members of

* the ”me| ]” data structure.

a

AllocEntry() returns a pointer to a new MemList of the same size as the MemList that you passed to it. For example, ROM code can provide a MemList containing the requirements of a task and create a RAM-resident copy of the list containing the addresses of the allocated entries.

70 Memory Allocation

Result of Allocating Multiple Memory Blocks

The MemList created by AllocEntry() contains MemEntry entries. MemEntrys are defined by a union statement, which allows one memory space to be defined in more than one way.

If AllocEntry() returns a value with bit 31 clear, then all of the meu_Addr positions in the returned MemList will contain valid memory addresses meeting the requirements you have provided.

To use this memory area, you would use code similar to the following:

struct MemList *ml; APTR mydata, moredata;

; (((ml & (1<<31)) < 0)

mydata = ml->ml_me[0].me_Addr; moremydata = ml->ml_me[1].me_Addr;

} else

exit (200); /* error during AllocEntry */

If AllocEntry() has problems while trying to allocate the memory you have requested, instead of the address of a new MemList, it will return the memory requirements value with which it had the problem. Bit 31 of the value returned will be set, and no memory will be allocated. Entries in the list that were already allocated will be freed.

Memory Allocation and Tasks

If you want your task to cooperate fully with Exec, use the MemList and AllocEntry() facility to do your dynamic memory allocation.

In the task control block structure, there is a list header named tc_MemEntry. This is the list header that you initialize to point to the MemLists that your task has created by call(s) to AllocEntry(). Here is a short program segment that handles task memory list header initialization only. It assumes that you have already run AllocEntry() as shown in the simple AllocEntry() example above.

Memory Allocation 71

struct Task *tc; struct MemList *ml]; NewList( tc->.tc_MemEntry ); /* Initialize the task’s * memory list header */

AddTail( tc->.tec_MemEntry, ml );

Assuming that you have only used the AllocEntry() method (or AllocMem() and built your own custom MemList), your task now knows where to find the blocks of memory that your task has dynamically allocated. If your clean-up routine (the task’s finalPC routine) finds items on the tc_MemEntry list when RemTask( &mytask ) is exe- cuted, your routine can wind through all linked lists of MemLists and return all allo- cated memory to the system free-list.

MEMORY ALLOCATION AND MULTITASKING

To make sure that you are working effectively in the multitasking system as a cooperat- ing task, you can do one of the following:

o Globally allocate and free memory blocks by using AllocMem() and FreeMem(), adding each block when allocated and deleting each when it is freed.

o Allocate one or more blocks of memory from the system global pool using AllocEntry() when your task begins and then manage those blocks internally using Allocate() and Deallocate().

MANAGING MEMORY WITH ALLOCATE() AND DEALLOCATE()

Allocate() and Deallocate() use a memory region header, called MemHeader, as part of the calling sequence. You can build your own local header to manage memory locally. This structure takes the form:

72 Memory Allocation

struct MemHeader { UWORD mh_Attributes; /* characteristics * / struct MemChunk *«mb_First;/* first free region */ APTR mbh_Lower; /* lower memory bounds */ APTR mh_Upper; /* upper memory bounds + 1 */ ULONG mb_Free; /* number of free bytes */

}3 where

mh_Attributes is ignored by Allocate() and Deallocate().

mh_First is the pointer to the first MemChunk structure.

mh_Lower is the lowest address within the memory block. This must be a multiple of eight bytes.

mh_Upper is the highest address within the memory block + 1. The highest address will itself be a multiple of eight if the block was allocated to you by AllocMem().

mh_Free is the total free space.

This structure is included in the include-files exec/memory.h and ezec/memory.t. The following sample program fragment shows the correct initialization of a

MemHeader structure. It assumes that you wish to allocate a block of memory from the global pool and thereafter manage it yourself using Allocate() and Deallocate().

Memory Allocation 73

struct MemHeader memheader; APTR myblock;

struct MemChunk { struct MemChunk *mc_Next; ULONG mc_bytes;

}3 struct MemChunk *me;

/* get a block from the system */ myblock = (APTR) AllocMem( 8000, MEMF_PUBLIC | MEMF_CLEAR );

memheader.mh_Lower = myblock; memheader.mh_First == (ULONG) myblock; memheader.mh_Upper == (ULONG)myblock + 8000;

/* takes 8 bytes for the memory chunk headers that tag free memory */ memheader.mh_Free = 8000 - (sizeof (struct MemChunk) );

/* initialize the free memory list */ mc = (struct MemChunk *) myblock; me->mc_Next = NULL; me->me_Size = memheader.mh_F ree;

/* now mymemhead is ready to use with * calls to Allocate( &memheader, size ); * or Deallocate( &memheader, size ); */

Note that only free memory is “tagged” using a MemChunk linked list. Once memory is allocated, the system has no way of determining which task now has control of that memory.

If you allocate a large chunk from the system, you can assure that in your finalPC rou- tine (specified when you perform AddTask()) you deallocate this large chunk as your task exits. Thus, local memory allocation and deallocation from a single large block can perhaps save some bookkeeping—that which might have been required if you had exten- sively used AllocMem() and FreeMem() instead. This can most easily be done by recording the allocated block in your task’s te_MemEntry structure.

74 Memory Allocation

Chapter 7

LIBRARIES

Using a properly designed machine code interface, it is possible to call any of the system routines without knowing in advance its absolute location in the system. This chapter shows how libraries are designed and used but does not cover the internal library struc- ture. For more information, see the “Library Base Offsets” appendix.

Libraries 75

What Is a Library?

A library is a collection of jump instructions, a system library node, and a data segment. System library conventions require that each code vector occupy six bytes. The size and content of a library node is specified below in the topic titled “Structure of a Library Node.” The size of the data segment varies, depending on the needs of the library itself.

How To Access a Library

You must perform two steps to access a library that is already initialized. First, you must open the library. Second, you must access the jump instructions or data by speci- fying an offset (negative or positive) from the library base pointer returned by OpenLibrary(). This form of indirection allows you to develop code that is not depen- dent on the absolute locations of the system routines. Note that in the same release of an Exec kernel, it is possible that routines can have different addresses. This depends, for example, on whether the hardware options are different or if the user asks for a different configuration. Therefore, accessing the system routines through library calls is the most expedient way of assuring that your code will work on different machines.

OPENING A LIBRARY

You prepare a library for use by calling the routine OpenLibrary(). This call takes the form

LibPtr = OpenLibrary(LibName, Version)

DO Al DO where LibPtr is a pointer value that is nonzero if the requested library has been located. Be sure to check that the returned value is nonzero before attempting to use LibPtr. If it is zero, the open failed. LibName

is a pointer to a string variable (null-terminated) that contains the name of the library that you wish to open.

76 Libraries

Version is the version number of the library that you expect to use. Libraries of the same name will be compatible with previous versions. However, if you specify a newer version than is present, the open will fail. Use the value 0 if you simply want “‘any” version of the named library.

The routine OpenLibrary() causes the system to search for a library of that name within the system library list. If such an entry is found, the library’s open-entry routine is called. If the library is not currently RAM-resident, AmigaDOS will search the direc- tory currently assigned to LIBS:. If that library is present, it will be loaded, initialized, and added to the system library list. If the library allows you access, the library pointer will be returned in LibPtr.

USING A LIBRARY TO CALL A ROUTINE

A typical way to use the library interface once a library has been opened is to use assem- bly language code as follows. Note that this save/restore is necessary only if A6 does not already contain the correct value.

move.l A6,-(SP) ssave current contents of A6 move.l <libptr>,A6 smove library pointer into A6 jsr _LVO<routineName>(A8) sthrough library vector table move.l (SP)+,A6 srestore A6 to original value

The example above is the actual assembly code generated by the use of a machine language macro named LINICLIB:

LINKLIB functionOffset, libraryBase where

functionOffset is “_LVO” followed by the name of the routine as called from C.

libraryBase is the address of the base of the library.

For example,

LINKLIB _LVODisplayBeep,IntuitionBase

Libraries 77

produces the same code sequence as shown above. This macro is located in the file exec/libraries.k. Notice that it handles only the linkage to the routine. It does not save any registers or preload any registers for passing values to the routine. Negative offsets, in multiples of six bytes, access the code vectors within the library.

By convention, A6 must contain the library pointer when a library routine is called. This allows any library routine to locate the library and access its data or any of its other entry points. Registers AO, Al, DO, and D1 may be used as scratch registers by any routine. All other registers, both address and data, if used in a routine, should be saved and restored before exit.

USING A LIBRARY TO REFERENCE DATA

You can use the LibPtr to reference a data segment associated with a library by speci- fying a positive offset from LibPtr, such as:

movel <libptr>,Al 3; Move library base move. <offset>(A1),D0_ ; Retrieve data located at <offset>

Library data is not usually accessed directly from outside of a library, but rather is accessed by the routines that are part of the library itself. The sample code retrieves data specifically associated with that library. Note that different languages have different interface requirements. This example shows only a typical assembly language interface. When you design your own libraries, you may decide how the associated data segment is to be used. The system itself places no restrictions on its use.

CACHING LIBRARY POINTERS

To make your library calls more efficient, you may cache various pointers if you wish. These pointers are are the libPtr itself (because the library node, while it is open, may not be moved) and the address within the library at which a jump instruction is located (because offsets from the libPtr do not change). You should not, however, cache the jump vector from within the library. You will always expect to be calling the current library routine and therefore should not cache the jump vector.

78 Libraries

CLOSING A LIBRARY

When your task has finished using a specific library, your program should call the rou- tine CloseLibrary(). This call takes the form:

CloseLibrary(libPtr) Al

where libPtr is the value returned to you by the call to OpenLibrary/().

You close a library to tell the library manager that there is one fewer task currently using that library. If there are no tasks using a library, it is possible for the system, on request, to purge that library and free up the memory resources it is currently using. Each successful open should be matched by exactly one close. Do not attempt to use a library pointer after you have closed that library.

Adding a Library

You can add your own library to the system library list, provided that it is constructed as indicated below. You add a library to the system by using the AddLibrary() func- tion. The format of the call to this function is as follows:

AddLibrary(libPtr) Al

This command links a new library to the system and makes it available to all tasks.

MAKING A NEW LIBRARY

A function called MakeLibrary() is a convenient way for you to construct a library. After running MakeLibrary(), you will normally add that library to the system library list.

libAddr = MakeLibrary(vectors, structure, init, dataSize, SegList) DO AO Al A2 D0 D1 AddLibrary(libAddr) Al

Libraries 79

MakeLibrary() allocates space for the code vectors and data area, initializes the library node, and initializes the data area according to your specifications. Its parameters have the following meanings:

vectors This is a pointer to a table of code pointers terminated with a-1. vectors must specify a valid table address.

structure This parameter points to the base of an InitStruct() data region. That is, it points to the first location within a table that the InitStruct() routine can use to initialize various memory areas. InitStruct() will typically be used to initial- ize the data segment of the library, perhaps forming data tables, task control blocks, I/O control blocks, etc. If this entry is a 0, then InitStruct() is not called.

init This parameter points to a routine that is to be executed after the library node has been allocated and the code and data areas have been initialized. When this routine is called, the libAddr (address of this library) is placed into data regis- ter DO. If init is zero, no init routine is called.

dataSize This variable specifies the size of the data area to be reserved for the library. It includes the standard library node data as well as the reserved data area itself.

SegList This is a pointer to the AmigaDOS memory segment list (for libraries loaded by DOS).

MINIMUM SUBSET OF LIBRARY CODE VECTORS

The code vectors of a library must include at least the following entries: OPEN, CLOSE, EXPUNGE, and one reserved entry.

OPEN is the entry point called when you use the command OpenLibrary(). In the system libraries, OPEN increments the library variable OpenCnt. This variable is also used by CLOSE and EXPUNGE.

CLOSE is the entry point called when you use the command CloseLibrary(). It decrements the library variable OpenCnt and may do a delayed EXPUNGE.

80 Libraries

EXPUNGE

prepares the library for removal from the system. This often includes deallocating memory resources that were reserved during initialization. EXPUNGE not only frees the memory allocated for data structures, but also the areas reserved for the library node itself.

The remaining vector is reserved for future use. It should always return zero.

STRUCTURE OF A LIBRARY NODE

A library node contains all of the information that the system needs to manage a library. Here is the library structure as it appears in the ezec/libraries.h include file:

struct Library {

struct UBYTE UBYTE UWORD UWORD UWORD UWORD APTR ULONG UWORD

}3

Node libNode; lib_F lags; lib_pad; lib_NegSize; lib_PosSize; lib_ Version; lib_Revision; lib_IdString; lib_Sum; lib_Open_Cnt;

#define LIBF_SUMMING (1 << 0)

#define LIBF_CHANGED (1 << 1)

#define LIBF_SUMUSED (1 << 2)

#define LIBF_DELEXP (1 << 3)

/* link into the system library list */ /* flag variables */

/* unused */

/* size of jump vectors in bytes. */ /* data size */

/* checksum */ /* count how many tasks * have this library open */

/* meaning of the flag bits: */

/* bit position says some task * is currently running a * checksum on this library */

/* bit position says one or more entries * have been changed in the library * code vectors used by SumLibrary */

/* bit position says user wants a check- * sum fault to cause a system panic */

/* says there is a delayed expunge. * Some user has requested expunge but * another user still has the library open. */

Libraries 81

CHANGING THE CONTENTS OF A LIBRARY

After a library has been constructed and linked to the system library list, you can use the routine SetFunction() either to add or to replace the contents of one of the library vectors. The format of this routine is as follows:

SetFunction( Library, FuncOffset, FuncEntry) Al AO DO

where

Library is a pointer to the library in which a function entry is to be changed.

FuncOffset is the offset (negative) at which the entry to be changed is located.

FuncEntry is a longword value that is the absolute address of the routine that is to be inserted at the selected position in the library code vectors.

When you use SetFunction() to modify a function entry in a library, it automatically recalculates the checksum of the library.

Relationship of Libraries to Devices

A device is an interface specification and an internal data structure based on the library structure. The interface specification defines a means of device control. The structures of libraries and devices are so similar that the routine MakeLibrary() is used to con- struct both libraries and devices. Devices require the same basic four code vectors but have additional code vectors that must be located in specific positions in the code vector table. The functions that devices are expected to perform, at minimum, are shown in chapter 4, “Input/Output.” Also, a skeleton device (source code) is provided in the “Skeleton Device/Library Code” appendix of the Amiga ROM Kernel Reference Manual: Devices and Librartes.

82 Libraries

Chapter 8

ROM-WACK

Introduction

Wack is a keystroke-interactive bug exterminator used with Amiga hardware and software. ROM-Wack is a small, ROM-resident version primarily useful for system-crash data-structure examination. ROM-Wack’s command syntax and display formats are identical to Grand-Wack, of which it is functionally a subset. Grand-Wack includes both the ROM-resident and the remote versions of Wack.

ROM-Wack 83

Getting to Wack

ROM-Wack will be invoked by Exec automatically upon a fatal system error, or it can be explicitly invoked through the Exec Debug() function. Once invoked, communica- tion is performed through the RS-232-C serial data port at 9600 baud.

When a fatal system error occurs, Wack can be used to examine memory in an attempt to locate the source of the failure. The state of the machine will be frozen at the point in which the error occurred and Wack will not disturb the state of system beyond using a small amount of supervisor stack, memory between 200 and 400 hex, and the serial data port.

A program may explicitly invoke Wack by calling the Exec Debug() function. This is useful during the debug phase of development for establishing program breakpoints. For future compatibility, Debug should be called with a single, null parameter—for exam- ple, Debug(0). Please note however, that calling the Debug() function does not neces- sarily invoke ROM-Wack. If Grand-Wack or a user supplied debugger has been installed, it will be invoked in place of ROM-Wack.

When Wack is called from a program, system interrupts continue to process, but multi- tasking is disabled. Generally this is not harmful to the system. Your graphics will still display, keys may be typed, the mouse can be moved, and so on. However, many inter- rupts deposit raw data into bounded or circular buffers. These interrupts often signal related device tasks to further process these buffers. If too many interrupts occur, device buffers may begin to overflow or wrap around. You should limit the number of inter- rupt actions (typing keys on the Amiga keyboard for example) you perform while execut- ing in Wack.

Finally, certain system failures are so serious that the system is forced to reboot. Before rebooting takes place, the power LED will flash slowly. If you type a Del character (hex 7F) while the LED is flashing, the system will enter Wack before rebooting.

Keystrokes, Numbers, and Symbols

Wack performs a function upon every keyboard keystroke. In ROM-Wack, these func- tions are permanently bound to certain keys. For example, typing ‘““>” will immediately result in the execution of the next-word function. This type of operation gives a “‘keystroke-interactive” feel to most of the common Wack commands.

84 ROM-Wack

Whenever a key is pressed, it is mapped through a KeyMap, which translates it into an action. A key can have different meanings in different contexts. For simplicity, ROM- Wack applies keys consistently in all contexts (the Grand-Wack feature of arbitrary key binding is not available in ROM-Wack).

In the default keymap, most punctuation marks are bound to simple actions, such as displaying a memory frame, moving the frame pointer, or altering a single word. These actions are always performed immediately. In contrast, the keys A-Z, a-z, and 0-9 are bound to a function that collects the keys as a string. When such a string is terminated with <RETURN>, the keys are interpreted as a single symbol or number.

In ROM-Wack, symbols are treated only as intrinsic functions. Macros, constants, offsets, and bases are not supported. Hence, typing a symbol name will always result in the invocation of the symbol’s statically bound function.

If a string of keys forms a number, that number is treated as a hexadecimal value. If a string of keys is neither a number nor a known symbol, the message ‘“‘unknown symbol” is presented.

During the “collection” of a symbol or number string, typing a backspace deletes the previous character. Typing <CTRL-X> deletes the entire line.

Register Frame

When Wack is invoked for any reason, a register frame is displayed:

ROM-Wack

PC: FOOAB4 SR: 0000 USP: 001268 SSP: O7FFE8 TRAP: 0000 TASK: 0008B8

DR: 00000001 00000004 0000000C O00000AB4 00000001 0000001C 00000914 00000914 AR: 00000AB4 OOFO0D348 00011A80 O0000B8C O00OF20770 OOF20380 00000604

SF: 0000 OOFO OAB4 0014 O0OFO OAB4 0014 OOFO OAB4 0004 OOF O OAB4 0000 0004 0000

This frame displays the current processor state and system context from which you entered Wack. If you are familiar with the M68000 processor, most of this frame should be obvious: USP for user stack pointer, SSP for system stack pointer, etc.

The TRAP field indicates the trap number that forced us into Wack. Motorola uses the

term exceptions for these traps. In Exec, the term ezceptzon is used for asynchronous task events. The standard TRAP numbers are

ROM-Wack 85

QO normal entry

2 bus error

3 address error

4 illegal instruction

5 zero divide

6 CHK instruction (should not happen)

7 TRAPV instruction (should not happen)

8 privilege violation

9 trace (single step)

A line 1010 emulator

B line 1111 emulator

2N trap instruction N (2F normally for breakpoint)

The TASK field indicates the task from which the system entered Wack. If this field is zero, the system entered Wack from supervisor mode.

The SF line provides a backtrace of the current stack frame. This is often useful for determining the current execution context (last function called, for example). The user stack is displayed for entry from a task; the system stack for entry from supervisor

mode. (Note: Version 25.1 always shows the system stack, never the user stack. This will change.)

Display Frames

Wack displays memory in fixed size frames. A frame may vary in size from 0 to 64K bytes. Frames normally show addresses, word size hex data, and ASCII equivalent characters:

86 ROM-Wack

FO000C4 6578 6563 2E6C 6962 7261 7279 0000 4AFC exec.tlibrary... FOO00D4 OOFO 00D2 OOFO 2918 0019 0978 OOFO 00C4 ...........- ) "X.Y I x...

By default, Wack will pack as much memory content as it can onto a single line. Some- times it is preferable to see more or less than this default frame size. The frame size may be modified with :n. Here ‘‘n” represents the number of bytes (rounded to the next unit size) that will be displayed.

34

Fo000C4 6578 6563 exec

220

F000C4 6578 6563 2E6C 6962 7261 7278 0000 4AFC exec.tlibrar sy... FOO00D4 OOFO O0O0D2 OOFO 2918 0019 0978 OOFO 00C4 ............ XV Lees

A “:0” frame size is useful for altering the write-only custom chip registers.

Relative Positioning

Wack functions as a memory editor; nearly all commands are performed relative to your current position in memory. The following commands cause relative movement:

forward a frame

backward a frame > forward a word < backward a word

+n forward n bytes -n backward n bytes

<RETURN> redisplay current frame

<SPACE> forward a word

ROM-Wack 87

<BKSP> backward a word

An example of the use of these commands is provided below:

< RETURN > [00200 7072 6573 656e 7429 Od0a 0000 2028 6372 present )‘M’J...

f00210 6173 6820 2d20 6361. BeBe 6f74 2072 6563 ash © cannot 9

£00200 7072 6573 656e 7429 Od0a 0000 2028 6372 present )’M’J...

>

{00202 6573 656e 7429 Od0a 0000 2028 6372 6173 es ent )*M’J.... ( <

£00200 7072 6573 656e 7429 Od0a 0000 2028 6372 present )‘M J...

+24

£00224 290d 0a00 2028 626f 6f74 2064 6576 6963 )*M’J.. (boot d -38

f0O0lec 6c65 290d 0a00 2028 Bebf 2064 6562 7567 1 e )*M’J.. (no d

Absolute Positioning

There are a few commands that perform absolute positioning. Typing a hex number moves you to that position in memory:

10ec 0010ec OOfO 17c0 4ef9 OOfO 179a 4ef9 OOFO 1786 ....“W..N...... W..

Also, Wack maintains an indirection stack to help you walk down linked lists of absolute pointers:

4

000004 0000 lLlec OOfO Oa8e OOfO 0a90 OOTO OaG2® .... Qi. eee Ferree e Jevees [ (use current longword as the next address)

OOllec 0000 18f6 0000 1332 0900 OOfO O86a 0000 ....°X... 00 eS Sr LToweeccees ] (return to the previous "indirected” address)

000004 0000 llec OOfO Oa8e OOfO 0a90 OOTO OaGZ ....°Qi cee Jenn eee Jeeves

88 ROM-Wack

The find command finds a given pattern in memory, and the limit command determines the upper bound of the search. The pattern may be from one to four bytes in length. The pattern is not affected by the alignment of memory; that is, byte alignment is used for all searches regardless of the pattern size.

To set the upper bound for a find command, type an address followed by limit or ~ The default bound is 1000000 hex.

Altering Memory

The = command lets you modify your current memory word:

20134

020134 0000 0000 0000 .....e ence 020134 0000 = 767

020134 0767 0000 0000 *G g........

If the frame size is zero, the contents of the word will not be displayed prior to your modification of that word:

30 dfogec DFFO9C xxxx = 71f

If you decide not to modify the contents after typing a ==, press <RETURN> without typing a number. If you have already typed a number, type <CTRL-X>.

The alter command performs a repeated = which is handy for setting up tables. While

in this mode, the > and < will move you forward or backward one word. To exit from this mode, type a <RETURN> with no preceding number.

ROM-Wack 89

alter

001400 0280 = 222

001402 00C8 =<

001400 0222 = 333

001402 00C8 = 444

001404 0000 = 0

001406 3700 =>

001408 0000 = 68686

00140A 0000 = < RETURN >

You can modify registers when single-stepping or breakpointing. Typing ! followed by the register name (DO-D7, AO-A6), U) lets you make modifications. SR and SSP cannot be modified.

The fill command fills memory with a given pattern from the current location to an upper bound. The limit command determines the upper bound of the fill. The size of

the fill pattern determines the number of bytes the pattern occupies in memory. For ex- ample, typing

fill <RETURN> 45

fills individual bytes with the value 45. Typing

fill <RETURN> 045

fills words, and

fill <RETURN> 0000045

fills longwords.

Caution: Using the fill command without properly setting the limit can destroy data in memory. To set the upper bound for a fill, type an address followed by limit ora”.

90 ROM-Wack

Execution Control

These commands control program execution and system reset: go execute from current address

resume resume at current PC address

“D resume at current PC address

“I(tab) single instruction step

boot reboot system (cold-reset) ig reboot system (cold-reset) Breakpoints

ROM-Wack has the ability to perform limited program breakpoints. Up to 16 break- points may be set. The breakpoint commands are as follows:

set set breakpoint at current address

clear clear breakpoint at current address

show _ show all breakpoint addresses

reset clear all breakpoints

To set a breakpoint, position the address pointer to the break address and type set. Resume program execution with go or resume. When your breakpoint has been

reached, Wack will display a register frame. The breakpoint is automatically cleared once the breakpoint is reached.

ROM-Wack 91

Returning to Multitasking After a Crash

The user command forces the machine back into multitasking mode after a crash that invoked ROM-Wack. This gives your system a chance to flush disk buffers before you reset, thus securing your disk’s super-structures.

Once you type user, you cannot exit from ROM-Wack, so you should use this command

only when you want to reboot after debugging. Give your disk a few seconds to write out its buffers. If your machine is in serious trouble, the user command may not work.

92 ROM-Wack

Appendix A

C EXEC INCLUDE FILES

This appendix contains the C-language include files that define the system data struc- tures used by Exec routines.

This appendix is a printed copy of the Exec portion of the the SYS:includes directory on the Amiga C (Lattice C) disk.

/y Aszowaur ou ‘’ooTtTe eue[td ust

Aszouru ou ’A40d e7ee10

/~ edky,e6pen Nv JO woz Ateacocer /+ 204 Aoepeb umouwjun

Asowsu ou ‘dew3tgitd

seyduy a03 Around ou ‘4x83

Asouru ou ‘TITJ Poors

oy septsoi ou ‘sures oe

¥ oweu ou ‘surety DuUOCT

Asouwu ou ‘pesy AST{ s4eddoo

/* PeOTASAO AST[ SRzeTpoutequT seddoo /*= PeOTASAO ASTT Aeddoo

/, Ascusu ou “ASTI uoT_ONAAQsuT seddoo Aszowsu ou ‘Ast{ Aetdstp eddos

204N0S YLdw ue jo () }onaQSgQTUL SAeAJeS Adns4eqUT 403 AxroUreU OU /* 3ASTT Axoueu peqdnss0)

Axezaqy{T seu oq Axroureur ou

@mMTTeZ umsyoeyo AreaqrT

/* wMNSrD8eYo |sseqoexe

/~ WMSxOSYO 10}00A UOoTAdGSoxe 00089

£000T0V0X0 ZO000TORBX0 T0000000X0 T0000008X0 000000000

/y Avesaqry uotzyN{Auy

000000€0x0 /s

W000TOZ8X0 6000T0Z0X0 8000TO0Z8X0 LO000TOZBxX0 9000T0Z8xX0 S000TOZ8xX0 700000280 €0000078X0 7000TOZ8X0 TOOOTOZEX0 00000020X0

/~ Axeaqyt* sofydesb

L00000TS8X0 900000TSX0 S00000TS8X0 vO0000TSX0 £00000TSX0 700000TS8X0 TOO0000TSX0 000000T0X0

SOTTWUSII NY SUTJEpH quogezeetD NW euTJepH qebpeoped NV SUTJOpPH edAy{ebped NW SUTJOp# UOTFATNAUT NY SUTJOpH ------ +/

qt IssckeT NY SUTJOpH Axeaqrt'saeket ------ x/

deyntdiid NW SuTsyop# SeYyquL Axo] NV SUTJOpH TITAPOOTT NW SUTJOEPH eure tqj1OUS NW SUTJOp# oure1JOuoT NY SUTJopH peopq st 1dop Ny SuTsJep# 4J®AQISTTIGOD NY OUTJopH ABAQIST1doD NW eUTJEpH Aqsuldop Ny euTsop# Ketdstqdop Nw euTzJepH qr isotyde.zn NW melas ee ¥

AAMVATUI- NW eSUTJOp# WeWAUI NY SUTJOpH qdn.10QURW NY SUTJepH WwoNdTT NW SUTJepH UMNSYUOATT NW SUTJEpH umgy~ujesed NW SUT ep qOeazdox” NW SuTJop# Qt YOOX” NW SuTJop#

/ + ArteraqyT{*oexe ------ x/

[OEE E EERE EEN ERNE EN EERE EN EERE E ERE ERE ERE EED EERE EEE ESE ER EEE EERE EEEEY

¥#

SyeTy pug-peseg oTJTOedS x

¥

PTeTELECUCTCCICCCCCOCOCTOCCOCCOCTTCCOCCCECCCTCIOCICSCTCECOLIST TITEL Lily

TE080000X0 0€080000X0 770800000 TC080000X0 070800000 S$T080000X0 ¥T080000X0 €T080000X0 7T080000X0 TT080000X0 0T080000X0 600800000 80080000X0 £0080000X0 900800000 $0080000X0

Zz obeg ysquelte/oexe 986T 6P:bT T Ady

Youss{40mM” OW eUuTJEpH deajsjoog” OW SUuTJEpH DASYOSTW OW .SUTJEpPH OASWMISTC OW SUTJoEp# O4ASYVID OW eUTJEpH ASQASUT LT OW SUTJEpH ASTISTPOeAL OW SUTJEpH Aaqpseogkey Oy SuTJep# ASqqogeurey Oy SUTJOpt ABJeTOsUucD OY SUT EPH ASqgotpny OW euTJEpH

QT TUCOT OW eUTJep#

Qt Inve OW SUTJEpH

Qt ISOd OV eUuTJEpH QTTIstTD OW SuTsJop# qtTUIeW OW SUTJEpPH

%0080000X0 UOTIFNAUL OW SUTJOpH# €0080000x0 qryssuekeT oy eutzeptt Z0080000X0 qt IsoTudesg oy euTyop# T0080000x0 Q}]OSexY OW SuTjop# :sq0efqo quete ------ / 00009000x0 AOAAZOI OW SuTJopi 0000S000x0 seyuedg jy auTsept 0000%000x0 Aequedg Sy euTJop# 0000€000x0 qtquedg Sy euTsepH 0000Z000x0 QT TOW Sy SuTsJopH 0000T000x0 KiouPyON OW SuTyept Sepoo quete esodund [eseueb ------ y/

00000000x0 AzeACDeY Ly SUTIJEpH 00000008x0 pugpeeq Ly eutTjop# sedkA quete ------ x/

LJ eERRRESUUESEEEUUEEESEEEEEUUPEEEUUUEREEEURE EEUU ER EE UVERERUE EERE ERE EEE ¥

SISTW puy-peeg [Te1908uey ,

¥

OEE ERERENUUEEUUUUUUUUESUUSEREEEESESESESE ERP ERP EPROP ERE EE EEE EE EEE /

LJ eORRE SEER ER EEE SES ESEESERES ESE REV EF EP EE EEE EEE EES ERE RES ESE REE EEE E EEE ES [Te_ep S10ul SEe_WeOTpUuT :4A011g OTJTOSdS SeM JO11e Ot ZeEYUM SeVeoTpuT ATYHNoA +1011 [e49Usy ‘szequnu weysAsqns WOU sezeotput :prsAsqns HieTe pugqpesq -q

¥

¥

3

¥

¥

¥ prsfksqns_ |al ¥ ¥

¥ sAeqUMU AOIUS VWieTe SyA jo Wyeuwiogq , ¥

/

FEFFFEFFEEF FEF FPF FF EK FEF EEL EEE FEE FE ECE KE EEF EVE VEEL FEL EEE EERE ERE REE HE

/+ Setasks:esegooxg ut y/ (T>>T) MOWMLYATW aS SUuTJEpH J ORR RRRERES ESSE UUUEEEEEE RES EEUEEESPPEEERER ERE REP EVER EERE SPUR ERE RES ES $ :AsxS0T$

$ dxq [4e> HH:S0:ST 8Z/80/S8 O'T A’U'SQAETe : 4epeeHs

> TOAQUOD BOANHOS

aqcanac aa «

S¥ECEEEE ESE FERFS FSS FE SEES EFF ERE SEES PFE FEC HE FEE EF E EEL ESE EF ERPS KELP EEE ey ¥ SlTJI Spnpour eaTyAnocexg weqshs HuTWeszedo WOU -- “OUT ‘ebTuy-S10poUMIOD , ¥ CRREMERESEESEUEUEUSENUEREVEVEUEES EPP EPSP EUS USESES ES ERE VERE ENE EEE E SEES /

H SLYaTW OSXA SUTJOPH H SLUT OaXA JOPUZ TH

T obeg y'squele/Oexe O86T 6P:FT

ANMNPNOKR DA

A-2

/+ A018 Ue peUuNAZe.s SPOS 300q

/* 3TuUN SATQO’ OU :QdN.WEAQUT /+ ustp sey Apeaite :47uN 3205

/+ ysenbe. peq

/~« ATeM Jou, UO 40.118 :AeTOp

x/

+/

x/

H SLWATW OaXa JTPUSH E6T

Z6T

000000TEX0 YOUS*IIOM NW SUTJOPH TET /= Yousecpjtoy ------ x/ 06T

68T

T000000€x0 AOIIGIOOG NY SUTJEP# SST 0000000€x0 dersqs oog NW SUTJOpPH LET deaqs}00q ------ x/ 98T

SST

000000zzx0 OASYOSTW NW SUTJEpP# FST /~ QOMOSSL*OSTUl ------ x/ €8T

ZeT

ZOO000TZXO 3OWONIUTUC NW SUTJOPH TEST T00000TZx0 ASTASeHYC NW SUTJOPH OST 000000TZX0 OASY|ISTC NW SUTJEpH ELT /= SOANOSSeA’HSTP ------ x/ SLT

LLT

00000002X0 OASYWID NW SUTJOPH YLT /= POAMNOSEA* CTO ------ x/ SLT

PLT

T00000STX0 boypegnL NW SUTJSep# ELT 000000STX0 AQQASUT LNW SUTJOpPH# ZLT J+ S0}Aep: Jeu ------ x/ TL

OLT

Z00000bTX0 Aeleaadl NW SuTJep# 69T

y eg q'szieyte/ooxe 9861 6P:PT T Ady

[= AOLIB YVOS :GZEACFTES ¥/ TOOOOOPTXO PSESATIEOGL NV SUTISOPH

/y KeT4eao peq

/+ MHuer Jo Ano for

[= 40143 ASTP

WNSO9YS PTTPAUT

9023 Apeoute Aor

/~ 30nas00 deujtq

/~ 40128 SoueNbes YooTq »AsSTp /+ PeTTey CeAeerg

/* PEATEOSA Yorxoed peRoedxeun /= PaNTTeEZ W1GH

/+ 4,UPTP 4SeLpug

/~ Gnquejs ye Arousu ou

/~ S0FAepP STOSUOS SYA UedO 3, UPTNOD

/+ UoTSUeYyPidUOoUT Huysned oyoS piATem /+ Addl Aq peateoo.1 ebessou peq

/* UOFAZINAUT HuT4seque uanje1 oReRs peq AsAouScU OU ’MOpUTM UuedoO

Asouwul ou ‘szeHpeH mg ppe

/+ 20) umouwjun ‘useesz0s sAs uedo

/+ Asousul ou ‘SOTTe 4eqAser ‘usee10s uedo Asowsw ou ’use10s usedo

OA®ZTEY > dol xoq uBzT

/+ Asousul ou ‘ooTtTe suetd

Asourmul Ou ‘OOTTe qns

000000bTX0 ASMIST@IOeAL NW EUTJEP# /-= SOTASP*HSTpHOeAQ ------ x/ Q00000ETXO Aagpsreoqhey NW SUTJopH /= B0}fAep’ paeoghay ------ +/ 000000ZTX0 Aa zA0geureQ-NY SUTIEpH /~= SBOFAep’ quodeureH ------ y/ QO0000TTXO ABqeTOSUCD NY SUTJepH /~ QOTA@P'@TOSUOD ------ ¥/ Q000000TX0 ASQGOTPNY Ny SUuTJopH /+ S0TAep oTpNe ------ v/

00000060x0 QT TUOOT NY SUTJOpH /y Asesaqrtqytwera ------ / 00000080x0 Cty NW SuTsJep}# Axexqytqytwes ------ »/ 200000L0x0 Aelszsaopeg NY SUTJepH G00000L0x0 obueyAoy NY SUTJopH# W00000L0x0 AOAATISTG NW SUTJop# 600000L0x0 UNS>>Ope_ NW SUTJepH 800000L0x0 eoighey NY SUTJOpHt L£00000L0x0 deyita NW SuTzeptt 90000020x0 besgrtg4std Nw euTJep# S00000L0x0 DSAS21T NY SUTJOpPH #90000L0X0 pidouAsy NW eUTJEpHt €00000L0x0 T¥eavidd NW SUuTJepH# Z00000L0X0 ySeLpuy NW SuTJoEpHt TO00TOLOX0 We,seIS NW SUTJOpH 000000200 QtISOC NW @UTzJopH# /~ Aseraqyt'sop ------ /

00000090x0 ATTASTIO NW SUTJOpH Jy AxeaqyTAST[O ------ x/ 000000S0x0 AtTUIeW NY SUTJOpt /~= AxerqyT yeu ------ x/ J00000%8X0 STOSUODONTNY SUTJEp# d00000%8X0 OUS"PATeM NY SUTJEpH G00000%8x0 Sbesseypeg Ny SUTJOpH 9000008x0 a 7eI{Spe_g NY SUTJOpH Goootorsxo MopuTMUedO NY SUTJOpH WOOOTObExXO Jebpequsppw NW SUTJOpH 600000%8x0 sdALUsogsAs NW SUTJopH B000TOPSX0 YseyUsOSuedO Ny SUTJepHt LOOOTOPEXO Useeto0SUuedO NY eUTJep}# 900000%8x0 dolxoguezI Ny SUuTJepH GOOOTOvSX0 XSOTTYeUeTq NW SUTJOpH YO00TOOX0 OOTTWANS NW SUTJOpPH

89T LoT 99T S9T v9OT €9T cot TST 09T 6ST

LsT

cert ceT Tet oeT 62T 8eT Let 9cT S2T vet eet eeu Tet 02T 6TT STT LTT OTT STT vITt €TT

A-3

sheg ysztele/oox® O86T 6P:bT T ady

/+ YAHuet prTea e jou y/ F- HIONATTGVE WAOI SuTJEpHt

/+ pezsoddns jou puewoo ,/ ¢- GADON WUFOI +SUuTJEptt

/+ peyoge ysenbei y/ Z- CaLYyOdW WasOI SUTJEpHt

vedo 03 pettez yyun/eotaAep y/ T- ‘TIVINAdO YasIOI SUTIEpH

J eeeeeEeERURUEREVEREEESUERESESUEUEVESERE VEER EVEDEUERESEUSERESUERES ESE $ 2499007 $ dxq T4eS HT:L0:ST 8Z/80/SB8 OT A’YSAOAIS : AEpesHs

2 [O1QU0) SOANOS

enanaana «&

FREVFPEFE EVEL FEF ELE LEFF SFE ESE RELFE PELE SLY ECF ELE LE FEEL EF EF ELE EL EY FEY F ¥

lta SpnTouy Sat {noexg we AsMs Huy Aesedo WOU -- “OUT ‘ebyuy-S10poumOD

¥ PERERENEUUNEEENUUESSSESEREUUESSEVESESURESUSESUUEERERESEEEE EEE EEEEEEES/

T ebeg y°si011e/9exe 9O86T Lb:7Z ueEL

ANONGMNOR OA

JTPUSH OF

(T>>T) NSWINI ALINN SUTJOpH FH (0>>T) AALIOW ALINN SUTJepH ECF

cy T? ‘{ oF /+ suedo eatjoe Jo sequnu ,/ {quguedg“3y7un qyomn 6€ ‘ped"3tun gixdn se ‘sheTJ~3tTun = gLxan Le /+ seBesseu pessesoidum 403 enenb y/ ‘q10gfsw4yun, qogbsW Jonas } 3¥un yon44s CE vE | eeeReeReEORERUUNEESERUUEEESSERUUUUENSSES0000E8888800008 2TUN. vyeees/ 2 Te ‘{ o€ ‘Axezqry pp Axesq¢] ons 6Z } SoFAed FONNS BZ ey “eo, J eeee¥eeuRE¥ENESEEEEESESSESUESESUSEESESSESEESESEES EEE EY SOTAST eveeey/ = < c

H SINOd OAXa! JTPUSH EZ wd’ Sqjt0d/oexe,, SOpNTOuUT# 77 H SLuOd O¢xXA JOPUITH TZ

H SaIUVadIT OFX FTPUSH ET yA SOTAeAQTT/OOxe,, OPNTOUTH# ST H SAIMWaaIT OdXA JOPUSTH LT

J eeeeeREUESEREDESSREVESUSUESUEVESUSRESESUESUEUESEEVESEEEUEDESEEEEEEES es $ 248007 : et

$ dxZ T4e> 0S:90:ST 927/80/S8 O'T A’U'SeoTASp : tepeeHs : rt

2 TOAWUOD SINE : 6

Was sea ousaveunscee seen ecussdcerseneus eee eiesissieriesec nis 1 SlTd Spn{sur SAt{nsexgy weyshg Huy zestedg WOU -- ‘our ‘ebyuy-e1r0poumoD : g sess obeceuy il eveus eau en vee eUe tana euWesdeunvuNENueea eeeasiceme : H SADIAad OaXaA =OUTJOPHE Z

H SADIASC OAXA =JOPUITH T

T ebeg q’seoyaep/sexe O86T Lb:7Z ueL

![9T] sQO@AQUI A0Q0@ARQUI «=ONAAS

J eevee eEURRUVEUUEEUEERDEESEEE EEE EEEEEEE DOTETOY qdnasequl seveey/

sumMgqq) dyOMNn

-3XgPASY ULdV

SeVeCGVeIW ULdW

seqzeqhngeq ULdW

sAajugbnqeq = ULdv

-US_OOTXeW ONOIN

/+ (punoq szemot) xoe3s weqsks jo doj y/ ‘4emopiyysshs ULdv /+ (pumoq seddn) eseq yoeRs weqshs y/ ‘aeddnyysshs ULdW feamnqdequuem ULdW

‘aanqdegtoo) §=Uldv

/* JOJOSA QJOS VaeAspTOo y/ ‘eanqdeDpIodD ULldv

/~ AUsUPTdUCS 4equtod eseq weqshs ,/ fesepPmty ONOIN SUMS TDUEWMOT CyOM

/~ aequmu eseeTet 24e4Sx9TH »/ /4@A3309S TaOMN

‘oponqr¢ry 9 Azeaqty Aonz4s

} esegoexg Aon43s

H SuSWL Daxai FTPUSH ud’ Sxsez/oexe,, Spn[OUTH H SuSWL OAXA JOpus TH

H SaIuWadIT OmXai JTPUSH ad’ SeTszeAqTT/oexe,, SpN[TOUT#

H SdIewedIT OdXa Jopus Tt

H SIdNaasINI OdXai JTPUSH ad’ Sadn2z18{UuT/oexe,, OPN[OUT#

H SIdNYagINI OgXa JOPUS TH

H SISIT Oaxdi JTpusit ud’ SOTAeAQTT/oexe,, SPNTOUT# H SISIT O3Xa JOPUz TH

J RRR RRRUREEEEEESEEEEEERERUEESEEEUEUEEPEEES ESE EEE EEEEESEEEEREEEEEEEEES

$ [Ato :4aex007$ $ dxg [Tae 97:0T:9T ZT/IT/S8 T’°T A’ eseqoexe : 4epeoHs 3 TOAQUOD SOANCS

i

FEFFECESFFFFEFEF ECE FEF EK FE EFL FE LEVEL ELE EF ELF EE EF EFF EEE FF FEF FES EYEE F

¥ SlTa SEpnypouy eaTynosexg weyshs Hbuyyesedo WOU -- “OU ‘ebTuy-S10poumloD

#

CRREVEEUEEEVESV USED SSOPECUEEUEEVEERSERVEEEVEEEEEESeEREESyererrer eee s/ H 3SWaOaXa OAXA SUTJOPH

H dSWaoaXe OSXA =FOPUTTH

T aeg yoeseqoexe/oexe 986T LP:72Z

ANMNANHOKR DA

ueL

yW' OT/O8xe,, SpNTOUTH

yn’ SSOTASP/OEXxE,, SpNTOUTH yd SeTAeAqTT/oexa,, SPNTOUTH yU' SHSEI/OoxS,, OpNTOUT#H

WU Sq40d/oexe,, Spn[TOuUT#H

WU Atousu/oexe,, SpN[TOUT#

ud’ Sqdnszs98qUutT/oexe,, SpNTOuT# wt’ SYSTT/OSxXe,, SPNTOUTH

ud’ Sepou/sexe,, SpnTOUT#t

tT eeg y°ooxe/oexe® 986T LP:7Z

ore eet

ANMNPENONR ADA

uel

yATeAQTT *OOxe,, INWNOAKA SuUTJSEp# Z /~ *OUl ’ebTu-ercpoumoD y/ T

T eHeg qoueudexe/oex® O86T L¥:7Z © uUeL

©3eY AOOTD OSIN/TWd

J TPUSH

6 ZHOS GaW SUTJopHt 8 Wd Gav SutsopH#

0Z7089 dav SUTJepH /+ (TTaK Se 0Z089 AOZ AOS UTEUA TITH) ¥/ 0 01089 Gav SUTJEPH /* 2=S4aossecoid-og pue Siosses0i1g ;/

/eeeevy SHETIUIA v¥xeey/

(esegooxg 3OnaQS )JOSZTS AZISASWASAS SUTJOpH

{

‘[g] poasoseyesegooxg SNOT

‘(p]asetwasel SNOT

LeReeReeEsESEEEEYEEEESESEESEDET ERE SER ERE¥ERE STEQOTO 49470 v¥seys/

‘[¢] squrazog ASTFIBUIAZOS

SIFEMASSL ASTT SApeaypjselL AST -3STT}410d ASTT *3STIQTT ASTT /4SFJT4AqUI ASTI -4STTOOTAed 3STtT :4STTeomnosey 4STtT S3STTWOW ISTT

qONAAS

qon134s Q0on13s Qona34s Qona4s Qonaq4s Qon.4s qon134s qon.Qs

J eeeeeeeesEs¥EE¥E¥E¥EsETssEssssysesesesessesy SISTT UWEISAS ¥¥¥sys/

Jo0TTywdespyseL dyomn JOOTTWOTSHSeL ONOTN fQponITXPASeL ULdV

fepoajdeoxspyiseL UldWv ‘epogdespyseL UldW

/~ Aequtod Aeisze STNpou QuepTses ,/ ‘SOTNPOWSeyY ULdV /~ uot {UEAIe Hut[npeyose.z ,/ ‘peyoseyuzjy dyOmMn

/+ sHetz uotqueqze TeToods ,/ ‘sHeTquzaw auomn

/+ yUM0o Buy seu eTqestp yse3 ,/ 7QUDISONGL SLAG

/+ ynoe Hut Aseu eTqestp AdnsiequT ;/ ‘quo3SenqI aLAG /+ sbety weqshs ostu ;,/ ‘sbeTasAs qyuomn

/~* S%OFZ umquenb Auesins ,/ ‘pesdetg quomn

/~ unquenb soTTS cut ;y/ ‘umjueng GyOmn

/+ 2 3UN0S Yo edstp y/ ‘qunoodstq ONOTN

/~ A@YUNCD STPT y/ ‘QuNODeTPI ONOTN

/~= AS€Z WueraNnS oF 4ejutTod yf =! HSeLSTULy ASSL

qon13s

8S

J eEREREREESUMESESEEESESSPESESESSEE EEE BEES SOTQCTACA we 3shkS xves¥%/ LS

A-6

Z obeg y'eseqoexe/oox® 986T Lb:7Z E ueL

aLWddN GW) SuTJSopH SLIGM GW) SuTzopH#

LaASaY GND SutTsopH

v Z Qvaet GD eutTsepH T 0

GITWANI GND SuTJop#

(o>>1T) NOINO JaOI OuTJOpH 0 MOING GOI SuTJEpH

‘{

/s

‘TpeAtesey OT ONOTN / ‘Tpeasesoy-oF = ONON

¥ SSOTASP peinjonajs ATG 403 Wessjo. ,/ -28S330 OF SONOTN :

ease ejzep 03 squtod ,/ Seqeq OF °ALaW /ypertejsuet3 soqAq szequmu pe senbe. ,/ ‘yqbueT oF = SONOTN / perstejsuery seqhq jo szequmu Tenjoe ,/ STenqoy oF ONOTN wnu HupureA JO Joie ,/ {A041 OF FLAG / oo. ‘sbeTy OF agLAAN y PUCULIOD SOTASP x ‘puewwoy of QyomMn /+(exeatad seatap) yyun ,/ ‘ATU “OT; yun «3ONaAs /~ Ae ,uTod epou eotAep ;/ {SOTACQ OTy SOTASQ WONIQS ‘eBbessey ot ebessoy Won14s

} beypyzsoI on43s

“{ wou Sutuszem 10 10110 ;/ {A0AAT OF FLAG / / :sbeT7 OF aLAAN yx PUCUWWIOD SOTASP , /puewwloy OF GaomMn /+(eyeaTad szeatap) ytun ,/ ‘3TUN-OT, == TUN -AONAQS /~ Ae \utod spou sotaep ;/ {QOTASQ OFy SOTASQ WoONaAS ‘aessoy of eHessoy jonaqs

} ysenbeyoI Aon44s

H SLYOd O3XaI JT PUSH

yd’ Sq40d/oexe,, spn [ouT#

H SLYOd OaXa JOopuz t+

JL eRRERERENEEEEEEUVERESE SE EERUUEEESEEERUUUEE EEE ER EURUEEEEUEEEEEUEEEEEES $ :AsHooI$

$ dxqJ Taeo 0€:0T:ST 8Z/80/S8 O°T A’Y'OT :4epeoys

> TOAQUOD SOANOS

“_«& eee KH

FEVFF FEF EF ELSE FEC E RIES EVE FEL EF ELE EEE EEE F ELE FFF EEE EEE VEEL EEE FEL EEE EE HF ¥ SlTq OPNTOUT SeaTQnoexg weqsig Hut Qeszsedo WOU -- “our ‘eHtuy-e10poumioD ,

¥

AACA GAO IO OOOO OO Ess / H OI OdxXd 9 SuTsJopt

H OF OdXd = JOpusT#

T ebed yot/oexe 986T LP:2Z

ANMNPNHWUOLK ODA

JTpuc# (03x0) ASWNIUd HIS SuTJepH :{

‘peg us qmaomn

‘ASTT US ASTI 30n23s

\XINO esn Sexy 407 ;/ } ASTIQUIAJOS QONI3s

:{ ‘OPON-ATy SPON 3ONI9S : () (epoD~ATy) GIOA feqeq-at ss ULaW

/* \XINO eSn Sexy 407g »/ } AOVOSARUT JoON1Q4S

:{ !() (@poD-stx) = GIOA -e ed St aLd¥ ‘@PON ST SPOON WONAAS } adnauequl jona4s

Aaque epod seares ;,/ Jueubes ejep sesses ,/

H SLISIT OaxXgai J TPUSH wd SISTT/O@xe,, Spn[OUT#H H SLSIT O9Xa JOPUSTH H SaGON OgXai J} PUSH yW' Sepou/oexe,, Spn[ouTH# H SHCON OdXd JOPUS TH

[OeeeeeeRRRERUESESESESEREREEUBEEESEESESEPVUVEVEVUVEVUE FEF EE EEE EEE EE EE EE $ :4eN007$ $ dxq Taeo €6:60:ST 82/80/SB OT A’ SqdniueqUyT : 4epeoHs

:TOA\QUOD SBo.mNo0S

enna ace a &

VEVECEFRFFSFVRFEF RHEE RHE FR FFEFE FF EEE F ERE VE FE FEF PERE FEEL EFF PF EF EPP EE TY % STI EpnTouy SaTynoexg weyshs Hut Zesz8edO WON -- “Sul ‘eDbTuly-SiCpouMloy) x

¥ PACER U ROU Ua arises y sys eeyss/ H SLdNYasINI OdXA SUuTJopH H SLdNYYALNI DaXA JoOPUrTH

T e6eg ysqdnsssquyt/oexe = 986T bS:bT

ANMNPMNON OA

SZTSHeN ATT ezTshSen ul euTjopH ped att ped yl eutjopt

sbelrz qit shel zy eutsopH# SPON ATT SPOON UT SUTJOpPH /+ Aatitatyeduog Aresroduey, ;/

ebundxe peketep y/ (€>>T) dXATIC adIT SUTJOpH

/~= UMS 02 ABYVIOG PTNoys om JF WES ,/ (Z>>T) GASNNNS 3AGII eUuTJEpP} /*s UT euA pebueyo ysnf eaey om ,/ (T>>T) GIONVHD JGIT SuTzop# /+ SuTuumsyoeyo ATQUezmMSO eue 3M ,/ (0>>1) ONIWNNS JGIT SuTJop}

“{ /+ suedo Que1und> Jo sequmu ,/ Squguedg-qtt dayomn /+ JTOSIF uMsOeyo oy ;/ ‘ums~qtT ONOTN ‘6uTAASPI ATT aLdv SUOTSTASY ATT dyxomn /UOTSASA-QTT CaOMNn /y Arzeaqry szeqye seqzkq jo sequmu ,/ ‘@zTSsod qt{ daomn Axeaqyy e10jeq seziq Jo sequmu ,/ ‘eztsben- att dyomn ‘ped qtt aLxAGN ‘sbela Qqtl aLAN S@PON QTT SpoN 3ON.13S } Axeaqr] qonays u1934xe

(bz-) ONNALXA AIT SuTJopH

(eT-) AONNAXA AIT SuTJop#

(ztT-) ASOIO GIT SuTJop#

(9-) NadO GIT Sutsop#

(aaquasn a11) GISNON GIT SuTzopit

( (AZISIOJA GIT¥QSANaSAW AIT) -aJSva a1) aagdaaSN GIT SuTsop}# (AZISLOSN GI1-) ASWA GIT SuTJop#

v GaANaSIA AIT SuTsJop# 9 AZISLOJA AIT SUTJoptt

H SAdON Daxai J TPUSH

yd’ Sepou/oexs,, Spn[OUT#

H SAGON OaXa JOPus TH

LOeeR eR UEREEEREEENNNEE EEE EEE ERE EYELEEEN EEE EEELEEEEEEEENEEEEEREYEEEEES $ :4aex00T§

$ dxJ TAeS 9G:0T:ST 82/e0/SsB O'T A’YSeTAeAgTT :4epeeys

:TOAQUOD Bo.1NnE¢g

“_“annann «

FIVER EERE ET ESE EEE EEE EERE EEE EEE EEE EEE EEE EEE KES EEE KEE EEE EE SEE EERE EEL EEF Y

¥ SITq SPN[OuU]. SAT Anoexy we sks Hut Qesedo Woy -- ‘our “ebHTuy-o10poumloy ,

¥ PPSPSECSSOSTOCCOSSSSOCCOCCSSSICCCCOSTOSLCOL OTITIS TTT eter erst titi t tr ii ty / H SAIuWasIT DAXA SUTJOPH H SaIMWaEIT OdXA = JOPUT TH

T ebeg Ysofsesqi{/oexe 986T Lb:7Z

ANONPNOR OA

uel

A-8

JTpust 9

£9

GISNON CWD SUTJepH 729 T9

HSNTI GHD SUTJeP#H 09 LUVIS GHD SUTJSp#H 6S dOLS GND SUTJSEp# 8S awa GND SuTJopH 1S

mWonrod a

z obeg y'ot/oex® Og6T Lb:7Z ueL

sTPuUSH OC

6Z ‘{ 92 oO ‘ped"t agLAGN LZ ‘edAL-UT LAAN 92 < ‘peAGITeL UTy ESPON ON4AS Gz ‘ITeEL Us SPON JONaAS bz /PeECH UTy ESPON 3ONI4S €Z } ASTI RON4QS 22 TZ 0z

H SdadON Daxai JTPUSH 6T yd’ Sepou/lexe,, SpN[OUT# ST H SAH0ON OdXa JOPUSTH LT

oT J eeeeReEvEVENEREREDEESERESERESEREREREREPEREREUEESESEUEVE EERE EVER ESE ST s OT $ :Asyo0T$ ET : x ZT $ Gxg [4eO EZ: TT:ST 87/e0/SB OT A’USASTT :sepeoHs » TT x OT :TOAQUOD BOUNCS «yf C6 * 8 JTpUuSH F9 FSV E VEE EE TEESE EEE ESE E VEE SELES SEF ESF SFE FEF FFE FEFFSFEFEFLE TELE FEF PEF EYE L a €9 58 quguedg qtT uQuedQ UT SUTJep# 79 SITdA SpnTouy SatTAnvexg weqAshs HuyAesedy Woy -- ‘our ‘ebtuy-e1cpoumloD » ¢ ums 4tT ung UT SUuTJop# TO x 8 HuTszASPI-Qt{T Huyayspr-ul suTzep# 09 EUERUESUES EERE EES ERUESUSEUESEEESESEVESEEESESEES ESSE ESE EENeEeyEereErY/ © UCTSTASY ATT UoTsyTAey UT SUTJepH 6S H SLSIT D3Xd ~=SuTJopH Z UOTSASA ATT UOTSASA YT SuUTJEpH# ES H SLSIT Daxa JOPUJTH T eZ2zTSsod Att SZTSSOd UL SuTzep# LS

T weg y°sqstt/ooxe 9861 Lb:7Z ueLr z ebeg yseypsesqy[/ooxe 986T Lb:7Z ueL

3 Tpus#

L YSYWIDOTd WAN SUTJEPH 8 aAZISMOTd WAN SUTJOpPH

(LT>>T) ISHOIWT ANIN SUTJEpH

(9T>>T) Wwe JWAIW SUTJOpH (Z>>T) <ISwWad ANIN CUTJEpH i} dIHD aWIWN SUTJOpH

O0>>T) OI'TGNd ANIN SUTJepH

| precee enn n nono ene --------- sedk], Quewestnbey A1ouspy ----- x/

/+ AATTTaTVedUOD ,/ aN Tu seul yur eutzepi#

‘{ ‘(t] aww Aaququey, 3On23s ‘sot AQuqUMN TW = GYOMN ‘OPON TW SPOON YOnaAAS

} asTTweW = JOnaAs

/~ aque ysitz ey y/ /+ JORIS STqQ UT seTzQUe Jo sequnU ,/

J eeeeeREeER¥ECESERESEEUERERESEEVEUEEEUESESEEESE¥E889838 ASTTOOW veyey/

z e6eg q:Asowsul/sexe O86T SS:bT T Ady

08 6L SL LL OL SL BL €L ZL TL OL 69 89 49 99 =) 09 €9 z9 19 09 6S 8S LS

appy neu’ un Sul appy Sul SuT Ep Ht sbey neu up eur sbey eu SUuTJopH /+ AATTFaFyedU0S ,/ up oul UN” Sul SUTJOpH

“{ ‘yqbueyT ea = ONOTN ‘ug eu { - Ippy Neu wd ‘sbey new SONoOmn } uotun } Axquqwey 3on-4s

uotTbes Axowsu stuR jo yybue, em ,/

/+ UoTbe1s Axowsul sTyR JO sseuppe ey ;,/ Squeusipnbet WeyPoTTy SuQ ,/

LeRORRERRE EERE EERE EREREEEEEEEESEREUUUUREEEEEEEREEEES AIQUAUCHY xxx eys/

‘{

‘eouz am

zeddy7uu ULaw

‘JomoT ym =: UL dW

{QsupA Uy xUMYQWe_ 3ON44S.

Sseqnqyaqqy Wu ‘epon yw epoN 3ON1Qs

} Aepeoxque_, = ON1QS

/+ SezAq Se1z JO sEquMU TeIO2 y/ /s T+punoq A.1oweu sueddn ,/ /s punoq Ax1oweul 4eMoT ,/ /* uoTbet eBe4J YS4TZ y/

/~+ voTber sTyA JO SOTFASTAe_oereYyo ,/

J ERReERESEEEETERERESESEEESESESESESESEESUESESYESES44% JOPCOHUDY xeyyyy/

‘{

‘seqyig7ou = SNOTIN

/QXON OU, YUNUQUEW 3ONAAS } junQQUE_, «3ON.4s

/+ ezts eyhq »uny ;/ /~ UNYS YxXEU 02 48qUTOd ,/

LJ EReEeEERERESEESESEESEESEEE ESE EEE EUESEESESEDES EE EESES MUNUDUOW ¢yyyey/

H SAIGON OdXaI JF TPUSHE

wd’ Sepou/oexe,, Spn[OUT#

H SHGON OaXa JOPUZ TH

JL ReResEREREESESEEREGEERE EUEEUEEUEEEEDEUEUEUEUEED ERE EOE EUEEYEREEEEEEY $ :4ex00T$

¢ dxq [4eO 6%: TT:ST 8z/s80/se O°T A’Y’AAOwSU : 4epeoHs

:TOAQUOD Somos

_“axxnnan «&

EERSEEEEREE EERE RSE REESE EES E REE EERE EEE EERE EEE E EERE ESE EER EV EEE REESE EE EY ¥ SlTd SPNTOUL SAT Anoexy we yshks Hutyeszedo WOY -- “oury ‘eHtTuly-e10poumc; ,

¥ FEEERENVEEEEEEUEUENEEESEEEEEEEEEEEEUEEEEE EERE EEE EV EEE EE EEE EEE yy eye E/ H AaOWAW DaXAT 9 SUTJOPH H AYOWIW DEXA =FJOPUZTH

T e6eg y°Arousul/oex® 986T SS:hT

ANONDNOKR ODA

A- 10

ady

:{ seqkq uy vet obessou vy ‘qn6ueyu = quomn /- od A{dea eGessou y/ ‘qu0gkTdey um, JiogSsw 3On-4s ‘@poN UM @pON ONS

} eBessopy Jon.4s

/eeaeREsERESERERESESERESESESREEEEUEESERERESERERERE SESS @Hessopl xxeysy/

AYONOT Wd SuyzopHt INILIOS Wd Sutzop# TWNOIS Wd Sutzep#

oatN

NOLL ad eutzop# yserStg "di quyi3jog du eurzep#

:{ ‘Ast Tsw "dm AstT ‘ysel5tg diy se] ‘yTqbtg du

‘shel aa aLXAN

‘epon Gul spon Wonsz4s } qogisy Qona3s

qon.4s qona4s a.LAGN

/+ ASTI PUTT ebessou ,/ /+ peteubts eq 03 se y/ /~+ <secqumu 3fq Teubts ;/

L eeeReREREREERESER ESE EESE EER EES ESTES EEE EEE EEUEEEE EES WOGSH] xeyyyy/

H SUSWI OSXai JTPUSH vt’ SxSe7/Ooxe,, OpNTOUTH H SNSWL OSXA JOPUS TH H SLISIT Ogxai JTPuSt nd’ SISTT/Oexe,, SpNTOUTH H SLISIT DaxXd JOPUS TH

H SSCON OaXai JFTPUSH

wd Sepou/oexe,, SpnTouT#

H SSCON OdXa JOpust#t

L eeReeeRRREEUERRESEREEEEEEEEEVEEEEEEEEEEEREESEREEEEEEEEEEU EEE ERUEEEEES $ :ASxOOT$

& dxq [4€O SP: IT:ST 2ZT/TT/S8 T°T A’U’SqOd : uepesys

$ TOAQUOD SOANoS

ene n nn eH

BREE EKEEEEEEESEFESSEESEFFEEEEEEE REEVE LELEFE FEE F FFE SESE EEE SEER E EE ¥ Sita Spntour eaTAnoexg weqshks HuTWQes18edo WOU -- “UL “eDTuly-SuOpouMloy) x

¥

COREA AAA UA AAO UU AA EO Oa rr Err rer sss / H SLYOd OsXA +=SUTJOPH H SLYOd OgXaT + JeOpust#

T seg y°sqsod/oexe 986T 9S:FT

ANMPNOKR ODA

2 Pus

YT FYOHAWNAS IN SUTJOpH €T SSH00dd IN SUuTzZEpH eT INO IN SUtJopH TT JINILAOS IN SutzZopH oT XAOWSNTIN SUTJFEpPH XWAGIT IN SUTZopit ADINOSAN IN SUTJopHt OSWATdIY IN SUTJOPH OSWATaT IN SutsopH AOWSSAWN IN SUTJOpH LYOGISW IN SUTJOpPH ADIAAC IN SuTzep#

@ LdNYaSINI IN Sutzep#

T ASW IN SUTJopH

0 NMONDINNIN SUTJepH

| ¢-------- sedk], Spon ----- v/

‘{

OVNOM OAH

/oureny UT

: Fag UT

‘adAL Ut “polguTy SPpON 3ON44S Joong UTy SpOoN Wona4s

} Spon Fon23s

zeyo SLAG a.LAGNnN

LORE ERE EERE EERESEEEREESEESEEUEESEESEEETEEUERE RES EET EEREREER EEL ERE ETS § :48xOO1$ +

$ dxy [T4eo €G:7Z:8T ZT/TT/S8 T'T A’USopou : tepeoHs :

:TOAQUOD BSounodos

¥

SESE ESE ELE E SESE ERLE CELE ESET EF EFF EVEL FR FEL FT EF EERE EEE R ESV EF EET ¥

SlTd SpNTouL eaTQnoexy weqAsks Huyyeszedo WOU -- “our ‘ebTuy-a10poumloD ,

* ER EREVEREREUSENENEUSESEREV ERE EE SEE EEE EERE EE EP EVER EET Er EE ES EE ee s/ H SH0ON OaxXad 9 SUTJEpH H SddGON OSXA 9=JOPUTTH

T abeg y'sepou/sexe 986T L¥:2Z

ANONGPNOK DA

A-11

uel

Mor

(0>>T) (L>>T)

OIWOX0

/+ _ Spo0o 3TUT 03 sequTod ,/

/s Huy4sys AuepT 03 seqUTOd ,/

/* eureu spou 03 se juTOd ,/

/+ Aayaoyad uofxRezyTeTaUy »/

/s (2{qumw-IN) STnpow Jo edAQ y/ /+ AequmU UOTSIEA eseeTe1 ;/

/* sHetz 5ez snotuea ;/

/~ ueos enuT QUO 03 sseuppe ,/

J TPUSH

LYWLSAIOO MLY + SUuTJep# UIAIN MLY SUTJopHt NJHM WLU SUTJep#

/+ :AQtttaTWedMoD ,/

LUVISTIOO ALY SuTzZep# LINIOLINY ald 9 Sutsept

CaOMHOIWW OLY =SUuTJepH

‘QTUI-AA UW

‘buyaysprlro jug Aeyo

‘ouren” qd, aeyoO ‘Fag-34 SLAG ‘edA~34 ALAAN SUOFSASA~AA FLAGN ‘shel 734 FLXGN ‘dypispug-34 ULdW

/+ aoge ay3.03 sAequtod y/ ‘belyoqeW qty Jueptsey Jon.3s

/* (WWOSTII) Uo Yyozeu 02 p4om ,/

$ dxq [4k EZ:ET:ST 87/80/S8 O°T A’UUEPTSe1 : 4epesHs

T ebeg yQueptses/oexe 986T L¥:7Z

SITJ EpNTOU] eaTAnoexg weyshg BHuyWe10dO WOU --

‘psomyozeN-34 GYOMN

‘{

} Juaptsey Aon43s

H SdSGON DaxXai J PUSH

ud’ Sepou/oexe,, Spn [OUT #

H SdGON OdXd JOPUS TH

$ :AaxD07$

2 TO1QUOD BONES

J eeeeeeRERERUEERUREGTEUUREUEETEESEEUT EET EEU EERE ERT ERTEVERU ERE EEE EUEEYS

ane ana n ea &

VRVEEESSEKRESK EERE REE EEK ERTS FE SESE THESES ESTE FETE ESE EE EE TER ELE RE EE

¥

‘our ‘ebTuNy-e10poOUMOD ,

SEPEENVERERESESESUUEUUEUURSEEEUUVUUVESEESERE ER EEE EERSTE EERE EEE EEE / H INSGISaY Dax 9 SUutTsopHt H INSdGISaed OgxXd 9 JOPUITH

ANOMSTNO” ®O

uer

3 Tpuc# yselhtsg du BHsppjpoTus sutjop#

‘{ -sptq ws dduyom fquogisw ws qAogbsp, 2on43s } eaoydeusesg 30Nn13s

J eee eRRRRUEREUUEEUEEEEUEESUUEUEUEEEPEEEUS EEUU EE EE YY @AOYCeUS xveeys/

$9 v9 £9 co T9 09 6S 8S Ls

A-12

z ebeg y'squod/oexe 9861 9S:FT T Ady

eyep yseq s0d

/_» Ksousu pazeoot rte

/+ Nad Sut33e6 yse}

/+ fidD Butsot sez

/s@ + punog seddn yoeq4s

/* punoq JeMOT 40e AS

/* Ae\uTod yore Ws

/+ eyep desaq 03 squtod

/~ @poo derzq oj squtod

/+ Spoo Aydeoxe 03 squtTod /+ e yep Adeoxe 03 sjuTtod /+ petTqeue sdesz3

/+ peqeootte sdes3

40} Sydeoxe are TITM om SbTS /% PeATeoe1 Saey om SHTS /~ 403 BuyAyTem ore om SHTS /+ peqeootte shits

/ Sut seu petqestp se} /HutASeu peTqestp 23uT

SL

JTPUSH FL

€L

(g>>T) SOd-a9IS SUTJep# ZZ

(%>>T) LIIa aOIS SuTJEpHe TL

(I>>T) GTIHD aDIS OUTJOpH O74

(oO>>T) LaOdW 3DIS SUuTJep# 69

89

oon nn neo - - - - - -ee sjTeubTS peuTjepeig -----,y/ 19 99

9 QaAOWAY SL SUTJEpH S9

GS IdaOXT SL SUTJEpH 9

v LIWM SL SuTJep# €9

AGWaN SL SuTJepHt 79

z NNa SL SuTJop# 9

t GaddW SL SUTJep# 09

0 ITWANISL Sutjep#t 65

were rrr nnn nnn nn nen en nnn ence nnnnnne= Seqeys Se] -----s/ 8S Ls

z ebeg ysysez/ooxe 9g6T Le:7Z © UeL

x/ +/

L HONNWT GL SutsepH 9 HOLIMS GL SUTJepH S LdaDXF GL SuTsopH b WHONOWIS GL SuTsJop# 0 SWILD0dd aL SuTJopH

s3tq betq ----- /

“{ feqequesf OF ULav *Aaququey oj} ASTI yAon4ys ‘() (youney03) = GIOA () (yoataso3%) = IOA ‘aeddnds-o2s UL ‘4eMOTGS 93s UldW ‘BeydS 09 ULdw ‘epopgdezl™93—s LW feqeqdesay93 UWLdV ‘epopydeoxg 02s LW sezeqqydeoxg93s UL dW ‘etqydeal~o3 = GuOMN ‘ooTtTwdeay“o3 «© «GYOMN ‘qydeoxg6Tts~02 = SNOTN SpadeYyOTS O32 = ONOTN ‘qtem6tg-03 = SNOTIN ‘OOTTWOTS 032 =SNOTN 7QUD3SONGL 9°32 0=—_s SLA SQUD3SENGI 93 2=— ss ALLA ‘e7e9S°032 «= ALAGN ‘sbeTy7 03 =©aLAGN Sepon O23 SpoN Jon14s

} AsSeL WONAAS U18ERxXE

H SLISIT DaxXdi J TPUSH yd SISTT/Oexe,, OpnToOuT# H SLISIT OdXd JOPUT TH

H S3GON OdXgei J TPUSH yW' Sepou/exe,, SpnTOuT# H SAdGON DaXd JOPUs TH

$

: AOHOOTS

$ dxq [Ae GT: TST 8zZ/80/S8 O'T A’YSHSeQ : 4epesHs

:TOAQUOD BouNOES

J eeeeeeRRESUUEREEEERUUEEEUUEEEUUUESEPEUULERERERESEEEUESE RUPEE EUR EE EE YY

_*x7 a anne

ERVFLEFVFFECFE VEE F FEF FE LFS FE TEPER FFE E EEE EVE VEE FEEL ESE FEE FETE EVE EY

¥

LTA SpnTour eaTAnoexg weysks Hutyesz8edo WOU -- “OUT ‘ebyuy-e10poumod

¥ EOUVEEERUUUEESESUUUUUEEEEUUVEEUUUEPESEEUUUUVERERDEE EEE E ER EE SEY EEE EEE /

H SNSWL DsXA 9=PeUuTJOpH

H SXSWL OaXd

JOPUZ TH

ANONGPNOK ODA

A-13

T ebeg y'syseq/oox® 986T LH:7Z E UPL

Tێ NOISUAA AMWVAAGIT euTzop# d3x0 YSWNELAG SUTJep#

0 'TINN SUTJOPH

4) ASTWA SUTJopHt

T ANAL SuTzopH

‘IXaL aeyuo peudtsun jopedé 3

: "100d q4oys jyopedAA SINNOON Woys peubtsun jepedAR - ZNNOOD quoys jyopedéAA :a7TEanod STqnop jepedAA ‘IWOTA ZeoTJ yepedAA

/~+ Soyquemes oTsToeds yyTM sodA], »/

/+ (aaomn) Aqtauenb 3Tq-9T poubTsun ,/ ‘INOHSN 340ys peubTsun zepedA3 /+ (aaom) Aatauenb 41q-9T poubts ;/ LYOHS quoys jepedAy /+ (@poo meu ut esn 4,uU0p) :ATUO AQT TTQeqedUIOS 404 y/

/~ 4equtod Axouweu eqntosqe ;/ ‘MLdW ULdaLs JepedAR 4equtod buyays ,/ ‘MLdaLSy zeyo peubtsun jopedAy

/+ Atvenptatpuy peyetndyuew sqtq g x/ ‘SLIGSLAd eyo peubtsun jzepedAy

/+ Aqtquenb 3TqQ-g peubtsun ;/ ‘aLAGN aeyo poubtsun jzopedAR

| /+ Aqtquenb 31qQ-g poubts ,/ ‘SLAG aeyo jepedé;

/+ Atvenpyatpuy peqetndtuew sqytq 9T x/ ‘SLIGdHOM toys peubtsun zepedéR /+ Aqtquenb 3Tq-9T poubTsun ,/ - qgoMmn 4oys peubtsun jzepedA3

Aqtquenb 3TqQ-9T peubts ,/ ‘raIOM quoys jyepedAy

/+ Atvenptatput peqetndruew sqtq ze x/ ‘SLIGONOT Suort peubtsun yepedé Aqrquenb 31Tq-ze peubtsun ,/ ‘ONOIN uot peubtsun zepedAy

/+ Ayyquenb 3TqQ-ze peubts ;/ SONOT Buoy. yepedAR

/-~ 2284 YA0M 03 Wees Jou soop zepedAQ ;/ PTOA CIOA SUuTzEpH /+ 1qeTzea seqystbax (ATInzedoy) e x/ JeqsTheit WFISIOW SuTsop# /* PTGETACA OFPEAS TeooT & y/ OF7e9S OIIVIS euTsept

/~ TeusteqzxXe ue 03 SoUsIOjoOI ,/ U1I93XO TYOdWI SUuTJop#

/+ Teuszeqxe ue jo esn A1sozereTOep ot x/ UI83X® TWHOTD SuTzop#

Leeeee RRR PRR EEEENUEEEEERERE ERY EERE UE EESEUN EEE ES ESE ESE EEEEESE EEE ESE YEE $ <AeNo07T$ ¢ Gxq [T4eo LE'EP:LT ST/TT/S8 Z°T A’YSedhQ : 4epeeys

2 TOAQUOD Bo.1n0S

“_axnacnnnn &

EEREREREREEEERE EE REEEREE SER ESEREREEEER EE EEE SEER EERE EEE SEEEEE REPS EEEE EY ¥ SITd Spnpouyr eaTAnoexg weyshg HuTReszedo WOU -- “OUT ‘eHTuy-s10powwoD

¥ OCIA OBGO OOOO IO OOOO Orr es y/

stpuct eS LS

z@ ebeg ysedAj/ooxe 9g6T Lb:7Z E€ uUeL

H SddAL OdXA SUTJOpPH H SaddAL OeXa 9 JOPUsTH

T ebeg ysedAR/oexe O86T L¥:7Z

ANMOMNAPNHOR DA

A-14

uel

I Sala Oaxai SOUNg 88

48

WANd 98

(ov) 3TUegOAT- usr S8 OW LIWdd 8

€8

z3

WONT T8

(OV) UDISONGL’T#H a‘ Gdav 08 OUOWN Giguod 64

8L

LL

WONT OL

QTWISGOAT aaUX SL FOSJJO YUOQISONGL 4A0J ,F°SEseqosxse, Aan IONI s OL OWWN SATIGWUSWL €Z

ZL

POOP ee RM mee Reem eee em mm meme meter eet meee te meme teen nme e x TL x O24

Ssotsep] UOCTSNTOxY HuyH7sey x 69

x 89

aa ata a athe haan ace aciea rae aeaaae eae eaten x 9 99

S9

WONG 19

Oana €9

‘O\TIGWNA 729

eUSQUT “000D0$# M*FAOW T9 ©\aIaWNA $°'39d 09

(T\) qUD9SeNGI‘T# ‘dans 6S T\’h ‘I’ SAW 8s

aaa TN ONAI LS

z e6eg T'setqe/oexe Og6T L¥:7Z UeL

OdNa

> O\FIGWNA NZINI aI+WIOLaS als eueqUT ‘00000$# M°ZAOW O\aTIaVNA. sada (ov) 3UDASONGI‘T# a’ Gans ia a tN oOdI [Beyyoqetos] » OWW a IaWNa

WANA DaNa (t\) 3UD9SeENCI‘T# a‘ Oday NALNI” A+ (WIOLSS AI LON) + eUSIUT ‘O00POS# M'FAOW

t\’e ‘I'SAOW on aTNe ONAI Sana

(ov) QUDISONGI‘T# a‘ Sdav

NALINI a+ (WIOLAS JAI LON) ¢ eUeIUT ‘000P0S# M* ZSAOW ci aE OsdI [Boyyoqet0s] y OW aTAWSIa

S[qeuse/STp AOJ STCULSAXO QDWN SAV INI

Tl ASWEOIXA OFKAI DUNT

wk SSeQOoxe/OOxs,, ACNIONI TaSWHOaXe OFXA ANAT

I SadkLDaxai SANA

SedhQ/oexe,, JGNIONI I’ S3dAL OaXa ANAI

SSVVFFFFEFSSSSCE SSF EEF VEE SESE EE EEE ELE EFF EFF SSE FEE FF FFF FFE FEF FLEE EYEE Y $ 248007$

¢ dxqJ T4e> 0€:90:ST 827/80/S8 O'T A’ FT SOTqe : 4epeoHs

7[TO1QUOD SOIMNOoS

xz Kcnexnn en eH

ESFSSFSSSSE ESE EE FEE KE EFF EFS FEE FEF EE FFE EEF SELLE EEE EE EEE EEE TEEPE FEE EEF FF

¥ SlTA epntTouy eaTyAnoexgy weqshs HutTWeszedo WON -- “Our ‘ebTuy-eA0poumIOD ¥

ERPEREEREREEREREEP ERE REE EERE EEE ER ERE EE EEE REE EEE SEE EERE EEL EEE EEE FEF ¥ T Las I SaTaWv OdxXa

I salav Oaxd GNdI

T a6eg T'setqe/oexs 986T LP:7Z

ANMYDNOR ODA

A-15

ASTT Axscoweu peqdn.s105 Aserqrt eeu oj Axuoweuw ou SMTTez umsyxooeyo Areiqtt uUMS)SeUD eseqoexe

SO00000TS$ Nbe yAdnz10DURPYW NW %00000TSS$ nbe UWeWNATT NW €00000TSs$ nb&e umsxyoatT AV Zoo000TSss nbe umgyDeseg NV

Ce Cqe OQ OD

UMSNxOSYO A0RDeA UCT AdGeoxXSe 00089 ¢ TO0000TSS$ Nbe 3Oe,qdox” NV 000000T0$ nbo qt 9Sxa NW Axeiqy[*Sexe ------:

FEV EFF FEF VEE FFE FEF F FEE FFE EFF FFFFE FE FFF ELF FEFFFF FFF FES FELT FEF FFFFFE FYFE ¥ Tw’ (ov) ‘3dnzu0csHey NY LUaIW *

¥ 3strT Axoweuw peqdn.ui0cs -- ArTe1qTT Sexe :@TGmexSe 410g

%

2S2I8Ty pug-peeq ofzyoeds sg

§

FFF FFT FFF FF FFE FT FFF FFT FFE FFFEFFEFFESFFE FE FEF FEF FEL FE FFE FFL FEL FE FES FFF FEF

TE0s0000$ Nbe YouemoM”OV

0€080000$ nbe de.sa3Sg 00g OV ZZ080000$ nbe OASYOSTW OW TZ080000$ nbO = SAs~pistd OW 0z7080000$ nbe OASYWID OW ST080000$ nbe ASQASUIT LOW

%T080000$ nbe AeC*ist@ioe4sl Ov €T080000$ nbe aaqpseoghey ov ZT080000$ Nbe Aaqj4Aogeuresy OW TT080000$ nbe aAaqertosucd OV

OTOS0000$ nbe aagotpny ov 60080000$ nbe Q¥TUOOT OV 80080000$ nbe arINWy OW L0080000$ nbe atIsod ow 90080000$ nbe QTIASTIO OW S0080000$ nbe arTu3en ow

700s0000$ nb& uUoTITNAUT OW €0080000$ nbeo = aqrysseheT ov Z0080000$ nbe qrysotydesg ov

To0s0000$ nbe QE TOOXA OW :Sq0efqo qQueTe ------?

00009000$ nbe AOANJOT OW 0000S000$ nbe seyuedg OW 0000%000$ nbe Aequedg Sy 0000€000$ nbe qt ued ow 0000Z000$ nbe qT THEW OW 0000T000$ nbe A10uSHON OW

sepood jquete esodimd Teszeueb ------!

00000000$ nbe Asaa0cexy_ LW

00000008$ nbe pugpeed IW

sodAQ quiere ------:

FFFF FFF FFE SFE FES FLY FEF EEE ELE KEKE ESS EES SSS LF ES EEF FEELS SSS SEE FEF EF ¥

Tw’ (ow) * (aT eWoviarTduedo oyiAeqJeuTL-NW) LUTIW '

z aBeg T°sqele/oexe OG6T LG:bT T Ady

LS

sArerqr[‘yqeul uedo Jouueo SoTAep’ JouT «6: eT durexe 107

* ¥ + SSTY pug-peeqd [TeisusO ¥ *

PRFFFFFFFFEFFFFFFFEFEFFESLELELEFEFEETFEFFFF FSF FFFFE FF LEFEFFESEFE REEL Y

wpuS ge/ce/_p’+(ds) [°weaou (9e) VSCIVOAT asl

(;; j;eureu szedoid esn) 9e’% T[*aaocu

Sang ge’z7\ BOT

oa ’a2\e ONAT Le’T\# eAour

(ds) -’9e/ge/_p Tweaou (yoqesos ‘Aertyuresed ‘sequmNzerTe) o10eu

"MOU JOJ YAOM PTNOYUS 3F ANG JH UOTFWESOCT uo Butddeq4s eNTT °° °*uoTAdn.s100 A1ousUl 02 SATRZTSUSS AOA ST II

IZONYHD AWN OWWN SIHL ‘Werte ue Huysneo 103 o1oeu STYR ESN

eon aa &

FEEVEVFFEEEEE EEE SES ERLE LT EEE EEE EE ESV FEEL EEE STEVE EEE ELEN EE EE EEE EEE TEES

TTe_,eEp S10wl SeZeotpuyT :4A011g OTJTOSedS ,

SeM JO1IO SUR WeUA SeqeoTpuT ATYyHN0’A s4orIg [Tesouey yy

‘aequmu we yshsqns WOU SezeoTpuy :prsAsqns +

qeTe pugpeeq :qd ¥

¥

me fee neem S285 S=sSee==S—= ¥ | Ao1ig OFZTOeds weysAgqns Jo1ig Teseuegn | pyrsksqns lal + hn eS SS SS 6 8 8 ee Piss 2 9 $95 sse=22= + ¥ ¥

sJEQUMU JOLISO VIETe SQ jo WeULiog ,

¥

FFFFFFFFFFFFFEFSFFFFFEFFEEFSEEFFFF FFF FFF ESF SEFEFE FEE FESR FEF EFE LE FEE EF TY

sHelgskg esegooxg UF » T’NOWMLUAIV’S 4JaCLId FSRFFFFFFFFFFFSFFEEFFFFFFFFFFFFEEFFFFFEFEFFFFEFE FF FFFEEFEFFEFFRFEE FEE ERE FY $ :408x907$

$ dxq [Taeo ESg:S0:ST 8z/80/S8 O°T A’F°SZIETe :s9Spesys

$[TOAQUOD SOuMeS

eanane ana

SERERRREREEEEE REESE EERE ERS EREREEEEE EEE EEE SEES EERE EERE SESE EEE EE EET SEY ¥ @-- ‘Our “ebTuNy-S1CpoUIOZ) , ¥ ERERESSERTESESEVESVEEEEETETELES TEES EF EFEEE TEE SEE FEE E TEETER TVET EY T LaS I SLYaIW OdXT

IT SLYaTV OaxXa ANSI

SlTdA SpnTou, eATAnoexg weqysihs Hut We1z68edo WO

A - 16

ry eed F°SqVelte/oexe OSGI LS:PI T Ady

10128 ASTP £ WO0000LOS Nbe s021pISTC NW S9T uMSxHOeY PTTeAuy £ 600000L0$ nbe ums>~ppeg NW ZL9OT eeiz Apeoute Asx ¢ 9§00000L0$ nbo seizkoy Ny 99T

qdnaizoo deuntq * £00000L0$ nbe depntd NW S9T 4Jo.ize eouenbes xo0Tq STP 900000L0$ nbe besirayista Ny v9T PeTTey CeAee1Z / $00000L0$ nbo O@ASCAT NW €9T PeATeoe1 Qerxoed pejoedxeun %00000L0$ nbe PidouAsyNW Z9T

A-17

I SLaaIW Osxai SUNa L1Z emMTFes VIG - €00000L0$ nbe [tesvidO NW TOT

91z 3,UPTP »ASelpuq ¢ 7Z00000L0$ nbe AseLpuy NW 09T

ooo000TEs Nbe = Yous@{IOMNY STZ dnzzeys ze Aiowsul ou TOQOOTOLOS nbo weynsteIS "NY 6ST

YOUSCPIAOM ------! IZ 000000L0$ nbe QFISOD NW 8ST

€1Z Axeaqyt sop ------/ L¢T

JO149 ue peuinjet poo Joog T000000E$ Nbe 4011FI0OG NY ZTZ 9ST Co00000E$ Nbe desysjoo_ NY ITZ 00000090$ nb> Ss qr AST NW SST

de138}00q ------: oTz ALeAQTT*YSFIO ------! OST

602 €ST

o00000zz$ nbe = OuSHOSTW NY 807 000000S0$ nbe ArTUEW NY ST

SOMOSOL*OSTUl ------° L0Z ArseACFT "YEU ------: TST

902 OST

3yuMN GATQOe ou :AdniteqUT ¢ 700000TZ$ Nbe JOWONIUTUC NW S0Z SOFASp STOSUCS eq} Uedo 3,Up—TNOD ! 300000%eS Nbe STOSUCDONNW 6bT 3stp sey Apeoate :4yun 38H : ToQ0000TZS Nb® rASTASeEHAT NW ¥0Z uoTsueye.iduioouy Huysnes oye pAjem / 3700000ve$ Nbe ocUoc_paTem NW 8sPT Qo0000TZS MO == SuSWISTC NY €0Z dwodaI Aq peatece.: ebesseul peq qoo000pes nbe sbhesseypeg Ny LPT

SOMOSA*HSTP ------: Z0Z UOFRTNAU] Huy4seque uUmMJe1 eReERS peq Dood00Pes NbO es7eASpeg NW YT

T0z Asousu ou ‘mopuym uedo { go00TOPss Nbe sMopuTMUeEdO NY SPT

0000000z$ nbo O4ASUWID NW 002 Axowsur ou ‘sqzeHpe5 ms ppe * woooTores nbe AeHpequsppw NW PhT

SOMOSEA’ SFO ------! 66T edkq umouwjun ‘ueeszos sks uedo 600000bes nbe edAéz~usosshg NY EFT

86T Ascwusul ou ‘OoTTe Jweq_sei ‘usei1os uedo * gn00TOPEss nbe AseyusosuedO NY ZT

qsenbex peq T00Q00STS Nbe beypeqnnoNw JL6T Axsowsul ou ‘useios uedo ¢ L000TOPE$ nbe uee1osuedQ NW THT C00000STS NbO aAaquowTy "NY 96T O4eZTSeY > doz xoq wet * 900000P8S Nbe dorxogue7I-NY OFT

SOTASp' IOUT. ------! S6T Aiousul ou ‘ooTTe euetd ¢ soooTOPes Nbe® SOTTWEUeTA NW 6€T

v6T Axsousl ou ‘DoTTe qns * POOOTOPOS Nb® cOTIVaNS NW BET

{Tem sowyTA uo 40.1% :AeTep { 700000BTS nbe Kelaqql NW €6T Axsowsur ou ‘ooTTe euetd wey / EN00TOPOS Nbe SOTTWRITNY LET AOLIO HSCS :O7eAATTeEO / T0OO00BTS Nbe ASesaqTTIesdL NW Z6T Axousul ou ‘j4od azee19 ¢ Z000TOPSS Nbe JuogejzeerD NY SET 000000bTS nbe ASMISTMPeAL NY TET edkyijebpedg Ny jo wioy Axeaccso ¢ T[090000h0$ nbe yebpeqpeg Nw SET

SOTASP* ASTpoeAQ ------! O6T edA3 3epe6 umowjun ¢ T[00000%8$ nbe edArAebpen Nw PET

68T 000000%0$ nbe uot z_NWQUI| NW €€T

000000ETS nbe Aeapze0ghey NY Sst ArxeAGTT *UoFZPNAUT ------: ZeT

eoTAep: pseoqhey ------: LET Tet

9st 000000€0$ nbo qrIsszekeT NW OeT

000000ZTS Nbe Asqziogeureg "NY SBT AxeaqyT *saeket ------! 6ZT

SoTAep’ AiodeweH ------! PET 82T

€8T Asomsu ou ‘dewataita woootozes nbo dewnatg3Ig NW ZT

QO0000TTS Nbe AaqeTosuCyNY Z8T seyduy, 103 Aioweul ou ‘9x83 / 6000T0ZO$ Nbe seyduyAxeL NY 92T

SOTASEP’STOSuoD ------! TST Arousal OU ‘TTTZ Poots * goooTOzTss Nbe IItaAPOOTT NW SZT

OST Axsousul ou ‘eurexy Quoys ¢ Lo00TOZSS Nbe surerqq40"US NY ZT

0000000TS nbe ASCOTPMY NY 6LT Axsoweu ou ‘eureay Huot * g9000TOZTSS Nbe eurerzHucT NW €7ZT

SoTAepotpne ------: SLT Arsousul ou ‘peey yst{ seddoo - goo0Tozes nbo peem4styidop Ny ZZT

LLT PEOCTASAO ASTI SVeTpoutejuT seddoo H00000ZES$ Nbe 4zeAOQASTTIGOD NY T2ZT

000000608 nbo QyTUOOT NW 9ZT peoT4ero AsSTT 4teddoo ¢ €00000z78$ nbe seADASTIdGOD NW 0ZT

Axesaqy[*uooF ------: SLT Asowsul ou “ASFT uoTRONAAQsuF seddoo * zZoooTOzTS$ Nbeo aqsurdoD NY 6IT

BLT Asousu ou ‘AstT Aetdstp ueddoo : Too00TOzZe$ nbe Aertdstqdoo Ny sTT

00000080¢ nboe ATTA NY ELT 000000z0$ nbe qr Isotydes | NW LTT

AxeraqrtqyTwes ------! ZLT Axerqyt sopydes6 ------: OTT

TLT STT

Kej2z2ao peq ? 900000L0$ nbe AeTsz9aQped NW OLT eoinos Yldw ue Jo ()AonaASATUI ¢ L00000TES Nbe AWWWATUT NY PIT

ebuer jo yno Aa ? g00000L0s nbe ebueydey NY 69T suemies ydnistequy 103 Axoweuwl ou * 900000TS$ nbo WoWAQUI- NW ETT

y eHeg T°’squeTte/oexe 9861 LS:bT T ady ¢ ebeg Fsquere/ooxe Og6T LS:PT T Ady

I saowdd OaXaI SGNT bit HAONSY » ‘TIWANSdO WasOI NO AOIAZGNAdO waa

yqyGuet prrTeA © Jou yy F%- NOd HIONTIGWY wo! pequoddns Jou puemo> y €- NOA GNOON wasOrI peqioge ysenbeu , Z- nod aazLNOaW wwgOI

uedo 02 peltey Qyun/eotAep » T- Ndd TIVANddO wsdl

:SAOAIA OI paepuejsg ------ ¥

FFF FEFFE FFF FFE FFFFFFFFFEFFFEFFFF FSF EFEF FS TEL EEFEFT FEEL FEF EFFECTS FFE F YF HH ¥

$ :8END0TS

, ¥

& xg Tse 97:40:ST 82/80/SB OT A’T'SHO1IS :4EpeCHS

x

:TOAQUOD BOMOG sy

¥

FSFELEPERT SESS E ELSE SEES SE SESE SESE ETE EPR LEER PE LEER EERE EYE . y

Sita Spntouy eaTancexg weqshs HuTQesedo WOY -- ‘Our ‘“eOTUIy-Su0poUIOD x ¥

FEPEEEEREVEA STEELE TEE EEEEEEEEE EERE EERE TEESE ESSELTE TE EEE YE EEE EEE EF ¥ T Las I saoddd OgXxa

I sdoued OaxXa GNAI

T eg ¥°s10110/00xd O86T LP:7Z E ueL

ANONGNOKR AA

IT SaDIAad OgxXat SONA

T’MSWINI’LINN JgGLId O’SAILOW’ LINN aaGLId

SSR S,4JeATsp ut Huyuuns , SATIOR ST ASCATUP

:SUOFRTUTJOP OWT LINN ------ *

aZIS LINN ‘TsaW INDON3IdO LINN Gaomn ped LINN aLAGN SOWIT LINN a@LAGN

sqsonbe1 10j enenb , aZIS dW’LINN ganionars

azIs qq ‘Taw! aZIS @I1’dd dYNLONLs

AxreACTT OF TEOTQUSPT

Tl SLYOd O3xXat SANA

ut SPod/oexe,, AGNTIONI

I SLYOd Dexa GNAI

I SaIuWwaaIT OgxXai DONd

a} SOpsAesqy[/oexe,, AGNTIONI

I Salawad1T Ogxgd GNAI

FSS SSE EF EE EE EEE EES EEE EEE EE EE KEL EEE EL EL EL EL EK EE SEE KE FEE FE LES EEE EY $ :4eHo07$

$ Gxd T4eO Z0:140:ST 87/80/S8 O'T A’ SSOTASP : tepeeps

2 [O1QU0D Bo.M0S

anaxnack &

FRRERER EERE ESE RE REE ES EERE REE E EEE EEE EEE EEE EE EERE EEE SEE EEN EEE EEE ¥ CLITA SpN[OUL SATANoexg weqshks HutAeszedo WON -- ‘our ‘eHTwy-ae10cpoumon , ¥ FEES ESTE EES ETE TEE ESET ELE ELE LF EEE ESE FEF FESS FSF FE FE FFF FFE FSCS EVES FF RF T LaS I SADIARId DaXa

T SSOIASC OSX ANAI

ANMNGNOR ODA

A- 18

T eeg T°seotaap/oexe 986T L277 UeL

jeubtsee1g JaCONNA 9S TeubTSoolty agGONNd SS jeubtsg JgaGONNA vS 3FeM J3aGONNA €S 3deoxg3eS JFGONNA zs Teubtsies agGONNd TS FAdIseLIeS aFTGONNA oS ASeLPUTA agTGONNd 60 yseluey ggGONNd SP ASeLPPY aAZGONNA Ly eweNputy JgaGONNa oF enenbug JggdONnd Sv Itelwey agyGONNd ty peoqwey gJgGONNa ey Saouey = AFTGONNA tp TTeLPPW agaGONNA Ty peoyppy JgGONNa 0% qesul ggaGONNd 6€ Asqugecesq gJaGONNA Se Asqugooltw ggaGONnd Le WOWTTeAY JSIGONNA WweWeet1g gJgCONNd se SQYOOTTW JdadONNg vE WEWOOTTW agaGONNA €€ eqecoTteeq JgyGONNA ze SCOTTY ATCGONNA Te esnep agGONNA o€ Jemiegquyuey gagdoNnnd 62 Jeasegqulppy agdoONnd. 8z J0_OeAIUTAeS gFaGONNA LZ e7eI4S4eSn aAFGONNA 92 eqeqsuedng gagadoNna SZ wsaes agGONnd bz 3Tuuieg agaGONNd €Z PTqt0q agGONnd zz etqeug JadONnd TZ erqestqd aJgGONNa 0z Bnqeq agadoONnd 6T Welty aJaGONNA ST qUepTseyITUI agGONNd LT queptTseyputy agdoNnd OT suotjounjereW = AIGONNA ST Axesqy¢y Teen agqonna vT QOnaAASATUL =gFaGONNA €T @popjztul gFaGONNnd ZT uot}deoxg gagyGONNd TT yozedstq J3gdGONnNd OT yoqtasg aadONnd 6 e[npeyosey JgaGONNd 8 etnpeys JaGONNd L AQUIATXY JIGONNA 9 Jostasedng aadoONnd S XTIWDIIWNOLNY LIING ATIA :LIGA ION OG eve B $ Gxgq [4e9 £4h:60:8T 80/0T/S8 G°Z A’ TW QTT-ueb :41EpeoHs yyy S86T 98:0S:9T ZT AON ONE UO pozessuEed OTTJ STUL vey Z ‘our “ebTUy-SAOpOUMOD yyy T

T a6eg F°QrT oexe/oexe 986T Lb:77 E UeL

A-19

uF OF/OSXS,, AGN TONI

ut SSOTASp/OSxe,, AGNTIONI ut SeTsaeaqt yt /oexe,, JGNTIONI wt Sse /ooxe,, FGNTONI

wre sq10d/oexe,, AGNIONI

wt Asoursu/ooxa,, AGNTIONI

ut Sqdns1eqUuT/oexe,, AGNTIONI ut S3ISTT/OSxe,, AGNIONI

uf sepou/oexe,, FCN TONI

ANNO oe WM WOE DONS of

t obeq F:oexe/oexe O86T LB:'7Z7Z E UeL

aZIS AI ’LNILAOSAI LONALS 9S 3ZIS AI’ NIGNSAGAI LONALS SS aZIS AI’ 3aLAI LONULS wS SRZOSAQUT §‘TIEWI es Zs PVECCEVELEEEREREELERTS TSE E PETES SEVERE EE EY POQETOY Adnz19QUI geveesy TS 0S 67 eaoge eyy JO [Te Aoz : mom dim SP Lv peaseser / AXAPASY WLidv oF quouHes elep queTte ¢ e7eqqeTW ULdv 5 queufes ejep sebbngep TeqoTb eqeghnqeq ULdv by qutod Aaque sebhngep TeqoTb : Axjugbhnqed Ld ey xeu A1ouwsul [BOOT peVe[NolTeo AseT : WepooTXeyy ONOTN cv (punoq szeMoT) oeAs weqsks Jo doq : Jeaopassks uldv Tv (punoq seddn) sseq xoeq3s weqshs ¢ seddpasshs wLdav 0” 4O}09A Gungdes Ajos wiem / eamnqdeyuiem =ULdW 6€ AOROSA Sunqdes AOS [ooo ? emydertoo) ULdW ge A0RO0A BiNnqAdeo AjJos pToo ? SANAGeDPIOD YALdW Le QueweTduoS uequTod eseq ue sks ¢ eSe—EYlD ONOIN SA0R00A destQ 00089 JO uMsxOeYO ¢ UMSHIOUS_MOT CYyOM se Jequmu esee[eir Are_syoTH : JOAI30S GYOMN ve e€€ epou AxeraqT{T paepueqys : aZIS gIT’esegoouxg AINIONUIS ZE Te VEREEEREEETEESEERPESE VERE SESE EE EEEEEZE SOTCETICA WEASAS OTIEAS vevseve OC 62 _ 82 I SSIMWaSIT OdXa SCNT 42 ut Sefsesqy[/oexe,, AGN TIONI 92 I SaIawudIT O3Xa ANAI Sz v2 I SIdNYaaINI DaxXai SANA €Z2 ut Sqdnszequy/oexe,, FAN IONI zz I SLANYYSINI O3xXa ANII TZ 02 I SISIT O3Xai SANA 6T ut S4STT/0exe,, FAN IONI ST I SLISIT Oaxa ANAI LT oT EREVEVELETESESESEEEEERVEELEEEESEVEVEVERE SEE SE SEVERE EEE EEE EE EEES ST s OT $ [TaeO :ABHYOOTS xy ET x eT $ Gxq TAeD TS: 0T:9T ZT/TT/SE T°T A’F°eseqoexe -40pecHs » IT x OT >TOAQUOD BOANOG ys 6 s 8 SRELEVEVECEETEFESEVEFETELEE FEE VELESERESTEFE SESE EERE ETE REEEEEEEsEseses L xs 9 SiTgI Epnpouyr eaTynoexg we sks HuyVesedo WOU -- “Our ‘eHTuly-s10poumOD y ¢ s BD PEREEFESEERE ET ESEVERTESELESESEVEETESESESEEFESESEVEVEVEEVE DE EEE E¥EYE CE T LaS I aASWHOAXY DaAXA 2 I g3SweoaxXe axa ANAI T

T eeg F°eseqoexe/oexe 9961 LG:bI T Ady

Aseiqr quedo aeEOeA @Moo1g

wey OedAy, 20329 quigoqney ACYDINGaey aeyn epAepney YFUIOINEY eo.noseyuedg So.NOsewey So.noseyppy OIqt0qy OI3TeEM OLPioeuD OIPUsS

OI°d SOTASQSSOTO SOT AequedyO SOT ASQURY SOTASQPPY Axsesqtyums UOTIOUNIIOS Axsesiqr Jeso[D AseiqyTuedopto Axserqr uey Aserqy Tppy qogput ¥

FAO GIT eM BsyAT doy Bsp32D Bsyang

FO que Y A0gppy

deszysesaz .

deszlLoot ty

A - 20

z eed Fat cexe/oexe O86T LZB¥:7Z E ueL

aZISASVWESAS Tadv I

OIT Oxh ‘PeAtesoyosegooxd ONOT 60T S0T beh’ WOTWASeT IONULS LOT 90T SsdZIS HS’SQUIQZOS §=6LONUIS SOT vOT AZIS HT’3tTemisel LoNUls €0T aZIS H1’ApeeypiseL LONULs ZOT AZIS H1‘3StTH40gd «=6LoNULS TOT AZIS H1I‘3STIAFI =©LONALS 00T AZIS HI‘3STIA3UI «= LONULS 66 AZIS H1‘3StTOoFAed =xLoNULs 86 AZIS HI’3STTecmnosey LONULS L6 aZIS H1‘3STTON = LONULS 96 S6 SRSVVEVEVH ESSERE SESE T ESTES Eee ee eesesyy SAOPCOH IST] WOISAS yesessy 06 €6 26 yseu de1zj pezecotteeid / sortydeszpiseL qaomn T6 yseu Teubys pezecoTTeeid * = = sol TwOTSseL ONOTN 06 poo 3TxXe Hse} W[Nejop ! SpoogjqTxgIseL ULdW 68 @poo uoTAdeoxe ysezZ AINejep * espoojdeoxgy7sel UldV se Sup {nor derzq yseZ ZINeJap : epopdeszpyjseL WLdW L8 98 Aeixze @[Npou queptse.1z 03 sequTod SOTNPoWsey wLdW sooo uofTjue ze Burp—tnpeyoses ¢ peyoseyuaqw dyoMn ve N sHeTj uofzquezze Tepoeds : sHeTquj3w aaomn €8 z8 qunoo Huy seu eTqestp xse3 ¢ QUDISCNGL ALAA Ts < unos Buyyseu eTqestp ydnas8quy ¢ QUDISONGI FLAG 08 sHeTz weqshs ost : sSetasAs daomn 6L SOT} umquenb Questind ¢ pesdetg qdyomn SL umjuenb SoTTs aut ° umquend dyOMn LL aequn0S yozedsTp ¢ qunopgdstqd ONOTA 9L ASAUNODS STPT ° SUNODETPI DNOTMN SL 4SeZ QuetmMd 03 sequTod ASCLSTUL ULdv tL

PREEEVFEFSEFEFSSESE ERSTE EEE Reese SOTAUEFACA wo sks ofureuAg yeyee¥e ZL

TL OL

AZIS AI’IWNAI JoONUuIs 69

AZIS AI‘NSINIAI Zonas 89

-SZIS AI’‘USIXSAI LONaLS 49

AZIS AL‘ONXASNSGAI LONUls 99

AZIS Al’ aaYAI LONaIs S9

Y aSWaoaxXe OAXai SUNT OzT AZIS AI’ECGNWAI JONAS v9

@2eY HOOTD 6 nOtl -ZHOS GaW 6IT AZIS-AI’ZGNWAI JONAS €9

OSIN/Twd 8 ndd WWd daW 8tIt AZIS AI’‘TGNWAI JONUaLs z9

v NnOl Teeso daw LIT AZIS AI‘OGNWAI JONaLs T9

T NOI o07089 Gav OTT AZIS AI’LITAAI JLONXLS 09

(TT@eK se 07089 AOJ QOS UFeEUSI IITA) 0 #£Nda OT089 GaW STT AZIS Al’G@LUAAAI JONAS 6S :S1t0ssecoid-op pue S10SseD0ig ,y PIT AZIS AI’WaAdOOAI JLONUIS 8S

SHETIURAWW sy¥zseee ETT AZIS AL’SLYOdAI JONUIS LS

ebeg F°eseqoexe/oexe O86T LS:bT T Ady

z ebeg eseqoexe/oexe 986T LS:bT T udy

(3334099 (z\) ) Mod (33089 (9T<<(z\))) q@‘od 0D0$i0\qdnD q@°od

DANA

LIXaW z7\ = ad 08$i (®\dNd) g°od

ssz- (z\) aTII

(®@\INNODi (b>> (T\))) Las O\ao Sang aN Las ®\INNOO ran AY ONSII Dana 0 Las ®\LNNOO a iw\, O4dI 0 mM‘Sd QUNODP ‘ONTeAP’ASSTJOP’OZTSP y OWN JLONULSLINI WONd z\ T°Od ™T™ Mm'od 0 q‘od 0°$ g‘od ONTCAP’ASSJJOV y OWW ONOTLINI WANA Z\ Mod T\ 8 M‘Od