*
*   Quick.s V0.2 für PickCDID V0.4
*

 include 'exec/ports.i'
 include 'exec/memory.i'
 include 'devices/scsidisk.i'
 include 'devices/cd.i'
 include 'dos/dos.i'
 include 'dos/dosextens.i'
 include 'intuition/intuition.i'
 include 'libraries/gadtools.i'
 include 'libraries/locale.i'
 include 'lvo/dos.i'
 include 'lvo/exec.i'
 include 'lvo/intuition.i'

 include "CD-ROM.i"

 xref __wbflag
 xref _DOSBase
 xref _IntuitionBase
 xref _ReqToolsBase

_LVOrtEZRequestA    equ -66

FALSE           equ 0
TRUE            equ 1
NULL            equ 0

 ;include "CD-ROM.s"


**************************  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


**************************  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-a4/a6,-(sp)

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

.WBMsg          move.l  _ReqToolsBase,d1            ; ReqToolsLib geöffnet?
                beq     .asl
                movea.l d1,a6                       ; ReqToolsBase laden
                suba.l  a0,a0                       ; taglist=NULL
                move.l  44(a7),a1                   ; ReqBody
                lea     Okay(pc),a2                 ; GadgetBody
                suba.l  a3,a3                       ; *reqInfo=NULL
                movea.l a7,a4                       ; * auf restliche
                adda.l  #48,a4                      ;  Argumente nach a4
                jsr     _LVOrtEZRequestA(a6)
                bra     .EXIT

.asl            lea     MsgEasyStruct(pc),a1        ; a1=*EasyStruct
                move.l  40(a7), es_Title(a1)        ; Titel eintragen
                move.l  44(a7), es_TextFormat(a1)   ; Text eintragen
                lea     Okay(pc),a0
                move.l  a0, es_GadgetFormat(a1)     ; Gadgettext eintragen
                suba.l  a0,a0                       ; *Window=NULL
                suba.l  a2,a2                       ; a2=NULL (IDCMP)
                movea.l a7,a3                       ; * auf restliche
                adda.l  #48,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  44(a7),d1                   ; ReqBody als Arg für VPrintf
                move.l  a7,d2
                addi.b  #48,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-a4/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

Okay:           dc.b "Okay",0
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 ShowMsgA
                lea     NoMem(pc),a0            ;  auf den Stack schieben
                move.l  a0,-(a7)
                lea     Error(pc),a0
                move.l  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

Error:      dc.b "Error",0
NoMem:      dc.b "Could not get %lU bytes of memory",0

***********************  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

*////*---------------- CDOpenDeviceA() ----------------------------< >-*/
*/
*  struct CDxRequest *CDOpenDeviceA(register __a0 char *devicename, register __d0 ULONG unitnumber, register __d1 ULONG lun);
*
* Öffnet das übergebene SCSI-Device und gibt einen Zeiger auf eine
* ausgefüllte CDx-Struktur zurück. Konnte das Device nicht geöffnet werden,
* wird der Fehler angezeigt und die Funktion gibt NULL zurück.
* Achtung: Der Devicename wird nicht kopiert, und muß für CDChangeDeviceA()
*          unbedingt erhalten bleiben!
*
  CNOP 0,2
  xdef _CDOpenDeviceA
_CDOpenDeviceA: movem.l d1-d7/a0-a6,-(sp)
                move.l  4.w,a6                      ; a6 = SysBase

                move.l  a0,a4                       ; a4 = devicename
                move.l  d0,d2                       ; d2 = unitnumber
                move.l  d1,d3                       ; d3 = lun

                moveq   #0,d4

.GetCDxStruct   move.l  #cdx_SIZEOF+CDTOC_SIZEOF+AUDIOPAGE_SIZEOF,d0  ; Speicher für CDxRequest, cdx_TOCData
                move.l  #MEMF_PUBLIC|MEMF_CLEAR,d1  ;  und cdx_ValidAudioPage
                bsr     _GetVecA
                tst.l   d0
                beq     .EXIT
                move.l  d0,a3                       ; a3 = CDxRequest
                addi.l  #cdx_SIZEOF,d0
                move.l  d0,cdx_TOCData(a3)          ; alle Adressen eintragen
                addi.l  #CDTOC_SIZEOF,d0
                move.l  d0,cdx_ValidAudioPage(a3)

