çSAP45 AZMLÎSAP50 AZMNÒO'SAP50 CZM Ü…SAP50 DZC(~—SAP50 HZS: ˜SAP50 NZTS Êÿ vþSAP45.ASM% €ŽÄ2 @@v DÑlÞr:Â�„Æk '#)Œé<ˆaxÈp/ � ¡Ü´A)‘Hà�Lœe2™'b4D@M ‚IœÝ2ˆ §#•ÒfNf“qž‚A!ËeòáP�I)ÅŠDò9H‚Mˆ¤%ŽÀE�؉w;a ¨O)kö‚,§E)ÞŠ—AÔA& ¨I!≅šåNµ‘JÊÑ ˆóW™L–.2‚©8«3"hÊB-£e¬¶L´z\=Ì[—»ÞowÚÕÔƒlÍ‘¯DÒ P¨EÙ•É6I‘Øö"9j'ëãoÝ‘OT X,Y}¼ËAd"•‰=Ë’L¹­?ºÂ!‰+.LxŠÍ‹jð$ŠÎ{¤-[);B;0"²â ’á¯Kà’ß,JÈœ'6íŒ#6ëœ'/‹t' ‚ŽÐ=,š¹ â40Ä­�Ò¸®!Œ$Ö ‚`ž+¬a<6)=æùßïüÅÁð¼:¯ãñ|o—ÉyÂiE ¡¥¹¼ÐìôA  (d Þ¨p á̪œò�Boð‘†PðÝC£v „‰¿¬ÃA~…0<ðêÈ#Éqƹæ90šåKQ@|.XºG¸éÞñ-%¨(&¢Êùš ³n©Ü7vòÝÃa*…-ø7rxÖÙ .@´SCx-ke]¹“æþÈ[Ó½ Á™ 'pÍÊ)@Š�–+X¶kz‹Ñ€2Æ ÑÛ"zïÐ3•gîVôc@µ=•êA£Lk(̉¶=ð#£PS Á›¾ˆ’íŸ[¹oÄ0¹6˜â O”5ÓÄø¢ïHA!Ž¡97—\ x)ñê>GèÍ C,ƒx¯âC†ˆë"SØ( ,ˆ«Jàh J6{’}îʆîõÃh#á®:„hʃmŒÏП‘àA[‹Æ—~0†xÆ£+"2 Ã…)ä‹Û"m²gÄÄ C“NÉ·Í–éÃÌfnð¦ƒ@Ãxmˆ4µ©k) øo �Ô4IF ¨:mq�ħØ(=%…!••†�ÆcÙ˜`‚�PÆ»CCy<n¨®ƒ²le#**m@9Ö ÖÉ=k­}°‡6dÍ'å;X0‡&ÀUÀyl€6”Àâa!c½M)ÊÑ�CŠmÑüºæ.êÜ` !Öˆ¥³ä .¸˜rÊ€H'!¼:‚ÚÕ'ÁL€SaɶØkI@±�å×J NQd  œ˜e+\gà ®Ê ]�q3¦uì)WÚÿ`lO ÏY�‚áKÐb³2и±)"�3³U½!°�gí ±¬ÖŒ¡›@GJmg6¬½ZÛa,5µ#6"¹8£o ¿(wÐ\;=[‚@¼áØ7&]˜B‹õßÕ:·n-˜v#ä„9Ûà¿o\ ¡ –…¤ˆ£°/¢þ–0áUs ˜8¬g Hn%¨Ð*ð–ì�5ÁWı‚ ÎíC#u#@�=á@Ý…› -&kp)g`ë± ò¨·ærÕKðO°­JÃ)…)7Hi½ø‰ÿ’šU[ Ö¡µú9€$!̳©rã³Õ¹�˜4ŒV*³w’âñ�s™÷ÞÜkŽe¬¸L&ŒH$À`·7 I”ÊåÓZO9M�ådÃzïu  Çvm=®:(í�Y#‹“q�† 0&é\Ç|„”› YvaR98¢äøhtáÞZ‘îFe8Ö"¸ÜN±(E9Åúç6òmÙ„9±vgK¿Nw^K‚ú=Äï6ˆ'V=ïi�CEr�ŒÊ݈ë÷ @`ô“â� Bi0 A8-\�Lú,"nóÓ^åCe'"r¶OÀ„[—è:ë{ß®õôcØw¾ù÷·Ä(0²ÛH'jx=·Ûïpïȉ¦>š9=4 Áw/<�&wuûê!¯½ ~4_Mð991Ÿ�Ù„‡I 0mõÞi¯óð=H!¸ÓßiÙ½z#;î¾ûÚ ZØ>«Ý£Ç¸šžX9ƒ€�ƒJÞƒ õ&°8jÈ«jÍ»ÓÉ»ãoœc§€K!‚0& ª$/Ð¥ &“'2 ñºA¤ªi¦)Ù�6·bذh7ƒ89&ùᬯƒsˆ é¾>ñÈCÆ?)!&Cð�ÑJC) ¤2‚€ãÃ(+Ã;óá:ó¾+˜>8À' Â$2¼qÆSó¯JôÂ0—Y¤ÂPÕžÀ:ƒ§¡h«+¦©:³cÃ±Ë ÃIK¥µ0±n´P!9³Ï ‹Ô„.Åx(‹×=‚3µÀ�Dã§°¸�Ûð�c 0áÙ‚zí›ñ¨ˆ™©Šr—§*•4¸‰Æ+ˆ? »D Bô_?qcF` ÃI1ÆyÙ‚‘¸�±"ÃH´˜�Œ`£-‹ƒ=!ÉÄÊ1ÜNƒ*J8“É<£¥#T�¼ÁØ‚«M 3p‹D\ÄÕ? ñAô©ë 0¢ ؃!ÓÀ´³ð;ƒ<£¨"ƒdj,*¬žÀ«8zÙšœ´9Z¨HqéB¤H¹ŒH¹«ÂÉ‹Ð×A‘MÁ¡ð‚i×E‹ƒ#ò˜�Œe‚cžFX �`D(.„" €( ŠL¶z;‹;ˆ$w‰)-‰•1Ò‰ð§ªÛÇÃ%ÈyÆ#T¦ÃüÊ`Êè'J„¯— Ë¥·À›bŸ@'™\¸4¹ »%¼— 8› s|F[|��ôò+›šØ¨cÃ�¹‘;zŒAñº »‹Ç@"�cÓ¹c½8ªÒ?|‚A¦¾”5œdÜMÒÎ<„Ù­Ô ˜rÔ¸$¦�DÞ�HTä8²áB+ˆ‘,L$×1¤ØM”N("ΘΘ$ )M”ñ à¹Sȸ "ÎPÎLãÎX�Dó‚lçΉÆtðÎò¾3™K@ÑÌjª»kÀ:—bËÄÌűÆ?|î „ñËヾa€‚�%¬ü¬Ç&AŒùÙ¥”Tš´+Éz< CJêü‰›BP‚jʽÊ3nŠóM3ôHB´,Nô :Ñœ¢»èJA´G<í͈˜N¼ìЄt7Ìî«|ÚOÛPœõÏ$¾Ž8Ò¬ÜÉ´÷9täO‹ƒ�E,‚dýÌþOôö¸ŒÃ7ËÆ…�2ṟø�ºØ¦ÄQ6åËÑôGx:Gˆž™!íÐ[\Ë­ÔíÌØŸ@#�¬QFÊ QM9L¹ÆR\±¿}'ÒØšÁ£ƒx;�O?´Q4•Hˆò,Ô•DP•JtìTµ'TQÙ˜õOP?´@üâO|ðФÝбrÔÍ.ÕÅ/Ïå0ÎdçN…3Ï•4έ·ÈL]8%I*Úa¦\ÊÔ›|L̽¹4×ÒMUUeDR|ÏTêRÕ �ÏE/M¬úOU.P¬½W<ßÒ´êNÖùV$ûOÅ3N•*W•CÄÃÐÆK}P»RÐÕ=LÕ&ÒDôÔ­pUxœWY¿µ‚ÐÍ_×i€>mƒWO4’šŠ²'"È1Á ŒGÿ«•M«¢µQ¥!šI™É¹ 4%ŠYÞ5…5™øž w=ŠØƒ;7`6É­¬ 2ÁkY©Ò3œø¢‰åD�;…O,*kÔŒ 6)Ñð UÃiE›!ý�nB k¨x8!jZÁiˆš Lx�ˆ¸z: je�ˆ”i’"»Xˆˆ23ƒzKëÊ…EÔlzTˆÉe»3ÔÈ¿-¾@*«‘ÙÔ`‰ÕzÜ"Ê+ª1©•ÓÉ ¹mt¹zžBà)‚ªø/UXW}?°Ë�¶\1‘<=Ì\Òý˜Í“DUÎÏ„ú)à½EœY�˜1šÚ©½°§I1Œ¹¹ž¹Ð¨$È‚x)‚!Ôýcºšƒ¢ÂÄ"N¨œ˜ [ÚƒX¤ÔôСEÌžÃuˆ‘‘2zQŠbüBp0±)‘¬Ȫ˜<•ìÒ!Ó³T–87›#y¿|«KÑ4em?{�ÝåˆTõÔTø†¥(¢�Pª±h¥‚,Ò'ÛˆßûøŸ@!Û²à[û80Û˜‘�V‰€´?M‚©Ù¸3Z¬?±º!1‘À² eD`q"ÅÍ -Ž_u½áEŠ7±ê'ÞŸa||»PTð1a¨©GRyàEàán%ሣ£î([H©UkJžÔæƒ32~ =½¸Ãʹæ[Ná~! V"n#@Þ 4baÓâvbèÖTFXÞÔþ BÏÏ,¦�€&’`»¾pÖ “(Í\Ñ=¬+‰êªÚ[ œd 'ß`Ÿ"ɨ,ú UŒmÚeZ{lƒp‘á ºÜ©Áˆý¼¡¥Ù«Y u¶Ù‘D³¥Kìü\‰ÝSUe‚+åºý5£©q‘a(PÔ­>#’‰1ìÉæ~Eš *‚h#ádˆ)ÙêZ™0¬ ¥4åö)s©µ´C(²µ+ƒ7ôNƒHžaÿgi5–xQyg¥@œÈß§?¨]VWÜ1áå˜9[Ñë ОPb:å9¨Y˜©@¶NYÒeYåŸý ŠZpZ-á×]M*Ø–† ØžÛÆ…[Û‚¸>FäyMäŽSàUžÙþŽ[m’K®”ÛÕtR�u] Ñ�VBàSƒlýÞÅIýzÝИàÍßœž$5››9ÅãÞMå�`‚Þ}èÕš™ Eꩽ’ÓSÔ@�›‘À&ÈÁÏhhö›ÉŒŽ­ §¸}H ㇚ó ÔÎ"i•Pi5UæFfÖ—Ðú$g ,$(²ž„(v•ø2�9àÂ~¸âÈ?Yi D¥én¿�æHGp�äæšhÚŠk`¥§ŠØéÐ9¼ä̽&ƒ«Lg©ÌÌĦ½¼×.ÏZÐY˜í?µŽd cNæ\­ËîFæàþæþÁ¦N†Tð7l1æµ}åÆÞæTIÆ[áôßK$<Œð¿ë);Kµ­Ó·laàÈþÂ.µ©n;Ú­ƒóEÛ¶mœ�Sð9aø 'PHPz¨)ó‰XúPQÔI]p f8‘hDQZ¸C&?s¿¾&ëS` Ù ‚pš¾~ï>“¹:rÌ*ð2ÅÝY°<æ|.D ñHŽ1"è§ê¹»Š³¢~cnvÞU(2f}/‰&=sžl&V¯ð7»ysZ­ 7ïù»ò3C„‘šNldvP¥ãF_PµŒÐÎ`�´).Îå*Þæöf¨Ø»Rº†ÁDñ‘FÀ�gSºÍD8Þ/ÆfÀ»EuƒP �¿lô1$áÚ÷ŒmiÒP?–köGlΘ\qÚ÷6êƒeŽÄe¢Ply�ÛÞl`!‘�l1êeç#P:%øÅO�`!\bN_5È_©fnkËè·O9ô7Bdy´—Ž'á&8(l´¦ø¦øŒk�yž�RUUÌ#3ZPB¸ßºî¶ ½GÜÇ"ðØ3z®¥þ Ô­qz 0íC6ú;¢öŒ†" R|¦Æ'$�¿_œ½ÉÅßËj¢Áઊjd‰êwZ�fZPdà"�[šh!�ÑJE‹˜øôA?1§â|Ÿ!r>°$ǧ÷u™ž`< 6×Òelm†×©ëË@àÎ ¢¾5PQçóç–9\‚·Æ¡kÕØÀbèëG�QµzY¡uò¡ªaÅWˆ%F=ÏAªŸzsGó¬Sñ‘\3‘‚ƒ{¹ËòòOÆÄå¶±‰ï2ÕÂz‹+Ô=Yv�Õž¬&“mÁO¹dÊlaùù 2˜x"¸ݹí{�CúW(4 iÛžXxëÀ0¦0œm'r9käú½DŠ'Ï–?:$p§ÎœòÙ á�gSðf‰Ù‚‡$gQ[¨v…g^u�ýºžà¾³x‰Ð ^ƒ�D•#Þ‡1®�J7¸3ÐyÍ2œ³ÿТÍ,¬»ýã™bžhzw|J³© !½ÿ„¨u´##Îÿ¯ò¾œ3Ql]�˜%z‰Þ H7jtÑuM¥�6ЧÏrz—’Ó$²5à°ÞKUˆjÉ¿²²ø`bDÞJ¦X:¸7�?x[­§íÅÝ6ëÕÈÜ–&½à[R2ž³A”Cõ#%°¨j÷$TÔ�)&öúËÇñÛX‚AfÿƒÐL�Âø¿tqèwTŠ·¸˜Aôjdƒ q‹Û7[P Ä'Šo™±ê†# 2%å½Ã:ïæÔq +�zo'M¬•:#íiâr› ê·m‚.;Ì'¨²¯³Õü¯¼ÿÙëç#vÁM)C«ú8£z¯¶ iv�ÐÀ�µ ½¸øœn\zÜ2’Ý÷]—T›Ð`7ñ K(oûðø8(ϸ�vú s±¡ÿã}¶Ø£\�à‚*_Võ&{ž¿"EÇò¡¡vú èM¥Û¿ùÅE˜�έOåeW�"aÄÊÔÁÊnÍÿùÀаž\à4ý»|7ð¹?´Öœ¨¸&àæÖnn#ùëÒ‡Iµ&ëA³‡±7²]&BƒCúÚqÀˆ{£Wë¤ÒŒïÏŒúû�àj†�^+œûæªûùì1èg§Âj÷E^ƤN"`WYÈœD‚f�.6AÛ-öN1JQ‰êx�t€Š5r÷03&ò“.€¶Mõ–„€7@)ó¿ômטÄ5>,qLò‘ØòȃŸG�<ãâe¢|j"ω9ó¼eP‡z§>CQWteÄÞÒl‚i¸ƒÔ+Çç;É÷Ùîþfôq�Õ>Âo)2PlŸX¸)äh»‚»te;ó–z]R¼îŸ Á �¡z€dh»‚¾«Yð6¬pgA5gËñÑygêvÒ½ dh»™ù·öÉ�í_ªà»ùHckw3XÔ÷1�©œf¥¶¦-*Ò­.½]M‚¾Æ°Ñà¯LøÂQ2ƒÕ¸®š*ø‚rÝë½Mé(9šˆ�ƒ¿î(®âj·êªòo<±»øßrÖ±þÔ«^˜¯¤ùÜ]Ò-¢ÇÂŒÑ4LA,eA­Ñ™ÍDJž)ò~O !`A�f)ê;¤ø´÷Ÿ·¸<§^u7Ʃ旿^aì1&gs¡¤.°V]‹‚˜»¹­¹Ûr¾Ø)ÃP+_îlAyO<[«ñkÍr¹~»ÔØ‹¿²ŒAI .’ÅD̽S·YVÅŸ0}÷\<�Ê0×,»?²‚ð®×°ý£ma톡kЕ¥.ÿ@`dTÞCP+•‹n¼Ây]Æ?Y`‚(vœÞCP+¥úÄ5°ª#ÖÕ iCiFY<Š•�GLËøäô‰AOIJ7¸‚7|:î1^ƒûCƒ’Ó¸#&$å‚ ìVÑJG0#=G¿´û‚) 4�Ï%Ñ¿œŠ­”x‡Þ€¿´R�‡ô(&.‚*�}ºžÒ\½û3L¿*¹ô„r: uOœTQWê_ìþ×âû·@"¬Iæüæ>ÚwDKЇ©²ˆ*™®›5}reÌz£šCÚ� ~Q�™±~“©ÚïÙÁ{]—Û©îlA|ÐM"‹Ð*Iª¢¶&³Ç‰?Œò]œ”±Þv½D`²VA[CV©t«RK˜'m}„ú³‚Mä58ò¢PáJ� "�•>zl4ã²,›q­§n:„ÀÚЏž¼8ž›‡œ‰ã6€‰Z<Ïä®8§ ‘‚N7X«e«(2–æ ’kªœÔtN†¥¯Ç�Ëà|Q›òÈê´t† �ɵØi kt�¾¡Ð$:kõD€,»�}ý‚Od¶Æ1ëg k‰ëh':ŠÁ°Ûz·rÈêÌ'ºìôÅ”ì~»yôXi æ×Q.† ™�€”ìz0‚Xi mug�RèfŠ÷úŠÜ#©ñ£$Xø¦²:¤®TTEÇò¡§,Ž©ûHÑ_Lø‚"Ì“©Ïâ\êEÛO [dh»ÿ�“˜‘�öàŽäH2Ôôt°ˆ:²±‘R;ðüGí7Î)Ê’®Àr|Øc%,2qªÞ}Yˆ}þ•´5I®×Ån\ä4}>°¹}Œ�W«gæÐ»ÿ�xMÎ"y�¨´OîAx½‘”@…M¤î¨ nËÃÙ9ÞªûúØÞuè85c€OÂ$,Àל‰À‡ ®‹æ‡§GÌÞCTšã$5°ãôÞCV€y=d1[zTF0”mq²ž¿~PM"‹Ðù›óEØì!¨äÓ=–™£t‡ÐAÿR'¦eðM­‚Z7·»üs]r¿Ã8ß–0Óu=�ÁÜg"‡uÓø0¶Ü¶¡mHÁÿ“‚~WH€L°$ŵµs¯¢}„K@íaí©ŠàÎ ¢ºþ&ÎÈ/Óû>½]€¨‰,»hÁPë’Ú:̬þ ”0W¨�å)¦ÖtJÙ�8<ø¯Ñ7¸�‚ñTPs{�Œt‹8iäðÁ°%y¥°`:Ü8À:me™æ`< M—SeùùzoÛ©ìÔ™s"ï,Š.šÏæŠ÷ú‰»ÞÎu~s*)uéäðÅ—ÄÓæÎ\Àžžf³œ£ºš&åÂú[�b[¯¶TAgø(‰ã˜‰þw¯�>RÜ@Ëþ[ÐAfÿƒÎ�<ùN5¹0;eƘ0•kì¦Ö X"áô˜ßt&ç9Ê×ç�ßÿöÔ=`.hzw£&’Á=i³¦à¤˜þfñl×/çG«ì&ò“iäðéþ v¡‚gµHõzϯ¸ñî=9°Þ^G¿hê‘^Ž­ºmëX!4ÔØ‹¿´üLŠWäãËÛA¿Š5rþ¬n´‘q¾! …o¼½ª‚©*¢Á(ƒªÛ)pµÂ¬I7‘ g–zGÕ@�‘Œgv|‚cà`ãÁêv+iªa8äÜ)–?à‹@)TE¨byiä骆;±ìH¨È: ÐÄgB¹Œ9:Ý–<ôº|ìVÕJýºç‚'÷Óí»á>É+r¨«† ‘®Ç°ZúìrX ^¡Ã$ýÂΠ-Ùcþ ™ÍD©cÇÍ.{÷Ô9l2ºûâ!§ƒ,é°} ¹ôó�¡ —ÕZB·;#R¨,Úíâ¼óø–ØßTÖDø‹}ŽwH‘‚²ñ^(� ‰  8”�À'ƒFyãacc‚š~p™WvýÉ‚’.J8 nµÇ|Q@¢•ã—±€½¶9+ýõ‘ÄmÄU\K‹`ú´Ñ�8s‡9`ŠÆžÇ;‰Ù y^9ò~x M‚¿(œF²&b·CÜ>Ž”Ñ�?)la¿ w•,“‰l1+äÑ�8Ö˜ÅÁH"Šp x¤¨jÙ;lç �Zx\š–ÞÀUŒ+äÑ·ui‚»P�°§'Xï6%ŽqŸØ3=ŠØœ-ïÔêØ¨qÈ  Š„¶'ž.BjM!<©J�SÌJAÅ+kä—% Íž6b7uiŸ�뢎ŽS¢>É µ@QjÀh¨dÆ¿ “EÄŒÍM‚©±A*–­ì�DÁh7,j¶‰hÏnÐÄôÑ·uiʸ��= “àbx4`Ÿ�àç‘ZŠ>î: ’‰šØÕ_·ÅÉ¡/#“ú·)Ù™ãp¯"g"©ñŒ+-›à |²Ó�ÇvÔ*ÛóW×7‚ý2þ™ÓXj7ë#iRÊ{03‘Ó¦²ñ"Æ 2ƒˆ:¡IÐø*j„ˆó�X{ß³-JxŸœ_³-J}¹­aœ_³-J|³°—š‘•œ¨Ū\´vhô§�“r‚Ai/Uû2Õ\�£gß³-UȺ‰¨Pgâ0íû2Ô§�˜:‰- KR”ÙØiY�X³Ç’ßÉ”Yß³-JxÖqǒݪ l¦ß³-Jx°IÇ’Ø––êõ_³-JxIÇ’ÚÖ ¥Çb…X…Æ€7sªeÑÁÍ Y-š(ƒ"¶Êë.ß³-oèoÇ:¦>Ì8õ]l]K 2ÀÛÁÈšß³-oëzA Õ¢­¦[·ôE§zÒ�Óiw‡ÐûTæ‘•˜�¡„ˆ)sƒ.»Jð-Œ §"l#"¾Rf’­BE3AÅI‘µìÙòªhå¤$©rܽ29~`_³-UËwß³-oì�‹Á8—-Jx–Qòæô‹·Äô@*$ùÜ4�6Ê…rÝV[§¦eáßhÿý‘[sˆ}'bˆô–*ÙòœŸƒŸdµ ¤W›5ävS%n3‰yß³-J{r ojŒðšß³-J}¹­¿ë=¹YÊ€‰oÚ°íû2Ô§Ë>žNŸ™_³-Jx�¿Ó*‡ae”–:bÙò¨šßÇç;ý¦ú_³-J}4W‰M¸ýû2Ô§«ZJº›ß³-Jx·-Ьš«Ìˆ±„58÷ZJ|³ `½1ÅjJ†öÄp½‘×ß³-Jx“8)äh»‚½âiY�YR,ø·€KU °Õ–°«}“(ß/Ð á¯&#Q�*E@X©a 8—·ß5ñÝŽ_\ª(‹½¡º&ºu° nQÉÕ)¨¬.Ê©ñjÕ(:,lˆAÆJJ‘�¿�€h o–+“#"Ï®–€ÐHЀ'ß; SAP v50 -- Sort & Pack Directory, 8/7/86 ; ;; .Z80 ; Needed for M80, ignore error otherwise ;; ASEG ; Needed for M80, ignore error otherwise ; ;======================================================================= ; ; User-customizable options: ; NO EQU 0 YES EQU .NOT.NO MAXDRIVE EQU 'B' ; Set to maximum drive in system, in UPPER CASE! ONLY22 EQU YES ; Set this to YES if SAP will be running ONLY under ; CP/M v2.2. If not sure, set to NO. ONLY14 EQU NO ; Set this to YES if SAP will be running ONLY under ; CP/M v1.4. If not sure, set to NO. CPMONLY EQU YES ; Set this to YES if SAP will be running ONLY under ; CP/M, either v1.4 or 2.2 If possibility exists ; That SAP might be executed under MP/M or CP/M 3.0, ; Set to NO. ASCENDING EQU YES ; Set this to NO for a descending sort of directory ; Entries. DELZRO EQU YES ; Set this to YES to cause SAP to delete all ; Zero-length files if SAVDASH is NO, or to delete ; All zero-length files not beginning with "-" if ; SAVDASH is YES. SAVDASH EQU YES ; Set this to YES and DELZRO to YES to cause SAP to ; Delete all zero-length files not beginning with ; "-" (as in SAP40). Set this to NO to not check ; Filenames before deleting zero-length files (if ; DELZRO is set YES). ; ; End of user-selected options ; ;======================================================================= ; ; Various system equates ; UNSPECI EQU .NOT.(ONLY22.OR.ONLY14) BOOT EQU 0000H ; BDOS warm boot vector. BDOS EQU 0005H ; BDOS entry point vector. FCB EQU 005CH ; Default CP/M file control block. CMDBUF EQU 0080H ; CP/M command line buffer. PRINT EQU 9 ; BDOS print string function. VERNO EQU 12 ; BDOS CP/M version number function. SELDRV EQU 14 ; BDOS select drive function. GETDSK EQU 25 ; BDOS "get disk #" function. ENTRYLEN EQU 32 ; Length of directory entry on disk in bytes. CR EQU 0DH LF EQU 0AH EOS EQU '$' ; BDOS fnc #9, end of string marker. ; ;======================================================================= ; ; Start of code. ; ; The code has been rearranged to make the flow linear. Thus we no longer have ; a mainline with several calls to subroutines. This optimizes execution time ; and code space at the expense of complexity. C'est la vie! ; ORG 100H ; Obtain certain BIOS vectors. LD SP,STACK ; Use our own stack. LD C,PRINT LD DE,IDENTITY ; Tell user who we are. CALL BDOS LD HL,(0001H) ; Addr of warm boot BIOS vector LD BC,24 ; Offset to SELDSK ADD HL,BC ; Source LD DE,SELDSK ; Destination LD BC,24 ; Get all BIOS vectors from SELDSK to the end. LDIR IF UNSPECI LD C,VERNO ; Returns H=0 for CP/M, H=1 for MP/M, CALL BDOS ; And L=0 for pre-2.0, else L = 2x or 3x. ENDIF IF UNSPECI.AND.(.NOT.CPMONLY) LD DE,BADOPSYS ; Tell user we can't work with MP/M or CP/M 3.0 ; This is only printed if we detect a problem. DEC H ; H=1 for MPM JP NZ,PRLAST ; If MP/M then print error and die. CP 30H JP NC,PRLAST ; Exit if CP/M 3.0 or higher, we can't use it. ENDIF ; UNSPECI AND (NOT CPMONLY) IF UNSPECI LD (VERFLG),A ; Store the version num. ENDIF ; ;======================================================================= ; ; Select drive, load disk parm block, and calculate free mem. ; SETUP: LD A,(FCB) ; (FCB) = (0=default, 1=A, 2=B, 3=C, etc.) DEC A ; SELDSK needs 0=A, 1=B, 2=C, etc. JP P,SETUP1 ; Skip BDOS call if drive mentioned, LD C,GETDSK ; Otherwise get current default drive. CALL BDOS ; Returns current drive code in A. BIGDRIVE EQU (MAXDRIVE-'A')+1 SETUP1: CP BIGDRIVE ; Verify that specified drive is not too big. JP C,OKDRIVE LD DE,BADDRIVE ; Else tell user it is an improper drive spec. JP PRLAST ; Print message and die. OKDRIVE: LD C,A ; SELDSK needs drive code in reg C. CALL SELDSK ; Returns addr of DPH in HL for selected drive. IF UNSPECI LD A,(VERFLG) OR A JP NZ,CPM22 ; If ver 2.2 then jump to CPM22, ENDIF ; Else must be 1.4 ; ;----------------------------------------------------------------------- ; ; CP/M 1.4 routine ; IF ONLY14.OR.UNSPECI CPM14: LD HL,(BDOS+1) LD L,0 LD A,(JP) ; This bizarre inst loads the op code for a JP! LD (RECTRN),A EX DE,HL ; Store ptr in DE. LD HL,15 ; RECTRAN offset from BDOS in CP/M 1.4 ADD HL,DE LD (RECTRN+1),HL EX DE,HL ; Restore ptr to HL. LD DE,3AH ; Offset from BDOS to 1.4 DPB ADD HL,DE ; ; Note that the previous LD DE,003AH zeroed out the D registor for us. ; ; LD D,0 ; Note that D=0 for rest of procedure. ; LD E,(HL) LD (SPT),DE INC HL LD E,(HL) LD (DRM),DE LD E,5 ; Offset to systrk field. (since D=0) ADD HL,DE LD E,(HL) LD (SYSTRK),DE ENDIF ; ONLY14 OR UNSPECI IF UNSPECI ; If CPM22 not present then just fall through, JP CALCSPACE ; Else we need to jump around CPM22. ENDIF ; ;----------------------------------------------------------------------- ; ; CP/M 2.2 routine ; ; Implementation Note: BC is used here only for loading offsets < 256. ; Thus after 1st 16 bit load, B=0 for rest of routine. ; IF ONLY22.OR.UNSPECI CPM22: LD E,(HL) ; On entry HL points to DPH. INC HL LD D,(HL) LD (RECTBL),DE ; Save the XLTO addr. LD BC,9 ; Offset to ptr to DPB (in DPH) (Now B=0) ADD HL,BC LD E,(HL) INC HL LD D,(HL) EX DE,HL ; Now HL = addr of DPB. LD E,(HL) INC HL LD D,(HL) LD (SPT),DE LD C,6 ; Offset to DRM (in DPB) (since B=0) ADD HL,BC LD E,(HL) INC HL LD D,(HL) LD (DRM),DE LD C,5 ; Offset to SYSTRK (in DPB) (since B=0) ADD HL,BC LD E,(HL) INC HL LD D,(HL) LD (SYSTRK),DE ; Now drop into CALCSPACE. ENDIF ; ONLY22 OR UNSPECI ; ;======================================================================= ; ; Determine if directory will fit in available memory. ; If not, determine how much of it will. ; CALCSPACE: ; First compute amount of free space in bytes. LD HL,(BDOS+1) ; Get addr of 1st byte of BDOS. LD DE,BUF ; Get addr of 1st byte of BUF. ; ; OR A ; Clear carry. ; SBC HL,DE ; ; Since we know that BUF starts on an even page boundary, E=0, so just go: ; LD A,H SUB D LD H,A ; Now HL = HL - DE, (since E=0). ; Note that E=0 is used in next paragraph. DEC HL ; This is for the extra 0E5H at the end of the ; Buf: needed for CLEAN to be bullet proof. ; Compute HL = HL/128 by HL = (HL * 2) / 256. SLA L ; New carry was msb of L. RL H ; Rotate left H (moving carry into lsb). ; The new carry was msb of H. LD L,H ; Effectively go HL = HL/256 (carry saved) LD H,E ; Zero H reg (since E=0 before) (carry saved) RL H ; Set H = carry. Now HL = HL/128. LD (NUMDIRRECS),HL ; Assume we need all space until proven wrong. ; Now determine how many 128-byte records the directory takes up. LD DE,(DRM) ; Get # 32-byte directory entries . . . INC DE ; Relative to 1. LD (NUMFNAMES),DE ; Save as a more convenient count for later. SRL D RR E ; Divide by 2. SRL D RR E ; Now DE = # 128-byte records needed. ; Note HL = # 128-byte records available. ; ; Now see if it all fits in available memory ; ; Note that carry flag = 0 from the above RR E ; since # direc entries is always a multiple of 4. ; Thus I don't need to go: ; ; OR A ; clear carry ; SBC HL,DE JR C,NOMEM ; If need > space then process exception. LD (NUMDIRRECS),DE ; Save needed recs as num to process. This JP BYESETUP ; Finally synchronizes NUMDIRRECS and NUMFNAMES. NOMEM: LD HL,(NUMDIRRECS) ; Recall available space (in records). ADD HL,HL ADD HL,HL ; Now HL = available space (in direc entries). LD (NUMFNAMES),HL ; Save new num dirc entries. This finally LD C,PRINT ; Synchronizes NUMDIRRECS and NUMFNAMES. LD DE,LACKMEMORY ; Warn user we'll do only as much as we can. CALL BDOS ; BDOS will return for me. BYESETUP: ; ;======================================================================= ; GETDIR: CALL RDDIR ; Read in the directory entries. ; ;======================================================================= ; ; CLEAN out the erased file entries to all E5's. ; ; While we're at it, move all empty entries to the bottom of the buffer. ; This way we can avoid adding them to the sort's work. For large underused ; directories (such as on my hard disk) this can speed up the sort ; substantially. ; ; The dummy entries before and after the buffer are generated to prevent this ; algorithm from leaving the buffer's boundaries. When we are scanning backward ; with the right pointer looking for a non-empty entry we must guard against ; the chance that all entries are empty. Similarly, when we scan forward with ; the left pointer looking for empty entries we must guard against the chance ; that all entries are non-empty. ; CLEAN: ; First we need to create a dummy non-empty record before BUF, and a ; Dummy empty record at end of buffer. LD A,0FFH LD (BUF-ENTRYLEN),A ; Mark a dummy entry as not deleted. IF DELZRO LD (BUF-ENTRYLEN+15),A ; And as having >0 number of records. ENDIF LD HL,(NUMFNAMES) ADD HL,HL ; Take advantage of ENTRYLEN = 32 = 2 ^ 5. ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ; Compute HL = NUMFNAMES * 32. LD DE,BUF ADD HL,DE ; Now HL = 1st entry after end of buffer. LD A,0E5H ; Needed at the entry point. ; Note that if .NOT.DELZRO then this register ; Will be preserved throughout the routine! LD (HL),A ; Mark as a dummy empty record. LD BC,-ENTRYLEN ADD HL,BC ; Now HL = last entry = BUF + 32*(NUMFNAMES-1). ; DE = 1st entry = BUF. JP CLNENTRY ; Jump to entry point within the big loop. ; ;----------------------------------------------------------------------- ; ; Swap left and right entries. ; DE = right = non-empty entry. ; HL = left = empty entry. ; CLSWAP: PUSH DE ; Save right pointer. EX DE,HL LD BC,ENTRYLEN LDIR ; Left <== Right. Now DE = left + 32. POP HL ; Recall right pointer. ; To finish the swap drop into FILLE5. ; ; ;----------------------------------------------------------------------- ; ; Here we clear out the entry at (HL). ; FILLE5: LD B,ENTRYLEN ; Number of bytes to clear IF DELZRO ; The cond. code for DELZRO won't preserve A. LD A,0E5H ; Load up char to replicate. ENDIF FILLOP: LD (HL),A INC HL DJNZ FILLOP ; ; Now we need to continually back up the right pointer as long as the entries ; are empty. I.e., we need to search for the 1st non-empty entry from the ; right. ; LD BC,-(ENTRYLEN*2) ; FILLE5 made HL = right + 32. ADD HL,BC ; Now HL = right - 32. CLNENTRY: CP (HL) ; Is the right pointer at an empty entry? JP Z,FILLE5 ; If so then clear it. ; ; This is all conditional code to implement DELZRO and SAVDASH. ; IF DELZRO PUSH HL ; Save right pointer. ENDIF IF DELZRO.AND.(.NOT.ONLY14) LD BC,14 ADD HL,BC LD A,(HL) ; Get s2 byte (extended rc). AND 0FH ; For CP/M 2.2, 0 for CP/M 1.4 INC HL OR (HL) ; Check record count field. ENDIF IF DELZRO.AND.ONLY14 LD BC,15 ADD HL,BC LD A,(HL) ; Check record count field. OR A ENDIF IF DELZRO POP HL ; Recall right pointer. (Flags unaffected) ENDIF IF DELZRO.AND.(.NOT.SAVDASH) JR Z,FILLE5 ; IF record count = 0 then delete it. ENDIF IF DELZRO.AND.SAVDASH JP NZ,NEXTLFT ; If record count <> 0 then don't delete. INC HL LD A,(HL) ; Get first character of filename. DEC HL ; Now HL is start of entry. CP '-' ; Don't delete files with '-' as 1st char. JR NZ,FILLE5 ENDIF ; ;----------------------------------------------------------------------- ; ; Now we have HL = right = non-empty record. So keep bumping the left pointer ; until we find an empty record to trade with. Note that DE = next left, so ; check the current record at DE first. ; NEXTLFT: EX DE,HL ; Now HL = left, and DE = right. IF .NOT.DELZRO ; If we don't need to delete 0 length files, LD BC,ENTRYLEN ; Then pull this constant out of the loop. ENDIF JP LFTENTRY ; Skip bumping the left pointer. LFTLOOP: IF DELZRO ; The conditional code for DELZRO will not LD BC,ENTRYLEN ; Preserve this register pair. ENDIF ADD HL,BC ; Bump left pointer by an entry. LFTENTRY: IF DELZRO ; The conditional code for DELZRO will not LD A,0E5H ; Preserve this register. ENDIF CP (HL) ; Is it marked for deletion? IF .NOT.DELZRO JP NZ,LFTLOOP ENDIF ; ; The following is all conditional code to handle DELZRO and SAVDASH. ; IF DELZRO JR Z,LFTFND ; If marked for deletion then we're done. PUSH HL ; Save left pointer. ENDIF IF DELZRO.AND.(.NOT.ONLY14) LD BC,14 ADD HL,BC LD A,(HL) ; Get s2 byte (extended rc). AND 0FH ; For CP/M 2.2, 0 for CP/M 1.4 INC HL OR (HL) ; Check record count field. ENDIF IF DELZRO.AND.ONLY14 LD BC,15 ADD HL,BC LD A,(HL) ; Check record count field. OR A ; Is A = 0 ? ENDIF IF DELZRO POP HL ; Recall left pointer. (Flags unaffected) JP NZ,LFTLOOP ; If record count <> 0 then it's not empty. ENDIF IF DELZRO.AND.SAVDASH INC HL LD A,(HL) ; Get first character of filename. DEC HL ; Now HL is start of entry. CP '-' ; Don't delete files with '-' as 1st char. JR Z,LFTLOOP ; If '-' is 1st char then it's not empty to us. ENDIF ; ;----------------------------------------------------------------------- ; ; Now we have HL = left = an empty entry, and DE = right = a non-empty record. ; Check if right address < left address. (Note that DE = HL is impossible.) ; LFTFND: LD A,E ; Do a CP DE,HL (as if it existed). SUB L LD A,D SBC A,H JP NC,CLSWAP ; If left < right then we're ok, do the swap. ; Ok, now we need to compute the minimum NUM2DO and return. ; DE = last non-empty entry, HL = DE + 32 = 1st empty entry. LD DE,-BUF ADD HL,DE ; Now HL = # bytes in buffer to sort. ; Now compute HL = HL / 32 quickly. ; Note that HL is a multiple of 32. OR A ; Clear carry. LD A,H ; This will allow me to use RRA several times. RRA RR L ; By 2. RRA RR L ; By 4. RRA RR L ; By 8. RRA RR L ; By 16. RRA RR L ; By 32. LD H,A LD (NUM2DO),HL ; Now we won't have to add the overhead of the ; Empty entries to the sort routine. BYECLEAN: ; This symbol is useful for debugging. ; ;======================================================================= ; ; SORT the directory with a Shell-Metzner sort. ; ; Brief Synopsis: ; ; A shell sort works by comparing entries that are far apart (by INXOFFSET) ; and successively shrinking the offset (by a factor of 2 at each iteration) ; until the sort degenerates into a simple version of an insert sort. There is ; a notion of a "left" and a "right" pointer at all times. In general, the ; current "left" pointer = LFTPTR, and the current "right" pointer = LFTPTR + ; PTROFFSET. The "left" entry is analagous to the higher entry in the sorted ; list. The index variables are used for counting and determining how many ; more entries need to be compared against at the current INXOFFSET value. ; SORT: XOR A LD (NOSWAP),A ; Zero the flag in case already sorted. LD C,PRINT LD DE,SORTING CALL BDOS LD HL,(NUM2DO) ; Get # direc entries to process. JP SRTENTRY ; Skip the next instruction and go. ; ;----------------------------------------------------------------------- ; ; Now divide index offset size by 2. ; DIVIDE: LD HL,(INXOFFSET) SRTENTRY: SRL H RR L ; Now HL = HL / 2 LD (INXOFFSET),HL LD A,L OR H JP Z,BYESORT ; If INXOFFSET=0 then we're done. EX DE,HL ; DE = INXOFFSET. LD HL,(NUM2DO) ; Get # direc entries to process. SCF ; Set carry flag. SBC HL,DE LD (MAXINX),HL ; Store (NUM2DO-1) - INXOFFSET. ; The -1 part is to make the index 0 relative. ; Thus our 0 relative indexes will all be ; Consistent. LD HL,0 LD (CURINX),HL LD (SAVINX),HL EX DE,HL ; Now HL = INXOFFSET. ADD HL,HL ; Take advantage of ENTRYLEN = 32 = 2 ^ 5. ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ; Now HL = INXOFFSET * 32. LD (PTROFFSET),HL LD HL,BUF ; Load initial LFTPTR. ; ;----------------------------------------------------------------------- ; ; On entry to NDONE we have HL = next left pointer. ; On entry to NDONE1 we also have DE = (PTROFFSET). ; NDONE: LD (SAVLFT),HL LD DE,(PTROFFSET) NDONE1: LD (LFTPTR),HL ; This is an alternate entry point for SWITCH. EX DE,HL ADD HL,DE ; Now HL = right, DE = left. LD B,ENTRYLEN CMPARE: LD A,(DE) AND 7FH LD C,A ; C = (DE) AND 7FH LD A,(HL) AND 7FH ; A = (HL) AND 7FH CP C ; Form (HL) - (DE) == (right) - (left). IF ASCENDING JP C,SWITCH ; If (right) < (left) then switch. JR Z,EQUAL ENDIF IF .NOT.ASCENDING JR Z,EQUAL EX AF,AF' ; Save carry flag. LD A,B ; Get loop counter. CP ENTRYLEN ; Are we looking at the 1st byte of entry? JR Z,CP1ST ; User area byte must be compared ascending. EX AF,AF' JP C,NOSWITCH ; If (right) < (left) then don't switch. JP SWITCH CP1ST: EX AF,AF' JP C,SWITCH ; If (right) < (left) then switch. ENDIF ; ;----------------------------------------------------------------------- ; ; Either drop into here or jump in from SWITCH or from above if descending sort. ; NOSWITCH: LD HL,(SAVINX) INC HL LD (SAVINX),HL LD (CURINX),HL EX DE,HL ; Now DE = CURINX LD HL,(MAXINX) LD A,L ; Now do a CP HL,DE (as if it existed). SUB E LD A,H SBC A,D JR C,DIVIDE ; IF CURINX > MAXINX then DIVIDE. LD HL,(SAVLFT) LD DE,ENTRYLEN ADD HL,DE ; Bump left by 32 bytes. JP NDONE ; ;----------------------------------------------------------------------- ; EQUAL: INC HL INC DE DJNZ CMPARE ; Keep checking each byte of entry. JP NOSWITCH ; If equal then don't switch. ; ;----------------------------------------------------------------------- ; ; Note that on entry B contains the number of chars we need to move. ; SWITCH: LD A,0FFH LD (NOSWAP),A ; Mark that we've switched something once. LD DE,(LFTPTR) ; Get the left pointer, LD HL,(PTROFFSET) ; And the offset from left to the right ptr. ADD HL,DE ; Now HL = right, DE = left. LD B,ENTRYLEN ; Length of entry to swap. SWLOOP: LD C,(HL) LD A,(DE) LD (HL),A ; (HL) <== (DE) LD A,C LD (DE),A ; (HL) ==> (DE) INC HL INC DE ; Bump both pointers. DJNZ SWLOOP LD HL,(CURINX) LD DE,(INXOFFSET) OR A ; Clear carry. SBC HL,DE JR C,NOSWITCH ; A bug fix for SAP 46, traces back to 43. ; If CURINX-INXOFFSET < 0 then NOSWITCH. LD (CURINX),HL ; Now CURINX = CURINX - INXOFFSET. LD HL,(LFTPTR) LD DE,(PTROFFSET) ; ; Since we just did a conditional jump on carry set we know that when we ; are here we have carry clear. ; ; OR A ; Clear carry. ; SBC HL,DE ; Now HL = left = LFTPTR - PTROFFSET. JP NDONE1 BYESORT: ; This is the sort's procedure exit location. ; ;======================================================================= ; ; PACK the directory entries. ; PACK: LD HL,0 ; I = 0 LD BC,BUF+9 ; This is a constant taken out of the loop. PACK1: PUSH HL ; Save index i. ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ; Compute HL = 32 * i ADD HL,BC ; Now HL = buf + 9 + 32 * i = addr of file type. LD A,(HL) ; Jump if filetype not 'x??'. SUB '0' JR C,PACK2 CP 10 JP NC,PACK2 EX AF,AF' ; Save extent number x in A'. LD A,'$' ; Make sure file type is '.x$$'. INC HL CP (HL) JP NZ,PACK2 ; If not '$' then next entry. INC HL CP (HL) JR NZ,PACK2 ; If not '$' then next entry. INC HL ; Bump to extent number field. EX AF,AF' ; Recall x. LD (HL),A ; Set extent number to x. DEC HL DEC HL DEC HL ; Point back to the x. LD (HL),'$' ; Make file type .$$$ once again. PACK2: POP HL ; Recall index i. INC HL LD DE,(NUM2DO) ; Skip the empty entries at end of buffer. LD A,E ; Now do a CP DE,HL (as if it existed). SUB L LD A,D SBC A,H JP NZ,PACK1 ; Loop until i = # file names to process. BYEPACK: ; This convenient symbol helps in debugging. ; ;======================================================================= ; ; Now write the directory back and finish up. The PUTDIR symbol is only used ; for debugging purposes - nothing actually references it. ; PUTDIR: CALL WRDIR ; Write the modified directory to disk. LD DE,FINISHED ; Drop into PRLAST to finish up. ; ;======================================================================= ; ; Print the message pointed to by DE and terminate. ; This routine is used to compress needless code repetition. ; PRLAST: LD C,PRINT CALL BDOS JP BOOT ; ;======================================================================= ; ; READ DIRECTORY entry point. ; RDDIR: LD C,PRINT LD DE,READING ; Tell user we're about to read the directory. CALL BDOS XOR A ; A=0 to indicate directory read. JP DODIR ; Enter main directory routines. ; ; WRITE DIRECTORY entry point. ; WRDIR: LD A,(NOSWAP) OR A ; Is noswap = 0? JP NZ,WRDIR1 ; If not zero then needed sorting, LD C,PRINT LD DE,PREVIOUS ; Else tell user no swaps were needed. CALL BDOS WRDIR1: LD C,PRINT LD DE,WRITING ; Tell user we're now writing the directory. CALL BDOS LD A,1 ; A <> 0 signals writes instead of reads. ; Fall into DODIR. ; ;======================================================================= ; ; The main read/write directory routine. ; DODIR: LD (RWFLAG),A ; Save read/write flag. LD BC,(SYSTRK) ; Get track num of directory. LD (TRACK),BC ; Save as current track. CALL SETTRK LD HL,0 LD (RECORD),HL ; Set current record to 0. LD HL,(NUMDIRRECS) LD (DIRCNT),HL ; Init loop counter. LD HL,BUF LD (DMAPTR),HL ; Start at buf for dma. DIRLOP: LD BC,(RECORD) INC BC ; Bump to next record. LD HL,(SPT) ; Get num records per track. LD A,L ; Now do a CP HL,BC (as if it existed). SUB C LD A,H SBC A,B JP NC,NOTROV ; If spt >= record then we're ok, ; Else we drop into track bumping. LD BC,(TRACK) ; Track overflow, bump to next. INC BC LD (TRACK),BC CALL SETTRK LD BC,1 ; Rewind record number to start of track. NOTROV: LD (RECORD),BC ; Save computed record number. DEC BC ; Make relative to 0 instead of to 1. LD DE,(RECTBL) CALL RECTRN ; Returns HL = physical record number. IF UNSPECI LD B,H LD C,L ; SETREC needs BC = record number. LD A,(VERFLG) OR A CALL NZ,SETREC ; If CP/M 2.2 then call SETREC. ENDIF IF ONLY22 LD B,H LD C,L ; SETREC needs BC = record number. CALL SETREC ENDIF LD BC,(DMAPTR) CALL SETDMA LD A,(RWFLAG) ; Are we reading the directory, or writing it? OR A JR NZ,DWRT ; Read the directory. CALL READ OR A ; Test flags on read. JP Z,MORE ; If 0 then ok, else . . . . LD DE,RDERR ; Oops, a read error. JP PRLAST ; Print message and die. ; Write the directory. DWRT: LD C,1 ; For CP/M 2.2 deblocking BIOS's. CALL WRITE OR A ; Test flags on write. JR NZ,BADWRT ; If A <> 0 then an error. ; The read or write succeeded. MORE: LD HL,(DMAPTR) LD DE,128 ADD HL,DE LD (DMAPTR),HL ; Bump dma address for next pass LD HL,(DIRCNT) DEC HL LD (DIRCNT),HL ; Count down entries. LD A,H OR L JP NZ,DIRLOP ; Loop till zero left, RET ; And then we're done. ; Come here if we get a write error. BADWRT: LD DE,WRTERR JP PRLAST ; Print message and die. ; ;======================================================================= ; ; Various Messages ; IDENTITY: DEFB CR,LF,'Sort and Pack Directory v' DEFB '50 8/7/86',CR,LF,EOS READING: DEFB LF,'---> Reading, ',EOS SORTING: DEFB 'Sorting',EOS PREVIOUS: DEFB ', (Previously Sorted)',EOS WRITING: DEFB ', Writing',EOS FINISHED: DEFB ', done' DEFB CR,LF,EOS RDERR: DEFB CR,LF,'++ Read Error - no change made ++',CR,LF,EOS WRTERR: DEFB CR,LF,'++ Write Error ' DEFB '- directory left in unknown condition ++',CR,LF,EOS IF UNSPECI.AND.(.NOT.CPMONLY) BADOPSYS: DEFB LF,'++ SAP not useable with MP/M or CP/M 3.0 ++',CR,LF,EOS ENDIF ; UNSPECI AND (NOT CPMONLY) LACKMEMORY: DEFB LF,'++ Directory too large - ' DEFB 'SAP will process as much as possible. ++',CR,LF,EOS BADDRIVE: DEFB LF,'++ Only drives A through ',MAXDRIVE,' are valid ++' DEFB CR,LF,EOS ; ;======================================================================= ; ; Stack and Buffer Area. ; DEFS 4+ENTRYLEN ; Minimum stack depth (of 2), plus a ; Dummy entry used for CLEAN. ; Note that BDOS will switch to its own ; Stack when called, so this is ; Enough. EVEN EQU (($+255)/256)*256 ; Start buffer on even page, which also ; Increases stack area greatly. ORG EVEN STACK EQU $-ENTRYLEN ; Note that stacks grow downward. BUF EQU $ ; ;======================================================================= ; ; Variable Area. ; ORG CMDBUF ; The CP/M command line buffer is convenient. ; This gives me 128 bytes of free storage! ; ; The BIOS vectors we use. ; SELDSK: DEFS 3 SETTRK: DEFS 3 SETREC: DEFS 3 SETDMA: DEFS 3 READ: DEFS 3 WRITE: DEFS 3 LSTS: DEFS 3 ; Only in CP/M 2.2 (not used here) RECTRN: DEFS 3 ; Only in CP/M 2.2 IF UNSPECI VERFLG EQU LSTS ; Squeeze him in where he fits! ENDIF NUMDIRRECS EQU LSTS+1 ; Squeeze him in where he fits! ; ; Disk parameter block vars. ; SPT: DEFS 2 ; Sectors per track DRM: DEFS 2 ; # direc entries - 1, before calcspace SYSTRK: DEFS 2 ; # reserved tracks = 1st direc track RECTBL: DEFS 2 ; Logical ==> physical, record translation table addr NUMFNAMES EQU DRM ; This is drm + 1 = # direc entries, after calcspace. ; ; Temp vars for reads/writes. ; RWFLAG: DEFS 1 DIRCNT: DEFS 2 DMAPTR: DEFS 2 RECORD: DEFS 2 TRACK: DEFS 2 ; ; Temp vars for sorting. ; NOSWAP: DEFS 1 ; Flag: 00h = no swaps, 0FFh = some swaps. NUM2DO: DEFS 2 ; Number of entries to sort. MAXINX: DEFS 2 ; Maximum left pointer value. SAVLFT: DEFS 2 ; Save for left pointer. LFTPTR: DEFS 2 ; Current left pointer. SAVINX: DEFS 2 ; Save for entry index. CURINX: DEFS 2 ; Current entry index. INXOFFSET: DEFS 2 ; Index offset (or comparison distance). PTROFFSET: DEFS 2 ; Pointer offset = entrylen * INXOFFSET. END vþSAP50.COM% €Œ\Pp$"ܳ@ P !ŒMÚ°×�˜ ÃàhbºÀŒ1 žÍ‹„egiM=Å&…g\ön˜€ÀšM5CѦ³tä8 €ÇÄ‘œV˲†ÃpD“;Kt�œÞ’Ë2Ãv«c´¤8ÍìL5UbŠDT˜8EL’3E 1óüe;À¢ªH¥ ©�ƒ#ç)Üà‚XmP UÖXÖ0ƒ,w„€m|Êd€\²¡ùÌ­œ,'pF~¿…¢Û­†ö袙eð +µm÷;½ëË�Ââ G´©é8Ò`Ê€ ÊÜøe‡}^Ï_·Úg*€ ñ’¦‡ à¡¢¯¡†"€AQlaãÖ@ƒéhe`Öÿºà€Bi @ÖÈ¡q ª�\±‹q DXCàËN ÃøžÜåÉ´XA@<CÁECpøU ¤˜øMU-2aŽF ‚‰†E€L;ïÅ¡TI„p4;� ”¨áT8²@…ºàòU¬‘"àaŽà0�Ç,B�ư`'ðiÀ�|aQTx@`@î’l!Èú;Ï„t€Fhj‚!؃¤†2¿€oãêa‰Àtü戯àÝf¿†5˜|†EÆ%©çh†S€iÃE*±E JE‘i€aR„>’#á0i€Ú%Ù·=™gZZÆ!Ú`ZÈœ™¤¨" ¦i d[fiV9n¦iæQTˆ‰j^&i„„¸Um‚(°3m!Ö¸Wkája Èd‚#V� P¦7ŽC @0�à @( #ÖƒHä2Œc¦`<„°jÀ^…áÀm–„€P[MÁ¤2Œ# Ò7 á`@eù޶3„šðP(gã°Ò7Ž£˜Ù¢l# Ê2;(@+ŽCHé±îà Þ7 º†Z…z¶±›£�å˜(Ü7„Ñš ã(@6ë<· Á\&ï¼îAÅŽ\hA­gú† ƒ(Í™kaê7 |xî7r;þµ½ïá6j<ö{ÔèC–‰¡r`Â9r½0¦ Šî4�ƒ`@8qcÊ9ŽyŸº6޼—½ë�þàÒ1u¡w|êï '�ÛO¼ŽÞØ@ „ ÑÅŽ£8Ð3gà€;ØY³¿€H¢7UvþSAP50.DOC% €”È%Øj0 E¢™<¤TIÄA@‚C%ˆey4@D$”ãr"‘†TˆapÑ€à^0‹Ç`h*n HJ†ƒIÎ9�N†“a¤èyŒ¦%èh2ˆ “ó]PÓL1� ç*QÐäa1šÎbÁκt¨T¦æSu€Òe M‡A„Äe¢˜î†ÊQ„ÜdTmÔ³)ÜåH¹`êFÜa—b±Uë˜úÁÌÖ.›�M†ÁÔÜu9™pUÑ–ãÁi†¼Ý–ªÖ«•ì†Çs¦aŒÕÓi„ètÒôƒDÜǰ¢èMú!œŠ5ˆ w}ÇäsÙÿPÞm8/F[öÚo;bÎñ§›@3Qªfã ¶å73¼;*ÈÊ­«ªPîŸ:¡îõ„ Ë 2)ªØÒ;8 #¶Ï£^3ÓP7„ Ü3ª)»~¥/*£Ìò°N*¢4ÑXÐæŽŒ°Ê< c(ࢶl2ù¼Šò3�#pÝ! î#ŒAhD›µÁ'ŽmÚ1/ÁjŠüE±Ãà7B�C¼ c¨à8-*ؼ<Ò+.² èäǹè7ÈÏlê°±Âh† Š�pR§ªœî3Ï."»6Åê >!pi'°4Z@Á”¢&ã ÞÅ�Ã|d;ÑÒÊ¢à@ã �ØËÚ2½ï‹Ï¬J,"á — g{Šc€ÊÒ(ž1ÆÒ’�9â˜ˆé° + 8zÇ�3Ô$òíZá¥î ÊIúÕ¶5ä5Ímœ:<ñÕjª³ž" ¡:̽ w k{ŠLƒÔÅǼ øÇn µm¶Wdd´?Þ2)O3Gk§@#’:�ŒµÀRõªNuÌ¢³±jˆÇ·|k©)�MZœ§iP@*â­\¢aò(�˜@!@Oƒ\¶*‹¾¾4~*ª¾8M’äÿŒ–¹ »“cÄË@åžÒ<ÜnÉÂnà’3z~«¹OFRÆ6°hȳV¶èÏe4­ˆÈĽ7¬âôÞÑiBkYW» žGy !P$Àt*D£¶€Ka{„RªŒ•©h¡É=âÎ^Œ²1'ÄÏš4àÒÙ°.pX¶§r$"@HI�,ó,àžÁ§8Êü,„P¦‹S€N å™´3šÓ°r°¸<œÔà qÂ;Gp"=Xbs£J)¸7™Õ¼ݼI�…@>B‚`8L%‡~…2Ì§Ž¡R7)ÿ3~ƒa¹nLFíű—y‘ †I&£T|> UK›óª�ƒ*'È%´@ÆL¬61ôÐEP@ªÔa¦T¡4Ç&´ ’U)A�û¬’§àËn%]µ ¼…#!ŒÁ:4Eˆ€YŽªQ%Ï"hêd²f‡¬·�â Ê4@.ó¯äñ jJL»XåƒcC1œ,ƒ!¡áf•É<݆pêk  ªÐ»£(úÿfÆ…€&h2ˆpÍ�çWRPÈe [�yü Çà·Ëd3:V˜ },f<†RÌiƒM 9³¨©Ñ3Ëg¬Â¢ Õ_¢i÷?^”ù 2GPPÝAÍ… …˜ÑÁd;&ä-ró€N d¥ç4l:uÎÒÂ[Ú$ò£ÄR ©ðgÐxš/v”ªS@äQ§¥¥f—–àéC* ¡ôF|Ñ:*œ(½_´h³Á’¦âæažÔ„õ§ IT'ã=c5T¡H•-V)u «”ÊZm\°d§N=N)É)â—¡ôÎËÓ\›%q/,fb‚P“•Ó¼€Ám2Ù0`Ù5mC¨4ì/°Ý[J•~«A¸Ùª¸…"}0uX¦Gc„Ù±ðÞ5Ów,iÐD¡0-( OË1€)PÚ!³ø‡‹r!dvæ$4ŠÆTRµ:³¸­«ô”ĬÁSA§‘îÙÒ�gÈ-¡uìf2@¬xH—�Âú†�åÖD±Á#ðÊ�nãEìfòÔ²èZ*b'½OˆFK‘r®eù�¨v^y`Šéh<�¹ãZó$Îü4) žë•‰ ƒ5:aá "À€œâÄk~6¼Ñ+õ¦“¦Îx¦œ±2*`¸8ä4Œ÷øÂ6axhhã °8££ìZþ�âYÄ6 #@2 ãhĈdÞA­³mˆCxÄÆ�ƒÈÜ2³â@Ól ÃHñ[ Ë’Z~‰”Œ#¥¹ ÃÞ9æ­~k9@§†¨a9ÙÄ€ i YpÐ0ŽWè�:ÜW]Ûwç7�êcï¾æC.§ƒ £`æ2„öƶ6�£.ljyHÊ4áC°Ê9cO%‡Ý~�#hà6 «º’€c¶Ï’€¼v°éü­²:Œá•¦døÛmòüÏ67bF� Ã8C//ÈcZˆ3Y�A™�wÿl8n# ÓŒo£¸Ò: x.�¸júØê6_¼pçl£‡&xc8щôáØ’€ƒ'1lüŠãiîÜ4ºWŠ_[`aä8-s>�ÝòÛlÌ61—¾è; Ï<;>†ˆƒb€o€<“0ÄÈž,w ñ´¿vÖÛ[{qnmÖ �’’Þ€3^KÑnw/ƒsh@�åv(°n~ Ͳ֨Ê3Wf €:¸‡Jû iFp­Ô‡`h]hI ÁÍkúaãkoí~†PÂø¢odKýƒ2€Í€-<5w ßÂÐ }ÏàòáËs l:1FÂÙh3c¯�ú0  ÈÈ1Ìq”8† Wàyï* -°î×Xsç%²ÅÙ#Øì¦~ ùÇ9å±#�4’ Õ‚4‰BµàXc�’/fb�;W“�Æ ¾@ùßH kϰ4²ÉI)� õ Ï<1±ö çä+TxŒ�Ì-u¬èbCÕzìj[Åçö^Û,`Ï}‚<ðp»A„Í ïîA¯ØìÇÜ@mžd™Æhz¿Ãl±ªuFw ƒ ÏŒOhÏ„÷èc`h&nùÍ55° £zNÕúJÉühžOѧ¼ðµ=Biielµ—³•)-'s}{7zñ—óû +ùqÍæÓÓϦôl7Ð̧tÓt ¸ÂçÅ Ì2nëºÃ’¼ÄX0C6†7ǨÜÀb ®>|:7Jµ¢ Cˆ±�° ÂÈ™“V /Ú-ƒ@pëfÜ[l…«ÇjL¢Ûqò,4­w<Õà �_«…øBUÅNjëÃtB¯ÇÚûàs¡‰ÄëbT!ËŸdH¾ÇèZF¡{rªíÚ:ˆl já bAÍ®5w‰;kk†[uÁðW Ì× ²˜ !4*‘�çfÃ$Z¨p@³Ö�®ª¶Áº["mˆ3¶ÍìÆµ7}9k˜ ¥ Àµ8‚Z mjl¹ö¹ Üí‚ÁP0àÔ™†G¤ Zý¥n¢èÅÅèÚç¬õ­u>2Û>Ðe°Ö¤3PåQ˜=†tA¤=6ÿƒ�u˜̬çóqXT½&4(XáØ)ŸŽÑÛK«snØëÃ�×°<ÑÐñì뉱¸†v n°ªØ‚7H'%P[…° � 5‚›XÛ.ÆO†7rƒ`^ —uâ Œã8 á”ZSa‘�®0k%^Ho ðx6ÞzÑ!×.±l¤à:ë\þ�…}¹àÈœøt 9 ? RÄi±�Ý„°i¢æ§…:©’…TˆàÛ |îÔ˜ƒ£žÛS§M^¶­‰fþíýH•שÒßV ¢©3yAžEWÿ­³ ¤ÏI@<Šöà›Ï§“¦Í·³‚áçŠ2 9™w}A\c™FçŸkZÛ®ÎY†wt† ¸¼-¸OZè7a¦Â¾&Þ§Œ:.Ð*BöÚ#N`LZ·í׃YF!™ñŽ6¼ßœ`6-·Z…i ÎòsL|wÑPík$�‘æ’qòö2l¦%”^TÀk�ä†aØÆ œÌÌy æðƵÜEKÄoÝü³9̨eäM¾ó†æe¤­c-*ÞSm àüq×ÃiÀÞcÀ%Ai[¬9¼=¬Àqô·RÏFèùóc*dRqò¥à주ÅH}”šja}àLóü±ÆÌ`3µÉ%œØö¬ŒŒ:Æ*s·2¶ÞËÇpÃXp �ƒ:›# ¶yeŸyKù«Ö…ñ}[utòÅÀeœ4lqîéI×ÙvÁáM‡Oö]DÆu"ÛÔÏÛÜg|ƒ{ˆz�uçì8Ü÷àŸ'\qSž/ÂgÃ�m™™¹éplæÊ5$�›8‘hy‡öÂ�ˆk�ºkÕ5?XÔõi¤þà 9,MФµë Í çæYãŽ:ÃîáݥŰPÆý› ²TŒöù)%�[*–�"{©(ƒ©¾‹qb7#ÄÒ“yË—è$Y�Ÿ£�9ã�‘p3#`:0¢9�ÛA4£å³“ܼŠ)œÂ½3ãΛYè³È2™™š}W)k ž�µ˜3µ©¥"©úZ'újiíœq罉©»qò'Ï<‹¥A83‘òB&–›yêA°ƒé"ÉAÐ7ÁáÇÁñúB*˜‘@â@4H#"…ª.¡»�“.²ù��&—úã(:6q÷A“¼°J¼'‚Hµª~¿¥.‚éŠê–�iéo‚Ap¼ÚÈ�‰x�ÁŽ ûíè‚�å±Í¢š ²2rŒÂˆ–±×¬qò˜êA(2„C¢$°�»Ñ"â{›Z𻹢‚þ¯ùÛž“9Åʾ¸Â$ )˜* �h&Á¸7AÑÀ°¬Y™‘yµI¾¶1Œ®kBs›8ûž¬Aæ*hE4T›õCU¾"ó°ì¤:; uB¢*Y«¬ƒEÀÜ–Ú¦{7¾2 Ä ¦.xÃ\J—-.ó.ÄôP®G)°ÅZt¬“!#r@Ë�,K¥Åà¢òë�! Œ‚"Òƒ’£­˜†‹Ã�‹.ÂHš ¢štª g06FŒƒÒƱ«½ã¬.tqÈ»ª�/¸ Õš¾›[OœÓêº:¤:æ¹Áî, »!˜4¹êŢ˿�)˜¨4�%¨C"Id70DôšFs$zž‘…3!³ò¿ÊK-Kþ‚h–É„ ø)+‰GÉÉ ê\¯9 CÇBOGQåÇdU-Üx™Dy¼‰Ì&á~³yû:sæGÚC�9¼€B„)È)mÈ9å6Ùµ2´†D¼‡Ip�¸€H+«ŠµÑS è"Šéºš ð&§ÊO,Z5£ÙÇœÑËÅRƒ4Ý‚¹hû‘«DÀ™‚YÊ5¨(A¹Ì@7 ˜%6lE*r ˜)�#9Òƒ�™‚2sû:ºŸØ´²´³ËIÌ(oHj¡Í–—ÛRª4°Éh‚ˆ8ÐH(€ùCvþSAP50.NOT% €”È%Ô\0 Æó¡”@�N¦s©Ìè, ãCÈàl H¤Q)$@w7�M†A°Òkˆ â‘”æp7›¥ÓA$äv æc0œæ 1›Í¦Ó)¸éI4›„B€Ðr.+”‰ÂèY" d7Î$Fƒ Ú aŒ3 tê¦t,×*µ`\C'“DMøæe6˜�†YtªY.2p°ûé°Ø =NFñnÜgºHŒÆœ-þyJ•Ënfˆ¸ƒLr¹] øÀÊo8ae&‰¬^ s:˜Í î|o9 †“™ŽDc0�-¦ó=LϯʙÎFiν?ªU†£ ÍÄ)fÓ†{ )4Ý*¤±‚S$0:¹©dŠS‘O�ÐïÐÒ¸Î�ÞÂ0NÈ„:®CDò¶c âÒ¹m+È7±iJü”ŽOZª ÎD-€š)6£KxÔ7#ò¤�­jäØ6M¢Ä7ðSÆ@C¸Ê0�nȨ4" ØØ<¸ƒJŒÊ)ã *oÜ‚Ñ1aK|Ê�¡CÄ£ C-Ȩt‚áË Ìô8pèˆCÃ�Û (°šD2�‚n9ŽcJvì!b¸Ñ"©lLj¹)[è4¼Ò"”³3)œ¡?6kÞɪ‰€Üœr–ŤC»ÖÞÎ3šq;OÊ©4 ¡ú ©3T'#(Ñ:X:Hª˜ì7�‹@È�Hr,Ð £ˆê嬀UVü këB*‰Â˜ "ˆiBÍ7QE=:T#tò,…Y/²•ìåjÎöºú›¤C€ÂÕ)4ºv⎗ÚŸˆÏ£˜‡©Ê‚’;ÁqDüÒG“µ1"8Ù-´;t7Œ38á@ðMz´*’;Â:COJ|±@*N*4ŽÃMà•C[‚áÐCª4”F‘³þˆ ±öš¡èÃj2Í2äÄ4Œ£WÌNÞÇSãÂ⌔ §®c�ò쉖 †�Éð¦2Ž®á¥›lÄ„>jeÈP暥Ý·6&AÛ"·CÛ ­«üëž6”Û6=arJHµNIVN8ªÔœŒ¯JP1��z�\<#ý‹®ªWBÒÕ$ 3 #WÉ )oVû\»oðt¢V©Zø«wwTNé‚òˆÊ�-£¼WÇ®ƒè:¦ðˆé'¢ ¼%ÞË•0ˆvêK>¤ŒCÎecŒ�_æ)CÏœ;"HÍn-« ‘p¯L0ŒI_9)О$¹@Sž ÂH¸ ”ÜUƒY‘ÍÕåÝõ8ß °*7»³ŠÙizh™7ny�ò)á”Η Èï]Ú¼l†©íF¸KTeG8dìdÉêPRŽÉñ8Ð’ Ò´idiû¾×ÚRUú'&AÈì„HfAj"åÈ0’(nÙ2g pö�cºôbX €Ìð¡àêhK¢*Ä…< @ãɺ‡s$`Ê7°Ã\2a笔œ ÖÙòúh(´á(´¶R£«>ˆ<—*¸³A |oƄΆæ HÒ¼M…L×ÃØjïâ|Q�mt1�SŽoBPåñ'£–½Z´&°f!$bnæÎ»rLå£2¸&¨ÛY‚”çdîE³