*
*    QuickFuncs.s  V0.22 by mak und MC3     passend zu ACDPlay.c V1.49
*
* Ersetzt einige C-Routinen von ACDPlay durch schnellere und kürzere
* Assemblervarianten. QuickFuncs muß als Object-File in ACDPlay eingebunden
* werden (Make), die Protos (Quickfuncs.h) dürfen natürlich auch nicht fehlen.
* Zum assemblieren muß die CATCOMP_NUMBERS Sektion des aktuellen Katalogs für
* Assembler als Datei "locale.i" vorhanden sein.
*
* ACHTUNG! Alle Funktionen hier (und auch C) gehen davon aus daß alle Register
* erhalten bleiben. Darum müssen alle Register die irgendwie verändert
* werden gesichert werden. Auch d0, wenn kein Wert zurückgegeben wird.
*
*  Autor   Version   Datum              Veränderungen
* ------- --------- --------    ------------------------------------------
*   mak     0.01    23.10.96    NewShowMsg() und  NewGetVec() dazu
*                   24.10.96    NewFlushMessages() dazu
*           0.02    26.10.96    Funktionen benutzen jetzt die lokalisierten
*                               Texte von ACDPlay
*           0.03    31.10.96    NewOpenLibs() erstellt
*           0.04     4.11.96    NewCloseLibs() neu
*           0.05     6.11.96    Funktionsnamen von New.. in ..A geändert
*                               OpenARexxPortA() erstellt
*                               QuickFuncs.h erstellt
*                    7.11.96    CloseARexxPortA() hinzu
*                   12.11.96    StrCmpA() geschrieben
*           0.06    14.11.96    HandleARexxMsgA() endlich fertiggestellt
*                   15.11.96    HandleARexxMsgA() optimiert und erweitert
*                               Play, Stop, Eject, JumpForward und JumpBackward eingeführt
*           0.07    17.11.96    FormatA() umgesetzt
*                   18.11.96    alles ausführlich kommentiert
*                               (falls dich die narrensichere Kommentierung nervt, sag das bitte)
*           0.08    20.11.96    HandleARexxMsgA() gibt jetzt BOOL zurück
*                               SHOW, HIDE, QUIT hinzugeführt und JUMPFORWARD in NEXTSONG geändert
*           0.09    22.11.96    FindARexxPort() fertiggestellt und OpenARexxPort() gibt
*                               jetzt nicht mehr PORTALREADYOPEN zurück (jetzt durch FindARexxPort())
*                   23.11.96    'ACDP'-Messagetyp eingeführt (private rexxlike Messages)
*           0.10    25.11.96    FakeRequestA() fertiggestellt
*           0.11    04.01.97    Mindestversion des zu öffnenden Katalogs ist CATALOG_VERSION
*
*   MC3     0.12    29.01.97    Workbench.library wird geöffnet
*
*   mak     0.13    02.02.97    ARexx 'SHOW' schließt jetzt AppIcon
*
*   MC3     0.14    14.02.97    ChangeButtonTextA() erstellt
*                   16.02.97    asl.library wird geöffnet (wenn vorhanden)
*                               Library-Locales dazu
*
*   mak     0.16    23.02.97    ExecuteARexxA() dazu
*                   01.03.97    OPEN, CLOSE und PAUSE hinzu
*                               CloseARexxPortA() und FlushMsgA() testen auf NULL
*           0.17    08.03.97    OpenLibsA() überprüft ob mathieeedoubbas.library vorhanden ist
*           0.18    14.03.97    Rexx-Kommandos extrahiert
*                               ExecuteARexxA() öffnet jetzt ggf. ein Window
*           0.19    23.03.97    Fehlermeldung für RexxSysLib-Fehler stimmt jetzt
*           0.20    06.04.97    ChangeButtonTextA auskommentiert
*                               CON-Fenster von ExecuteArexxA() ist jetzt vom Typ AUTO
*           0.21    27.04.97    HandleARexxMsgA() überarbeitet
*                               rexxcmds.s überarbeitet und ca. 40 ARexx-Kommandos dazu
*           0.22    11.07.97    FindPort´s() mit Forbid abgesichert
*                   15.07.97    MyGT_SetGadgetAttrs() erstellt

 include 'exec/ports.i'
 include 'dos/dos.i'
 include 'dos/dosextens.i'
 include 'intuition/intuition.i'
 include 'libraries/gadtools.i'
 include 'libraries/locale.i'
 include 'rexx/rxslib.i'
 include 'rexx/storage.i'
 include 'lvo/dos.i'
 include 'lvo/exec.i'
 include 'lvo/gadtools.i'
 include 'lvo/graphics.i'
 include 'lvo/intuition.i'
 include 'lvo/locale.i'

 include "locale.i"

 xref _GetString

 xref __wbflag
 xref _CDPlayBase
 xref _CxBase
 xref _DOSBase
 xref _GadToolsBase
 xref _GfxBase
 xref _WorkbenchBase
 xref _IconBase
 xref _AslBase
 xref _IntuitionBase
 xref _LocaleBase
 xref _RexxSysBase
 xref _ScreenNotifyBase
 xref _li               ; LocaleInfo Struktur die _GetString übergeben wird
 xref _ls               ; Array mit Zeigern auf lokalisierte Texte

FALSE           equ 0
TRUE            equ 1
NULL            equ 0

 include "CD-ROM.s"

*************************  FakeRequestA   *****************************
*
*  void FakeRequestA(struct Window *win, APTR visualinfo, LONG xoffset, LONG yoffset, LONG height);
*
* Füllt das Fenster 'win' mit einem Muster aus und malt in der Mitte ein
* von einer umgedrehten BevelBox umrahmtes Rechteck mit Farbe 0, so daß es
* wie ein IntuitionRequester aussieht. Die Offsetparameter beziehen sich
* dabei auf den inneren Rand des Fensters.
*
  CNOP 0,2
  xdef _FakeRequestA
_FakeRequestA:  movem.l d0-d5/a0-a6,-(sp)
                movem.l 52+4(a7),a3-a4          ; a3=win a4=visualinfo
                lea     wd_RPort(a3),a2
                movea.l (a2)+,a5                ; a5=RastPort