.InitCDxStruct  move.b  d2,cdx_Unit(a3)
                move.b  d3,cdx_LUN(a3)
                move.l  a4,cdx_DeviceName(a3)

.GetBufMem      move.l  #DATA_LEN+SENSE_LEN,d0      ; 2 Puffer-Blöcke
                move.l  #MEMF_PUBLIC|MEMF_24BITDMA|MEMF_CLEAR,d1
                bsr     _GetVecA
                move.l  d0,cdx_SCSIData(a3)         ; Data-Block
                beq     FreeCDxStruct
                addi.l  #DATA_LEN,d0                ; Sense-Block errechnen
                move.l  d0,cdx_SCSISense(a3)

.CreateIOPort   jsr     _LVOCreateMsgPort(a6)
                move.l  d0,cdx_IOPort(a3)
                bne     .CreateIOReq
                 lea     NoPort(pc),a0              ;  auf den Stack schieben
                 move.l  a0,-(a7)
                 lea     Error(pc),a0
                 move.l  a0,-(a7)
                 bsr     _ShowMsgA                  ; Error anzeigen
                 lea     8(a7),a7                   ; Stack wieder runtersetzen
                 bra    FreeBufMem

.CreateIOReq    movea.l d0,a0
                moveq   #IOSTD_SIZE,d0
                jsr     _LVOCreateIORequest(a6)
                move.l  d0,cdx_IORequest(a3)
                beq     DeleteIOPort
                move.l  d0,a2                       ; a2 = cdx_IORequest

.OpenDevice     move.l  d3,d0
                mulu.w  #10,d0
                add.l   d2,d0                       ; d0 = unit + lun*10
                moveq   #0,d1                       ; Flags
                move.l  a4,a0                       ; devicename
                move.l  a2,a1                       ; io_request
                jsr     _LVOOpenDevice(a6)
                tst.l   d0
                beq     .GetCmdStruct
                 move.l  d3,-(a7)                   ; Argumente für ShowMsgA
                 move.l  d2,-(a7)
                 move.l  a4,-(a7)
                 lea     NoDevice(pc),a0            ;  auf den Stack schieben
                 move.l  a0,-(a7)
                 lea     Error(pc),a0
                 move.l  a0,-(a7)
                 bsr     _ShowMsgA                  ; Error anzeigen
                 lea     20(a7),a7                  ; Stack wieder runtersetzen
                 bra     DeleteIOReq

.GetCmdStruct   move.l  #scsi_SIZEOF,d0
                move.l  #MEMF_PUBLIC|MEMF_CLEAR,d1  ; RAM für die SCSICmd-
                bsr     _GetVecA                    ;  Struktur
                move.l  d0,cdx_Cmd(a3)
                beq     CloseDevice
                move.l  d0,a1                       ; a1 = SCSICmd

                move.l  a3,a0
                bsr     _CDInitDeviceA              ; CDx Rest ausfüllen

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

NoDevice:   dc.b    "Could not open SCSI device '%s' Unit %ld LUN %ld",0
NoPort:     dc.b    'Failed to create message port',0


********************* CDInitDeviceA() *********************************
*
*  LONG CDInitDeviceA(register __a0 struct CDxRequest);
*
*
  CNOP 0,2
_CDInitDeviceA: movem.l d1-d7/a0-a6,-(sp)
                move.l  a0,a3                       ; a3 = cdx

                move.l  cdx_IORequest(a0),a1
                move.w  #HD_SCSICMD,IO_COMMAND(a1)  ; IOStdRequest füllen
                move.l  cdx_Cmd(a0),IO_DATA(a1)
                move.l  #scsi_SIZEOF,IO_LENGTH(a1)

