(@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
v¥ 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}# J» 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 9¢ } 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
w¥ 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\# 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’ T° 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 9¢ 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 o¢ 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 T° 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