.PrepareRPort   lea     Pattern(pc),a0          ; Daten für Ditherpattern in
                move.l  a0,rp_AreaPtrn(a5)      ;  RastPort eintragen
                move.b  #1,rp_AreaPtSz(a5)
                movea.l _GfxBase,a6             ; GfxBase laden
                movea.l a5,a1
                moveq   #2,d0                   ; Vordergrundfarbe auf weiß
                jsr     _LVOSetAPen(a6)         ;  setzen

.DitherWindow   moveq   #0,d0                   ; d0 und d1 müssen vom Typ
                moveq   #0,d1                   ;  LONG sein
                move.b  (a2)+,d0                ; =wd_BorderLeft
                move.b  (a2)+,d1                ; =wd_BorderTop
                move.b  (a2)+,d4                ; =wd_BorderRight
                move.b  (a2),d5                 ; =wd_BorderBottom
                ext.w   d4                      ; d4 und d5 auf WORD
                ext.w   d5                      ;  erweitern
                movem.w wd_Width(a3),d2-d3      ; Windowbreite und Höhe laden
                sub.w   d4,d2                   ; x2 = Width-BorderRight-1
                sub.w   d5,d3                   ; y2 = Height-BorderBottom-1
                subq.w  #1,d2
                subq.w  #1,d3
                ext.l   d2                      ; RectFill erwartet Parameter
                ext.l   d3                      ;  vom Typ LONG, darum erweitern
                movem.l d0-d1,-(a7)             ; wird noch gebraucht
                movea.l a5,a1                   ; RastPort ist Parameter
                jsr     _LVORectFill(a6)        ; Window Füllen

                clr.l   rp_AreaPtrn(a5)         ; Dithermuster wieder löschen
                clr.b   rp_AreaPtSz(a5)

                movea.l a5,a1                   ; RastPort ist Parameter
                moveq   #0,d0                   ; sollte aus DrawInfo gelesen werden...
                jsr     _LVOSetAPen(a6)         ; Vordergrundfarbe auf 0 setzen

.ClearArea      movem.l (a7)+,d0-d1             ; gesichertes left, top laden
                lea     52+12(a7),a0            ; Adresse der Parameter ab 'xoffset' laden
                add.l   (a0),d0                 ; left=left+xoffset
                sub.l   (a0)+,d2                ; right=right-xoffset
                add.l   (a0)+,d1                ; top=top+yoffset
                move.l  d1,d3
                add.l   (a0),d3                 ; bottom=top+height-1
                movem.l d0-d1/d3,-(a7)          ; wird nochmal gebraucht
                subq.l  #1,d3
                movea.l a5,a1                   ; RastPort
                jsr     _LVORectFill(a6)        ; Fläche löschen

.DrawBevelBox   movea.l _GadToolsBase,a6        ; für DrawBevelBoxA
                movea.l a5,a0                   ; RastPort ist Parameter
                movem.l (a7)+,d0-d1/d3
                sub.l   d0,d2                   ; Absolute Koordinaten in
                addq.l  #1,d2                   ;  Breite / Höhe wandeln
                sub.l   d1,d3
                lea     BevelBoxTags(pc),a1     ; Taglist laden
                move.l  a4,4(a1)                ; VisualInfo in TagList eintragen
                jsr     _LVODrawBevelBoxA(a6)   ; inverse BevelBox zeichnen

.EXIT           movem.l (sp)+,d0-d5/a0-a6
                rts

Pattern:        dc.w    %1010101010101010
                dc.w    %0101010101010101

BevelBoxTags:   dc.l    GT_VisualInfo, NULL
                dc.l    GTBB_Recessed, TRUE
                dc.l    TAG_END


**************************  ListLengthA  *****************************
*
*  ULONG ListLengthA(register __a0 struct List *list);
*
  CNOP 0,2
  xdef _ListLengthA
_ListLengthA:   movem.l d1/a0,-(sp)                 ; Register sichern
                moveq   #-2,d0                      ; Zähler auf -2 setzen

.Loop           addq.l  #1,d0                       ; Nodezähler erhöhen
                move.l  (a0),a0                     ; lh_Head/ln_Succ in a0
                move.l  a0,d1                       ; für Condition-Flags
                bne.s   .Loop                       ; bis Liste zu Ende ist

.EXIT           movem.l (sp)+,d1/a0                 ; Register restaurieren
                rts


**************************  ChangeButtonTextA  ***********************
*
*  struct Gadget *ChangeButtonTextA(register __a0 struct Application *app, register __a1 struct Window *win, register __a2 struct Gadget *prevgad, register __a3 char *newtext);
*
*  Vorsicht: Es wird auf die Application-Struktur zugegriffen!
*  Diese darf sich vor app->font nicht mehr ändern!
*
;  CNOP 0,2
;  xdef _ChangeButtonTextA
;_ChangeButtonTextA:
;                movem.l d1/a0-a6,-(sp)              ; Register sichern
;                moveq   #0,d0                       ; für die Rückgabe
;
;                move.l  (a2),a4                     ; gad = prevgad->NextGadget
;                move.l  (a4),a5                     ; nextgad = gad->NextGadget
;
;                move.l  a2,d1                       ; prüft prevgad
;                beq.s   .EXIT
;                move.l  a4,d1                       ; prüft gad
;                beq.s   .EXIT
;
;                lea     NewGadStruct(pc),a6         ; newgad laden
;
;                move.l  4(a4),(a6)                  ; LeftEdge, TopEdge kopieren
;                move.l  8(a4),4(a6)                 ; Width, Height kopieren
;                move.l  a3,8(a6)                    ; GadgetText kopieren
;                move.l  16(a0),12(a6)               ; app->font nach TextAttr ***
;                move.w  38(a4),16(a6)               ; GadgetID kopieren
;                move.l  12(a0),22(a6)               ; app->visualinfo kopieren ***
;
;                move.l  d0,(a4)                     ; NextGadget auf NULL
;                move.l  a4,a0                       ; gad ist Parameter
;                move.l  a1,a3                       ; win verlagern, damit
;                move.l  a6,a1                       ; newgad nach a1 kann
;                movea.l _GadToolsBase,a6            ; für Funktionsaufruf
;
;                movem.l a0-a6,-(sp)                 ; alles retten
;                jsr     _LVOFreeGadgets(a6)         ; FreeGadgets()
;                movem.l (sp)+,a0-a6                 ; alles restaurieren
;
;                moveq   #BUTTON_KIND,d0             ; Argument 1
;                move.l  a2,a0                       ; Argument 2
;                                                    ; Argument 3 ist bereits in a1
;                suba.l  a2,a2                       ; auf NULL setzen (Argument 4)
;
;                movem.l a0-a5,-(sp)                 ; alles retten
;                jsr     _LVOCreateGadgetA(a6)       ; CreateGadgetsA()
;                movem.l (sp)+,a0-a5                 ; alles restaurieren
;
;                tst.l   d0                          ; Rückgabewert testen
;                beq.s   .EXIT                       ; ggf. weg
;
;                move.l  d0,a0                       ; gad sichern & Argument 1
;                move.l  a5,(a0)                     ; gad->NextGadget = nextgad
;                move.l  a3,a1                       ; win ist Argument 2
;                                                    ; a2 ist noch NULL (Argument 3)
;                moveq   #1,d0                       ; Argument 4
;                movea.l _IntuitionBase,a6           ; für Funktionsaufruf
;
;                movem.l a1,-(sp)
;                jsr     _LVORefreshGList(a6)        ; RefreshGList()
;                movem.l (sp)+,a1
;
;                move.l  a1,d0                       ; Rückgabewert (gad)
;
;.EXIT           movem.l (sp)+,d1/a0-a6              ; Register restaurieren
;                rts
;
;  CNOP 0,2
;NewGadStruct:   dc.w 0              ; ng_LeftEdge
;                dc.w 0              ; ng_TopEdge
;                dc.w 0              ; ng_Width
;                dc.w 0              ; ng_Height
;                dc.l 0              ; ng_GadgetText
;                dc.l 0              ; ng_TextAttr
;                dc.w 0              ; ng_GadgetID
;                dc.l 0              ; ng_Flags
;                dc.l 0              ; ng_VisualInfo
;                dc.l 0              ; ng_UserData