.DoInquiry      moveq   #6,d0
                moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
                lea     CMDInquiry,a1
                bsr     CDDoSCSIA                   ; Inquiry schicken
                bne     .EXIT

                move.l  cdx_SCSIData(a3),a2
                move.b  (a2),d0
                andi.l  #%00011111,d0               ; nur Bits 0-4
                move.b  d0,cdx_DeviceType(a3)       ; Peripheral Device Type

                adda.l  #8,a2                       ; a2 = CDROM-VendorID
.VendorUnknown  lea     UnknownVendor(pc),a0
                move.l  a0,cdx_Vendor(a3)
                move.l  (a2)+,(a0)+
                move.l  (a2),(a0)

.CopyRest       adda.l  #8,a2                       ; nächte Bytes der Inquirydaten
                lea     cdx_Product(a3),a0          ; Product Identification
                moveq   #3,d0                       ; 16 Bytes (4 Langworte)
.l1             move.l  (a2)+,(a0)+                 ;  kopieren
                dbra    d0,.l1
                lea     cdx_Revision(a3),a0         ; Product Revision Level
                move.l  (a2)+,(a0)                  ; nur 4 Bytes groß
                lea     cdx_Specific(a3),a0         ; Vendor Specific
                moveq   #4,d0                       ; 20 Bytes (5 Langworte)
.l2             move.l  (a2)+,(a0)+                 ;  kopieren
                dbra    d0,.l2

.FillCDx        move.b  #AUDIO_STAT_NO_DISK,cdx_AudioStatus(a3)
                move.b  #AUDIO_STAT_NO_DISK,cdx_Active(a3)
                move.l  a3,a0
                bsr     _CDUpdateA                  ; Rest der CDx-Struktur füllen
                bsr     _CDReadTOCA                 ; TOC einlesen

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

CMDInquiry:     dc.b    $12, 0, 0, 0, 96, 0


*********************** CDCloseDeviceA() *****************************
*
*  BOOL CDCloseDeviceA(register __a0 struct CDxRequest);
*
* Schließt ein SCSI-Device, das mit CDOpenDeviceA() geöffnet wurde. Das
* Ergebnis ist dabei immer NULL.
*
  CNOP 0,2
  xdef _CDCloseDeviceA
_CDCloseDeviceA: movem.l d1-d7/a0-a6,-(sp)
                move.l  4.w,a6
                move.l  a0,a3                       ; a3 = MyCDxReq

CloseCmdStruct  move.l  cdx_Cmd(a3),a1
                jsr     _LVOFreeVec(a6)

CloseDevice     move.l  cdx_IORequest(a3),a1
                jsr     _LVOCloseDevice(a6)

DeleteIOReq     move.l  cdx_IORequest(a3),a0
                jsr     _LVODeleteIORequest(a6)

DeleteIOPort    move.l  cdx_IOPort(a3),a0
                jsr     _LVODeleteMsgPort(a6)

FreeBufMem      move.l  cdx_SCSIData(a3),a1
                jsr     _LVOFreeVec(a6)

FreeCDxStruct   move.l  a3,a1
                jsr     _LVOFreeVec(a6)

.EXIT           moveq   #NULL,d0
                movem.l (sp)+,d1-d7/a0-a6
                rts


************************ CDDoSCSIA() *********************************
*
*  LONG CDDoSCSIA(register __a0 struct CDxRequest *cdx, a1 APTR Command, d0 cmdlen. d1 flags);
*
* Führt das übergebene SCSI Direktkommando aus und gibt im Fehlerfall einen
* Wert zurück, der sich aus dem Sense Key (Bit 16-31), dem Additional Sense
* Code (Bit 8-15) und dem Additional Sense Code Qualifier (Bit 0-7)
* zusammensetzt. Ist keine CD eingelegt werden alle CD-abhängigen Daten auf
* 0 gesetzt. Die Conditioncodes werden in Abhängigkeit von d0 gesetzt.
*
  CNOP 0,2