**************************  FormatA   ********************************
*
*  void FormatA(register __a0 char *buffer, register __a1 char *formatstr, ...);
*
* Kopiert den formatierten Text formatstr nach buffer und benutzt dabei
* die Werte bzw. Adressen der Argumente vom Stack.
*
  CNOP 0,2
  xdef _FormatA
_FormatA:       movem.l d0-d1/a0-a3/a6,-(sp)        ; Register sichern
                movea.l 4.w,a6                      ; SysBase laden
                                                    ; Argumente für RawDoFmt()
                movea.l a0,a3                       ; PutChData -> a3
                movea.l a1,a0                       ; FormatString -> a0
                movea.l a7,a1                       ; DataStream -> a1
                lea     32(a1),a1                   ; Offset da Register auf dem Stack sind
                lea     PutChProc(pc),a2            ; Copy-Routine
                jsr     _LVORawDoFmt(a6)

.EXIT           movem.l (sp)+,d0-d1/a0-a3/a6        ; Register restaurieren
                rts

PutChProc:      move.b  d0,(a3)+                    ; wird von RawDoFMt aufgerufen
                rts                                 ; soll ich einen Overflowtest einbauen?


*************************  ShowMsgA   ********************************
*
*  void ShowMsgA(char *ReqTitle, char *ReqBody, ...);
*
* Zeigt eine Nachricht Abhängig von __wbflag auf der Workbench als Requester
* oder im CLI an.
*
  CNOP 0,2
  xdef _ShowMsgA
_ShowMsgA:      movem.l d0-d2/a0-a3/a6,-(sp)

                tst.w   __wbflag                    ; von WB gestartet?
                beq     .CLIMsg                     ; nein: in CLI schreiben

.WBMsg          lea     MsgEasyStruct(pc),a1        ; a1=*EasyStruct
                move.l  36(a7), es_Title(a1)        ; Titel eintragen
                move.l  40(a7), es_TextFormat(a1)   ; Text eintragen
                lea     _ls,a0
                move.l  4*MSG_OK_REQ(a0), es_GadgetFormat(a1) ; Gadget eintragen
                suba.l  a0,a0                       ; *Window=NULL
                suba.l  a2,a2                       ; a2=NULL (IDCMP)
                movea.l a7,a3                       ; * auf restliche
                adda.l  #44,a3                      ;  Argumente nach a3
                move.l  _IntuitionBase, a6          ; IBase laden
                jsr     _LVOEasyRequestArgs(a6)     ; Requester darstellen
                bra     .EXIT                       ; das wars

.CLIMsg         suba.l  #88,a7                      ; 88 bytes auf´m Stack
                move.l  a7,d1                       ;  freimachen damit 87
                moveq   #87,d2                      ;  Zeichen da reinpassen
                move.l  _DOSBase, a6                ; DOSBase laden
                jsr     _LVOGetProgramName(a6)      ; ProgNamen in Puffer schreiben
                tst.w   d0                          ; geklappt ?
                beq     .WriteMsg                   ; nö: Msg ausgeben
                 move.l  a7,d1
                 jsr     _LVOPutStr(a6)             ; Prognamen ausgeben
                 lea     Colon(pc),a1
                 move.l  a1,d1
                 jsr     _LVOPutStr(a6)             ; ": " ausgeben

.WriteMsg       adda.l  #88,a7                      ; Stack zurücksetzen
                move.l  40(a7),d1                   ; ReqBody als Arg für VPrintf
                move.l  a7,d2
                addi.b  #44,d2                      ; Rest als Argument
                jsr     _LVOVPrintf(a6)             ; Msg ausgeben
                lea     LineFeed(pc),a1             ; erst mit dem LF
                move.l  a1,d1                       ;  erscheint alles im
                jsr     _LVOPutStr(a6)              ;  CON-CLI-Window

.EXIT           movem.l (sp)+, d0-d2/a0-a3/a6       ; Register wiederherstellen
                rts

MsgEasyStruct:  dc.l 20             ; es_StructSize
                dc.l 0              ; es_Flags
                dc.l 0              ; es_Title
                dc.l 0              ; es_TextFormat
                dc.l 0              ; es_GadgetFormat

Colon:          dc.b ": ",0
LineFeed:       dc.b 10,0


*************************  GetVecA  *********************************
*
*  APTR GetVecA(register __d0 ULONG bytesize, register __d1 ULONG requirements);
*
* Versucht 'bytesize' Bytes RAM zu erhalten und Zeigt im Fehlerfall eine
* Fehlermeldung an.
*
  CNOP 0,2
  xdef _GetVecA
_GetVecA:       movem.l d1-d2/a0-a1/a6,-(sp)    ; alte Register speichern
                move.l  d0,d2                   ; bytesize für Fehlerfall sichern
                move.l  4.w,a6                  ; SysBase laden
                jsr     _LVOAllocVec(a6)        ; AllocVec bytesize requiements
                tst.l   d0                      ; erfolgreich?
                bne     .EXIT                   ; mem <> 0 -->.EXIT
                move.l  d2,-(a7)                ; Argumente für NewShowMsg
                lea     _ls,a0                  ;  auf den Stack schieben
                move.l  4*MSG_NO_MEMORY(a0),-(a7)
                move.l  4*MSG_ERROR_TITLE(a0),-(a7)
                bsr     _ShowMsgA               ; Error anzeigen
                lea     12(a7),a7               ; Stack wieder runtersetzen

.EXIT           movem.l (sp)+,d1-d2/a0-a1/a6    ; alte Register restaurieren
                rts


*************************  OpenLibsA  ********************************
*
*  BOOL OpenLibsA(void);
*
* Versucht alle benötigten (siehe Tabelle) Libraries zu öffnen und zeigt
* im Fehlerfall, falls die Library unbedingt benötigt wird, einen Requester an.
* Außerdem wird die LocaleInfo-Struktur initialisiert und die Pointer auf
* die lokalisierten Texte werden in das ls-Array kopiert.
*
  CNOP 0,2
  xdef _OpenLibsA
_OpenLibsA:     movem.l d1-d2/d7/a0-a3/a6,-(sp)
                moveq   #FALSE,d7               ; success = FALSE
                move.l  4.w,a6                  ; SysBase laden

.OpenIntuition  lea     IntuitionLibName(pc),a1
                moveq   #37,d0
                jsr     _LVOOpenLibrary(a6)     ; IntuitionLib V37 öffnen
                move.l  d0,_IntuitionBase
                beq     .EXIT                   ; if (!IntuitionBase) return success;
.OpenLocale     lea     LocaleLibName(pc),a1
                moveq   #38,d0
                jsr     _LVOOpenLibrary(a6)     ; LocaleLib V38 öffnen
                lea     _li,a3                  ; a3 = LocaleInfo
                move.l  d0,_LocaleBase
                beq     .CopyStrings            ; if (LocaleBase)
                                                ; {
.OpenCatalog    move.l  d0,(a3)                 ;   li.li_LocaleBase = LocaleBase
                suba.l  a0,a0
                lea     CatName(pc),a1
                lea     CatalogVerTags(pc),a2
                move.l  d0,a6                   ;   LocaleBase laden
                jsr     _LVOOpenCatalogA(a6)    ;   li.li_Catalog=OpenCatalog()
                move.l  d0,4(a3)                ; }

.CopyStrings    lea     _ls,a2                  ; a2 = ls[]
                lea     _GetString,a6           ; ein bißchen Speed...
                moveq   #0,d2
                                                ; for (i = 0;i < ANZ_MSG;)