CDDoSCSIA:      movem.l d1/a0-a2/a6,-(sp)
                move.l  4.w,a6                  ; SysBase...
                move.l  cdx_Cmd(a0),a2          ; a2=SCSICmd

                move.l  cdx_SCSIData(a0),(a2)+  ; scsi_Data;
                move.l  #DATA_LEN,(a2)+         ; scsi_Length;
                clr.l   (a2)+                   ; scsi_Actual;
                move.l  a1,(a2)+                ; scsi_Command;
                move.w  d0,(a2)+                ; scsi_CmdLength;
                clr.w   (a2)+                   ; scsi_CmdActual;
                move.b  d1,(a2)+                ; scsi_Flags;
                clr.b   (a2)+                   ; scsi_Status;
                move.l  cdx_SCSISense(a0),(a2)+ ; scsi_SenseData;
                move.w  #SENSE_LEN,(a2)+        ; scsi_SenseLength;
                clr.w   (a2)                    ; scsi_SenseActual;

                move.l  a0,a2                   ; CDxRequest retten

                move.l  cdx_IORequest(a0),a1
                jsr     _LVODoIO(a6)            ; Kommando schicken und warten

                moveq   #0,d0                   ; Return = 0
                move.l  cdx_Cmd(a2),a1          ; a1=SCSICmd
                tst.w   scsi_SenseActual(a1)    ; Sense Daten da?
                beq     .EXIT

                move.l  scsi_SenseData(a1),a1   ; a1=SenseData
                move.b  (a1),d1                 ; Error Code ($70 oder $71)
                andi.b  #%01111111,d1           ; Valid-Bit auslassen
                cmp.b   #$70,d1                 ; Current Error ($70) ?
                bne     .EXIT
                move.b  2(a1),d0                ; d0=Sense Key
                andi.b  #%00001111,d0           ; nur 4 Bits
                tst.b   d0                      ; NO SENSE ($0) ?
                beq     .EXIT
                move.b  d0,cdx_SenseKey(a2)     ; in cdx schreiben (für ARexx)
                swap.w  d0                      ; Returnwert berechnen
                cmp.b   #6,7(a1)                ; ASC und ASCQ vorhanden?
                blt     .EXIT
                move.w  12(a1),d0               ; ASC und ASCQ zu Returnwert
                move.w  d0,cdx_AddSenseCode(a2) ; in cdx schreiben

.EXIT           movem.l (sp)+,d1/a0-a2/a6
                tst.l   d0                      ; Conditioncodes setzen
                rts


************************ CDUpdateA() **********************************
*
*  LONG CDUpdateA(register __a0 struct CDxRequest *cdx);
*
* Aktualisiert den Track, Index, Adresssen und Audiostatus der übergebenen
* CDx-Struktur. Tritt ein Fehler auf, liefert die Funktion einen Wert in
* dem in CDDoSCSIA beschriebenem Format zurück. Wurde seit dem letztem
* Update eine CD eingelegt, wird der TOC neu eingelesen. Entspricht der
* aktuelle Audiostatus AUDIO_STAT_NO_DISK, AUDIO_STAT_NOT_VALID oder
* AUDIO_STAT_NONE, werden die Positionsfelder der CDx-Struktur auf 0 gesetzt.
*
  CNOP 0,2
  xdef _CDUpdateA
_CDUpdateA:     movem.l d1-d3/a0-a2,-(sp)

                moveq   #10,d0                  ; 10 Byte Kommando
                moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
                lea     CMDReadSubCh(pc),a1
                bsr     CDDoSCSIA               ; Kommando ausführen
                beq     .DiscChanged            ; alles korrekt

                cmp.l   #$023A00,d0             ; NO MEDIA PRESENT ?
                bne     .EXIT                   ; unerwarteter Fehler

                move.b  #AUDIO_STAT_NO_DISK,cdx_AudioStatus(a0)
                move.b  #AUDIO_STAT_NO_DISK,cdx_Active(a0)