.Next           move.l  a3,a0                   ; {
                move.l  d2,d0
                jsr     (a6)                    ;   GetString(li, id)
                move.l  d0,(a2)+                ;   ls[i]=GetString(li, i)
                addq.w  #1,d2                   ;   i++
                cmp.w   #ANZ_MSG,d2             ; }
                blt    .Next                    ;

.TstMathDoub    move.l  4.w,a6                  ; SysBase wieder laden
                lea     MathDoubBasName(pc),a1
                moveq   #34,d0
                jsr     _LVOOpenLibrary(a6)     ; mathieeedoubbas.library öffnen
                tst.l   d0                      ; geklappt?
                bne     .CloseMathDoub          ; ja: wieder schließen...
                 lea     _ls,a0
                 move.l  4*MSG_NO_MATHIEEEDOUBBASLIB(a0),-(a7)
                 move.l  4*MSG_ERROR_TITLE(a0),-(a7); ErrorTitle auf den Stack
                 bsr     _ShowMsgA                  ; Fehlertext anzeigen
                 addq.l  #8,a7                      ; Stack aufräumen
                 bra     .EXIT
.CloseMathDoub  move.l  d0,a1
                jsr     _LVOCloseLibrary(a6)

.OpenRestLibs   lea     Libs(pc),a2             ; Librarytabelle laden

.Loop           tst.l   (a2)                    ; while (Libs.LibName)
                beq     .AllOpened              ; {
                move.l  (a2)+,a1                ;   Libs.LibName
                move.l  (a2)+,d0                ;   Libs.Version
                jsr     _LVOOpenLibrary(a6)     ;   OpenLibrary()
                move.l  (a2)+,a0
                move.l  (a2)+,d1                ;   ...[Libs++]...
                move.l  d0,(a0)                 ;
                bne     .Loop                   ;   if (!(Libs.Base = OpenLibrary())
                                                ;   {
.Error          tst.l   d1                      ;     ErrorText vorgesehen?
                beq     .Loop                   ;     nein -> kein Fehler, weitermachen
                movea.l d1,a0                   ;     Adresse des Errortextes auf den Stack
                move.l  (a0),-(a7)
                move.l  MSG_ERROR_TITLE*4+_ls,-(a7);  ErrorTitle auf den Stack
                bsr     _ShowMsgA               ;     Fehlertext anzeigen
                addq.l  #8,a7                   ;     Stack aufräumen
                bra     .EXIT                   ;     return success
                                                ; } }
.AllOpened      moveq   #TRUE,d7                ; success = TRUE

.EXIT           move.l  d7,d0
                movem.l (sp)+,d1-d2/d7/a0-a3/a6
                rts

CatalogVerTags:     dc.l    OC_Version, CATALOG_VERSION
                    dc.l    TAG_END

*************************  CloseLibsA  *******************************
*
*  void CloseLibsA(void);
*
* Schließt, ausgehend von der Libstabellealle, alle geöffneten Libraries
* und den geöffnetet Katalog.
*
  CNOP 0,2
  xdef _CloseLibsA
_CloseLibsA:    movem.l d0-d7/a0-a6,-(sp)
                move.l  4.w,a6                  ; SysBase laden
                lea     Libs(pc),a2             ; Librarytabelle laden

.Loop           tst.l   (a2)                    ; while (Libs.LibName)
                beq     .CloseLocale            ; {
                move.l  8(a2),a1                ;   Libs.LibName
                tst.l   (a1)                    ;   Library geöffnet?
                beq     .IsClosed
                move.l  (a1),a1
                jsr     _LVOCloseLibrary(a6)    ;   Library schließen
.IsClosed       add.l   #16,a2
                bra     .Loop                   ; }

.CloseLocale    lea     _LocaleBase,a0
                tst.l   (a0)                    ; LocaleLib geöffnet ?
                beq     .CloseIntui
                movea.l (a0),a6                 ; LocaleBase laden
                move.l  _li+4,a0                ; a0 = li.li_Catalog
                jsr     _LVOCloseCatalog(a6)
                move.l  a6,a1                   ; LocaleBase wird geschlossen
                move.l  4.w,a6                  ; SysBase laden
                jsr     _LVOCloseLibrary(a6)

.CloseIntui     lea     _IntuitionBase,a0       ; Prüfen mit (An)
                tst.l   (a0)                    ;  ist schneller und
                beq     .EXIT                   ;  kürzer als 2 mal die
                move.l  (a0),a1                 ;  absolute Adresse zu benutzen
                jsr     _LVOCloseLibrary(a6)

.EXIT           movem.l (sp)+,d0-d7/a0-a6       ; Register & Stack wiederherstellen
                rts

Libs:       *LibName,     Version,  *LibBase,       **ErrorText im Fehlerfall
    dc.l    CxLibName,       37, _CxBase,           MSG_NO_CXLIB*4+_ls
    dc.l    GadToolsLibName, 37, _GadToolsBase,     MSG_NO_GADTOOLSLIB*4+_ls
    dc.l    GfxLibName,      37, _GfxBase,          MSG_NO_GFXLIB*4+_ls
    dc.l    WorkbenchLibName, 37, _WorkbenchBase,   MSG_NO_WORKBENCHLIB*4+_ls
    dc.l    IconLibName,     37, _IconBase,         MSG_NO_ICONLIB*4+_ls
    dc.l    AslLibName,       0, _AslBase,          0
    dc.l    RexxSysName,     36, _RexxSysBase,      MSG_NO_REXXSYSLIB*4+_ls
    dc.l    SNLibName,        0, _ScreenNotifyBase, 0 ; ( 0 bedeutet daß Library nicht unbedingt geöffnet werden muß)
    dc.l    0

CatName:            dc.b "ACDPlay.catalog",0
IntuitionLibName:   dc.b 'intuition.library',0
MathDoubBasName:    dc.b 'mathieeedoubbas.library',0
LocaleLibName:      dc.b 'locale.library',0
CxLibName:          dc.b 'commodities.library',0
GadToolsLibName:    dc.b 'gadtools.library',0
GfxLibName:         dc.b 'graphics.library',0
WorkbenchLibName:   dc.b 'workbench.library',0
IconLibName:        dc.b 'icon.library',0
AslLibName:         dc.b 'asl.library',0
RexxSysName:        dc.b 'rexxsyslib.library',0
SNLibName:          dc.b 'screennotify.library',0


*************************  FindARexxPortA  ***************************
*
*  BOOL FindARexxPortA(void);
*
* Prüft ob schon ein öffentlicher Port 'ACDPLAY' vorhanden ist, sendet
* gegebenenfalls das ARexxkommando 'SHOW' zu diesem Port und gibt TRUE
* zurück, falls der Port nicht ausfindig gemacht werden konnte FALSE.
*
  CNOP 0,2
  xdef _FindARexxPortA
_FindARexxPortA: movem.l d1-d3/d6-d7/a0-a2/a6,-(sp)
                moveq   #FALSE,d7                   ; Vorgabe: kein Port gefunden
                move.l  4.w,a6                      ; SysBase laden

                lea     _ARexxPortName(pc),a2       ; a2=ACDPlayPortName
                jsr     _LVOForbid(a6)              ; damit PatchWork nicht meckert
                movea.l a2,a1
                jsr     _LVOFindPort(a6)            ; Port 'ACDPLAY' suchen
                move.l  d0,d2
                jsr     _LVOPermit(a6)
                tst.l   d2                          ; gefunden?
                beq     .EXIT                       ; nein: Ende

                moveq   #TRUE,d7                    ; Port gefunden

.OpenRexxLib    lea     RexxSysName(pc),a1          ; rexxsyslib.library
                moveq   #36,d0                      ; Version 36
                jsr     _LVOOpenLibrary(a6)         ; wird für RexxMsg benötigt
                move.l  d0,d6                       ; RexxSysBase->d6
                beq     .EXIT

.MakeReplyPort  jsr     _LVOCreateMsgPort(a6)       ; ReplyPort erstellen
                move.l  d0,d3                       ; d3=ReplyMsgPort
                beq     .CloseRexxLib               ; nicht genug Speicher...

.PrepareRexxMsg movea.l d6,a6                       ; RexxSysBase laden
                movea.l d0,a0                       ; Replymsgport
                suba.l  a1,a1                       ; Standartextension 'REXX'
                move.l  a2,d0                       ; Portname
                jsr     _LVOCreateRexxMsg(a6)
                move.l  d0,d2                       ; d2 = a0 = RexxMsg
                beq     .DeleteMsgPort              ; nicht geklappt: Ende
                lea     name_SHOW(pc),a1
                move.l  a1,ARG0(a0)                 ; SHOW-Befehl senden
                lea     PrivateMsg(pc),a1           ; damit ACDPlay die Msg
                move.l  a1,LN_NAME(a0)              ;  identifizieren kann
                move.l  #RXCOMM|RXFB_NOIO,rm_Action(a0) ; RexxMsg ist ein RX_Kommando/keine IO-Umleitung
                moveq   #1,d0                       ; nur ein Argument
                moveq   #0,d1                       ; alle Argumente sind STRPTRs
                jsr     _LVOFillRexxMsg(a6)         ; Argumente in ArgStrs konvertieren
                tst.b   d0                          ; erfolgreich?
                beq     .DeleteRexxMsg              ; nein: Ende

.SendMsg        movea.l 4.w,a6                      ; SysBase laden
                jsr     _LVOForbid(a6)              ; Taskswitching abschalten
                movea.l a2,a1                       ;  damit der Port nicht
                jsr     _LVOFindPort(a6)            ;  plötzlich verschwindet
                tst.l   d0
                bne     .SendIt                     ; Port überhaupt noch da?
                 jsr    _LVOPermit(a6)              ; Multitasking wieder an
                 bra    .ClearRexxMsg               ;  und Ende
.SendIt         movea.l d0,a0                       ; gefundener Port
                movea.l d2,a1                       ; RexxMsg
                jsr     _LVOPutMsg(a6)              ; ab geht die Post :-)
                jsr     _LVOPermit(a6)              ; Kermit?
                movea.l d3,a0                       ; eigener ReplyPort
                jsr     _LVOWaitPort(a6)            ; auf Reply warten
                movea.l d3,a0                       ; ReplyPort
                jsr     _LVOGetMsg(a6)              ; es kommt mit Sicherheit nur
                                                    ;  eine Msg an, darum reicht GetMsg

.ClearRexxMsg   movea.l d6,a6                       ; RexxSysBase wieder laden
                movea.l d2,a0                       ; RexxMsg
                moveq   #1,d0                       ; einen ArgString freigeben
                jsr     _LVOClearRexxMsg(a6)

.DeleteRexxMsg  movea.l d2,a0                       ; RexxMsg
                jsr     _LVODeleteRexxMsg(a6)       ;  freigeben

.DeleteMsgPort  movea.l 4.w,a6                      ; und SysBase wieder laden
                movea.l d3,a0                       ; eigener ReplyPort
                jsr     _LVODeleteMsgPort(a6)       ;  entfernen

.CloseRexxLib   movea.l d6,a1                       ; RexxSysBase
                jsr     _LVOCloseLibrary(a6)        ;  schließen

.EXIT           move.l  d7,d0                       ; Returnwert setzen
                movem.l (sp)+,d1-d3/d6-d7/a0-a2/a6
                rts


*************************  OpenARexxPortA  ***************************
*
*  struct MsgPort *OpenARexxPortA(void);
*
  CNOP 0,2
  xdef _OpenARexxPortA
_OpenARexxPortA: movem.l d1/d7/a0-a2/a6,-(sp)
            moveq   #FALSE,d7                   ; Returnwert = FALSE
            move.l  4.w,a6                      ; SysBase laden

            lea     _ARexxPortName(pc),a2
            jsr     _LVOForbid(a6)              ; sicherheitshalber...

            movea.l a2,a1
            jsr     _LVOFindPort(a6)            ; Port 'ACDPlay' suchen
            tst.l   d0                          ; ACDPlay schon gestartet?
            bne     .EXIT                       ; ja: Ende

            jsr     _LVOCreateMsgPort(a6)       ; Port erstellen
            move.l  d0,d7                       ; Returnwert = MsgPort *
            beq     .EXIT                       ;  oder NULL

            movea.l d0,a1
            move.l  a2,LN_NAME(a1)              ; Namen und Priorität (die AddPort sowieso ändert)
            move.b  #0,LN_PRI(a1)               ;  des MessagePorts setzen
            jsr     _LVOAddPort(a6)             ;  und öffentlich machen

.EXIT       jsr     _LVOPermit(a6)              ; Und die Welt darf sich wieder drehen :-)
            move.l  d7,d0
            movem.l (sp)+,d1/d7/a0-a2/a6
            rts

  xdef _ARexxPortName