.NoCD           move.l  cdx_TOCData(a0),a1      ; keine CD eingelegt: alles
                moveq   #3,d1                   ;  CD spezifische löschen
.loop           clr.l   (a1)+                   ; FirstTrack/LastTrack/FirstAudioTrack/reserved3/FirstAudioAddress
                dbra    d1,.loop                ; und Entry[0] löschen

.Stopped        clr.w   (a0)+                   ; cdx_CurrentTrack/CurrentIndex
                moveq   #4,d1
.loop2          clr.l   (a0)+                   ; cdx_CurrentAbsAddr/CurrentRelAddr/RelRemainBlocks/AbsRemainBlocks/TrackLength
                dbra    d1,.loop2
                bra     .EXIT

.SetSimpleStat  move.b  #AUDIO_STAT_STOPPED,cdx_Active(a0)
                bra     .Stopped

.DiscChanged    cmp.b   #AUDIO_STAT_NO_DISK,cdx_AudioStatus(a0)    ; neue Disk eingelegt?
                bne     .UpdateCDx
                bsr     _CDReadTOCA             ; ja: Table Of Contents neu einlesen

.UpdateCDx      move.l  cdx_SCSIData(a0),a1
                addq.l  #1,a1
                move.b  (a1)+,d0                ; Audio Status lesen
                move.b  d0,cdx_AudioStatus(a0)  ;  und setzen
                move.b  d0,cdx_Active(a0)
                beq     .SetSimpleStat          ; AUDIO_STAT_NOT_VALID (=0)
                cmp.b   #AUDIO_STAT_COMPLETED,d0
                bcc     .SetSimpleStat          ; d0 >= AUDIO_STAT_COMPLETED (STOPPED/NONE)
                                                ; übrig bleibt PLAY und PAUSE: Positionen lesen
                moveq   #0,d0                   ; alles okay: RETURN_OK

.EXIT           movem.l (sp)+,d1-d3/a0-a2
                rts

CMDReadSubCh:   dc.b    $42, 0, $40, 1, 0, 0, 0, 0, 16, 0


************************ CDReadTOCA() ********************************
*
*  LONG CDReadTOCA(register __a0 struct CDxRequest *cdx);
*
  CNOP 0,2
  xdef _CDReadTOCA
_CDReadTOCA:    movem.l d1-d3/a0-a3/a6,-(sp)

                moveq   #10,d0
                moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
                lea     CMDReadTOC(pc),a1
                bsr     CDDoSCSIA
                bne     .EXIT

                move.l  cdx_SCSIData(a0),a1
                move.w  (a1)+,d0                ; TOC Data Length
                beq     .EXIT

                move.l  cdx_TOCData(a0),a0      ; da soll alles hin
                move.w  (a1),(a0)+              ; First und Last Track

                move.b  (a1)+,d1                ; FirstTrack
                moveq   #0,d3
                move.b  (a1)+,d3                ; LastTrack
                sub.b   d1,d3
                addq.b  #1,d3                   ; d3=Track Anzahl

                move.l  a0,a3                   ; für später
                clr.w   (a0)+                   ; wird nachher gefüllt...
                clr.l   (a0)+                   ; (TrackNum/FirstAudioTrack/FirstAudioAddress)

                move.l  a0,a2                   ; Track 0 gibt's nicht, da kommt
                clr.l   (a0)+                   ;  nacher die logical block
                clr.l   (a0)+                   ;  address der lead-out area rein

                clr.l   16(a0)                  ; Entry für Track 3 sicherheitshalber löschen
                clr.l   20(a0)                  ; (für ID Berechnung :-)

                lsr.l   #3,d0                   ; durch 8 (pro Entry 8 bytes)
                                                ; die 2 bytes für F. und L. Track fallen raus
                subq.l  #1,d0                   ; dbra endet bei -1
                bmi     .Good                   ; jetzt schon -1?

                moveq   #-1,d2                  ; noch keinen AudioTrack gefunden...