_ARexxPortName: dc.b "ACDPLAY",0


*************************  CloseARexxPortA  **************************
*
*  void CloseARexxPortA(register __a1 struct MsgPort *arexxport);
*
* 'arexxport' : öffentlicher Port oder NULL
*
  CNOP 0,2
  xdef _CloseARexxPortA
_CloseARexxPortA: movem.l d0-d2/a0-a1/a6,-(sp)
            move.l  a1,d2                       ; arexxport sichern und testen
            beq     .EXIT
            movea.l 4.w,a6                      ; SysBase
            jsr     _LVORemPort(a6)             ; Port aus der Public-Liste entfernen
            jsr     _LVOForbid(a6)              ; Taskswitching abschalten
            movea.l d2,a0                       ;  damit uns nach FlushMessages
            bsr     _FlushMessagesA             ;  niemand mehr Nachrichten in
            movea.l d2,a0                       ;  den Port legt
            jsr     _LVODeleteMsgPort(a6)       ; MsgPort entfernen
            jsr     _LVOPermit(a6)              ; Multitasking wieder erlauben
.EXIT       movem.l (sp)+,d0-d2/a0-a1/a6
            rts


*************************  ExecuteARexxA  ***************************
*
*  BOOL ExecuteARexxA(register __a0 char *rexxscript, register __a1 struct MsgPort *rexxport, register __a2 struct Application *app);
*
* Führt das übergebene ARexxmacro "rexxscript" aus. Wurde QUIT empfangen,
* wird QUIT zurückgegeben.
*
  CNOP 0,2
  xdef _ExecuteARexxA
_ExecuteARexxA: movem.l d1-d7/a0-a6,-(sp)
                moveq   #FALSE,d7
                moveq   #0,d5
                move.l  4.w,a6                      ; SysBase laden
                move.l  a0,a3                       ; a3=Filename
                move.l  a1,a4                       ; a4=eigener ARexxPort

.MakeReplyPort  jsr     _LVOCreateMsgPort(a6)       ; ReplyPort erstellen
                move.l  d0,d3                       ; d3=ReplyMsgPort für
                beq     .EXIT                       ;  Antwort von REXX

.PrepareRexxMsg movea.l _RexxSysBase,a6             ; RexxSysBase laden
                movea.l d0,a0                       ; ReplyPort
                suba.l  a1,a1                       ; Standartextension "REXX"
                moveq   #0,d0                       ; Standardport "REXX"
                jsr     _LVOCreateRexxMsg(a6)
                move.l  d0,d2                       ; d2 = a0 = RexxMsg
                beq     .KillReplyPort              ; nicht geklappt: Ende

                move.l  a3,ARG0(a0)                 ; Filename ist 1. Argument
                move.l  #RXFUNC,rm_Action(a0)       ; RexxMsg ist eine RX_Function
                moveq   #1,d0                       ; nur ein Argument
                moveq   #0,d1                       ; alle Argumente sind STRPTRs
                jsr     _LVOFillRexxMsg(a6)         ; Argumente in ArgStrs konvertieren
                tst.l   d0                          ; erfolgreich?
                beq     .DeleteRexxMsg              ; nein: Ende

                move.l  4.w,a6                      ; SysBase greifen
                suba.l  a1,a1
                jsr     _LVOFindTask(a6)            ; eigenen Task finden
                move.l  d0,a0                       ; Task wird immer gefunden
                tst.l   pr_CIS(a0)                  ; ACDPlay ist ein Process
                beq     .OpenCON                    ; Kein Eingabestrom...
                tst.l   pr_COS(a0)
                bne     .SendMsg                    ; Ein- und Ausgabestrom da

.OpenCON        move.l  _DOSBase,a6                 ; DOSBase laden
                lea     CONName(pc),a0
                move.l  a0,d1                       ; Filename "CON:..."
                move.l  d2,d4                       ; RexxMsg sichern
                move.l  #MODE_OLDFILE,d2            ; Zugriffsmodus
                jsr     _LVOOpen(a6)
                move.l  d0,d5                       ; d5=filehandle
                beq     .doserror                   ; Fehler...
                move.l  d4,d2                       ; RexxMsg zurück
                move.l  d4,a0
                move.l  d0,rm_Stdin(a0)             ; Ein- und Ausgabe in
                move.l  d0,rm_Stdout(a0)            ;  RexxMsg eintragen

.doserror       movea.l 4.w,a6                      ; SysBase laden

.SendMsg        jsr     _LVOForbid(a6)              ; Multitasking aus
                lea     RexxName(pc),a1
                jsr     _LVOFindPort(a6)            ; Port von RexxMast holen
                tst.l   d0
                bne     .SendIt                     ; Port überhaupt noch da?
                 jsr    _LVOPermit(a6)              ; Multitasking wieder an
                 bra    .ClearRexxMsg               ;  und Ende

.SendIt         movea.l d0,a0                       ; gefundener Port
                movea.l d2,a1                       ; RexxMsg
                jsr     _LVOPutMsg(a6)              ; ab geht die Post :-)
                jsr     _LVOPermit(a6)              ; Tasks laufen wieder und
                                                    ;  das Macro startet jetzt
.Loop           movea.l d3,a0
                moveq   #0,d1
                moveq   #1,d0
                move.b  MP_SIGBIT(a0),d1            ; Signalbit vom ReplyPort
                lsl.l   d1,d0                       ;  in Bitmaske wandeln
                moveq   #1,d4
                move.b  MP_SIGBIT(a4),d1            ; Signalbit vom ARexxPort
                lsl.l   d1,d4                       ;  in Bitmaske wandeln
                or.l    d4,d0                       ;  und zusammenodern :-)

                jsr     _LVOWait(a6)                ; auf Message warten
                cmp.l   d0,d4                       ; Message vom Macro (ARexxkommando)?
                bne     .IsEnd                      ; nein: warscheinlich vom ReplyPort (Macro zuende)
                move.l  a4,a0
                move.l  a2,a1
                bsr     _HandleARexxMsgA            ; ARexxkommando bearbeiten
                or.l    d0,d7                       ; QUIT empfangen?
                bra     .Loop                       ; auf ein neues...

.IsEnd          movea.l d3,a0                       ; ReplyPort
                jsr     _LVOGetMsg(a6)              ; Message holen
                tst.l   d0                          ; wirklich Reply von Rexx?
                beq     .Loop

.ClearRexxMsg   movea.l _RexxSysBase,a6             ; RexxSysBase wieder laden
                movea.l d2,a0                       ; RexxMsg
                moveq   #1,d0                       ; einen ArgString freigeben
                jsr     _LVOClearRexxMsg(a6)

                move.l  d5,d1                       ; CON-Filehandle
                beq     .DeleteRexxMsg              ;  falls vorhanden...
                move.l  _DOSBase,a6
                jsr     _LVOClose(a6)               ;  ...schließen
                move.l  _RexxSysBase,a6

.DeleteRexxMsg  movea.l d2,a0                       ; RexxMsg
                jsr     _LVODeleteRexxMsg(a6)       ; freigeben

.KillReplyPort  movea.l 4.w,a6                      ; und SysBase wieder laden
                movea.l d3,a0                       ; eigenen ReplyPort
                jsr     _LVODeleteMsgPort(a6)       ;  entfernen

.EXIT           move.l  d7,d0                       ; returnwert setzen
                movem.l (sp)+,d1-d7/a0-a6
                rts

RexxName:   dc.b "REXX",0
CONName:    dc.b "CON:0/100/640/140/ACDPlay Output/AUTO/WAIT",0


***********************  HandleARexxMsgA  ****************************
*
*  BOOL HandleARexxMsgA(register __a0 struct MsgPort *arexxport, register __a1 struct Application *app);
*
* Gibt TRUE zurück wenn QUIT empfangen wurde
*
  CNOP 0,2
  xdef _HandleARexxMsgA
_HandleARexxMsgA:   movem.l d1-d3/d7/a0-a6,-(sp)
                    moveq   #FALSE,d7               ; success=FALSE
                    move.l  4.w,d3                  ; SysBase speichern um
                    movea.l d3,a6                   ;  CHIPRam-Zugriffe zu vermeiden
                    move.l  a0,d2                   ; MsgPort sichern
                    movea.l a1,a5                   ; Appzeiger sichern

.GetNextMsg         movea.l d2,a0
                    jsr     _LVOGetMsg(a6)          ; GetMsg(arexxport)
                    tst.l   d0                      ; Msg da?
                    beq     .EXIT                   ; nein: raus hier
                    movea.l d0,a4                   ; a4=Message

.TstMsg             movea.l _RexxSysBase,a6         ; RexxSysBase laden
                    movea.l d0,a0
                    jsr     _LVOIsRexxMsg(a6)       ; kommt Msg von ARexx?
                    tst.b   d0
                    bne     .GetFirstToken          ; ja: weitermachen
                    move.l  LN_NAME(a4),a0
                    lea     PrivateMsg(pc),a1       ; 4 Bytes reichen für 'ACDP'
                    cmp.l   (a0)+,(a1)+             ; kommt Msg von ACDPlay?
                    bne     .ReplyMsg               ; nein: zurückschicken

.GetFirstToken      movea.l ARG0(a4),a0             ; ARexxCmd -> a0
                    jsr     _LVOStcToken(a6)        ; a1=FirstToken
                    tst.w   d1                      ; d1=TokenLenght
                    beq     .ReplyMsg               ; leeres Kommando ''
                    move.l  a0,a2                   ; a2=Scan (Rest)
                    lea     Commands(pc),a0         ; a0=Schlüsselwörter
                    lea     JmpTable(pc),a3         ; a3=Sprungtabelle
                    move.l  #10,rm_Result1(a4)      ; sicherheitshalber RETURN_ERROR :-)
                    tst.b   d0                      ; Anführungszeichen vorhanden?
                    beq     .CmpNextCmd             ; nein: weiter
                    subq.w  #1,d1                   ; TokenLength-1
                    addq.l  #1,a1                   ; ' bzw. " übergehen