.loop           move.l  (a1)+,d1
                move.l  d1,(a0)+                ; ersten 4 bytes (8 bytes / Entry) kopieren
                tst.b   d2                      ; schon AudioTrack gefunden?
                beq     .loopend
                btst.l  #18,d1                  ; AudioTrack?
                bne     .loopend
                move.b  d3,(a3)+                ; TrackNum
                lsr.w   #8,d1
                move.b  d1,(a3)+                ; FirstAudioTrack
                move.l  (a1),-4(a2)             ; FirstAudioAddress
                moveq   #0,d2
.loopend        move.l  (a1)+,(a0)+             ; letzten 4 Bytes kopieren
                dbra    d0,.loop

.LeadOut        subq.l  #8,a1                   ; letzten Entry nochmal holen
                cmp.b   #$AA,2(a1)              ; wirklich lead out area? (Tracknr. $AA)
                bne     .Good
                move.l  (a1)+,(a2)+             ; ja: nochmal nach Entry[0] kopieren
                move.l  (a1),(a2)

.CalcID         move.l  4.w,a6
                lea     IDFormat(pc),a0
                move.l  (a2),-(a7)              ; Entry[0].Address
                move.l  24(a2),-(a7)            ; Entry[3].Address
                move.l  d3,-(a7)                ; LastTrack-FirstTrack+1
                move.l  a7,a1
                lea     CDID-(Entry+4)(a2),a3
                lea     PutChProc(pc),a2
                jsr     _LVORawDoFmt(a6)        ; ID formatieren
                lea     12(a7),a7

.Good           moveq   #0,d0

.EXIT           movem.l (sp)+,d1-d3/a0-a3/a6
                rts

CMDReadTOC: dc.b $43, 0, 0, 0, 0, 0, 0, $3, $24, 0
IDFormat:   dc.b '%02ld%06lx%06lx',0


************************ CDTestUnitA() ******************************
*
*  LONG CDTestUnitA(register __a0 struct CDxRequest *cdx);
*
* Testet die logical unit ob sie bereit ist. Wenn sie das ist, wird GOOD (0)
* zurückgegeben, ansonsten ein Returncode im in CDDoSCSIA beschriebenem
* Format.
*
  CNOP 0,2
  xdef _CDTestUnitA
_CDTestUnitA:   movem.l d1/a0-a1,-(sp)

                moveq   #6,d0                           ; 6 Byte Kommando
                moveq   #SCSIF_AUTOSENSE,d1             ; REQUEST SENSE ausführen lassen
                lea     CMDReadSubCh(pc),a1
                bsr     CDDoSCSIA                       ; Kommando ausführen

.EXIT           movem.l (sp)+,d1/a0-a1
                rts

CMDTestUnitReady:   dc.b    $0, 0, 0, 0, 0, 0


************************ CDEjectA() *********************************
*
*  LONG CDEjectA(register __a0 struct CDxRequest *cdx, register __d0 BOOL really);
*
* TRUE öffnet die Schublade, FALSE schließt sie. Im Fehlerfall (Schublade
* schon offen/geschlossen) wird ein Fehlercode zurückgegeben. Außerdem
* wird das Flag JUSTCLOSED gesetzt, wenn die Schublade geschlossen werden
* soll, sonst wird es gelöscht.
*
  CNOP 0,2
  xdef _CDEjectA
_CDEjectA:      movem.l d1/a0-a1,-(sp)

                lea     CMDStartStopUnit(pc),a1

.eject          moveq   #2,d1
                and.b   #~SPECIALF_JUSTCLOSED,cdx_Flags(a0)
                tst.l   d0
                bne     .goon
.load           moveq   #3,d1
                or.b    #SPECIALF_JUSTCLOSED,cdx_Flags(a0)
.goon           move.b  d1,4(a1)
                moveq   #6,d0
                moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
                bsr     CDDoSCSIA

.EXIT           movem.l (sp)+,d1/a0-a1
                rts

CMDStartStopUnit:   dc.b $1B, 1, 0, 0, 2, 0

UnknownVendor:  dcb.b   9