.CmpNextCmd         tst.b   (a0)                    ; weiteres Kommando bekannt?
                    beq     .ReplyMsg               ; nein: zurückschicken
                    move.l  d1,d0                   ; d1 Zeichen vergleichen
                    bsr     _StrCmpA
                    tst.b   d0                      ; Kommando bekannt?
                    bne     .FoundCmd               ; ja: Funktion anspringen
.SkipRest           tst.b   (a0)+                   ; nächstes Kommando
                    bne     .SkipRest               ;  suchen
                    addq.w  #4,a3                   ; JmpTableoffset erhöhen
                    bra     .CmpNextCmd             ; nächstes vergleichen

.FoundCmd           movem.l d1-d6/a0-a4,-(a7)       ; Register sichern, damit das Kommando keinen Unsinn macht ;-)
                    move.l  (a3),a3                 ; Adresse des Kommandos laden um ...Buttonaufrufe vorzubereiten
                    move.l  a5,-(a7)                ; AppStruct auf Stack sichern
                    move.l  40(a5),a0
                    move.l  8(a0),a0                ; app->cdstr->cdx nach a0
                    moveq   #0,d0                   ; Errorcode vordefinieren
                    jsr     (a3)                    ; Kommando anspringen
                    move.l  (a7)+,a5
                    movem.l (a7)+,d1-d6/a0-a4       ; Register zurück
                    move.l  d0,rm_Result1(a4)       ; Errorcode in RexxMsg setzen

.ReplyMsg           movea.l d3,a6                   ; SysBase wieder laden
                    movea.l a4,a1
                    jsr     _LVOReplyMsg(a6)        ; Message zurück
                    tst.b   d7                      ; QUIT empfangen?
                    bne     .EXIT                   ; ja: gleich beenden
                    bra     .GetNextMsg             ;  und nächste holen

.EXIT               move.l  d7,d0                   ; "done" zurückgeben
                    movem.l (sp)+,d1-d3/d7/a0-a6
                    rts

PrivateMsg: dc.b 'ACDP',0


*****************************  StrCmpA  ******************************
*
*  BOOL StrCmpA(register __a0 char *str1, register __a1 char *str2, register __d0 UWORD length);
*
* Vergleicht die Strings str1 und str2 Zeichen für Zeichen bis length oder
* das Ende von str1 erreicht ist oder eine Abweichung erkannt wird. Dabei wird
* nicht zwischen Groß- und Kleinschreibung unterschieden. Die Funktion gibt
* nur dann TRUE zurück, wenn str1 vollständig in str2 enthalten ist.
*
  CNOP 0,2
  xdef _StrCmpA
_StrCmpA:   movem.l d1-d2/d7/a0-a1,-(sp)
            moveq   #FALSE,d7           ; success=FALSE
            subq.w  #1,d0               ; dbra geht bis -1, darum jetzt d0--

.GetChar1   move.b  (a0)+,d1            ; Byte aus String1 einlesen
            cmpi.b  #'a',d1             ;  und gegebenenfalls
            bcs     .GetChar2           ;  Kleinbuchstaben in Großbuchstaben
            cmpi.b  #'z',d1             ;  wandeln
            bhi     .GetChar2
            bclr    #5,d1               ; 'a' <= d1 <= 'z'
.GetChar2   move.b  (a1)+,d2            ; s.o.
            cmpi.b  #'a',d2
            bcs     .CmpChars
            cmpi.b  #'z',d2
            bhi     .CmpChars
            bclr    #5,d2               ; 'a' <= d2 <= 'z'
.CmpChars   cmp.b   d1,d2               ; Bytes identisch?
            bne     .EXIT
            dbra    d0,.GetChar1        ; wenn ja: weiter bis d0=-1
            tst.b   (a0)                ; Ende von str1 erreicht?
            bne     .EXIT               ; nein: str1 ist nicht in str2
            moveq   #TRUE,d7            ; sonst: success=TRUE

.EXIT       move.l  d7,d0               ; return success
            movem.l (sp)+,d1-d2/d7/a0-a1
            rts


***********************  FlushMessagesA  *****************************
*
*  void FlushMessagesA(register __a0 struct MsgPort *port);
*
* Leert den MsgPort 'port', der auch NULL sein kann.
*
  CNOP 0,2
  xdef _FlushMessagesA
_FlushMessagesA:    movem.l d0-d2/a0-a1/a6,-(sp)
                    move.l  a0,d2                   ; MsgPort sichern und testen
                    beq     .EXIT
                    move.l  4.w,a6                  ; SysBase laden

.loop               jsr     _LVOGetMsg(a6)          ; Msg holen
                    tst.l   d0                      ; vorhanden?
                    beq     .EXIT                   ; nein: Ende
                    move.l  d0,a1
                    jsr     _LVOReplyMsg(a6)        ; ja: antworten
                    movea.l d2,a0                   ; MsgPort zurückholen
                    bra     .loop                   ; nächste Msg holen

.EXIT               movem.l (sp)+,d0-d2/a0-a1/a6
                    rts


***********************  MyGT_SetGadgetAttrs  *****************************
*
*  void MyGT_SetGadgetAttrs(struct Gadget *gadget, struct Window *window, struct Requester *req, ...);
*
* Prüft 'gadget' und 'window' auf NULL und ruft ggf. GT_SetGadgetAttrsA()
* aus der gadtools.library auf. Diese Funktion basiert auf GT_SetGadgetAttrs()
* aus der amiga.lib.
*
  CNOP 0,2
  xdef _MyGT_SetGadgetAttrs
_MyGT_SetGadgetAttrs:   link    a5,#0
                        movem.l a2-a3/a6,-(sp)
                        movea.l $0c(a5),d0
                        beq     .EXIT
                        move.l  d0,a1
                        movea.l $08(a5),d0
                        beq     .EXIT
                        move.l  d0,a0
                        lea     $14(a5),a3
                        movea.l $10(a5),a2
                        movea.l _GadToolsBase,a6
                        jsr     _LVOGT_SetGadgetAttrsA(a6)
.EXIT                   movem.l (sp)+,a2-a3/a6
                        unlk    a5
                        rts


 include "rexxcmds.s"

