home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 13 / AACD13.ISO / AACD / Utilities / ACDPlay / src / V1.6 / asmlib / CD-ROM.s < prev    next >
Text File  |  1997-10-18  |  60KB  |  1,429 lines

  1. *
  2. *    CD-ROM.s  V0.17 by mak  passend zu ACDPlay.c V1.49 beta
  3. *
  4. * ACHTUNG! Alle Funktionen hier (und auch C) gehen davon aus daß alle Register
  5. * erhalten bleiben. Darum müssen alle Register die irgendwie verändert
  6. * werden gesichert werden. Auch d0, wenn kein Wert zurückgegeben wird.
  7. *
  8. *///Autor  Version   Datum              Veränderungen
  9. * ------- --------- --------    ------------------------------------------
  10. *   mak     0.01    04.01.97    - Ersterstellung CDOpenDeviceA(), CDCloseDeviceA()
  11. *           0.02    07.01.97    - CDUpdateA() dazu
  12. *                   08.01.97    - CDDoSCSIA() erstellt
  13. *                   14.01.97    - CDPauseA() dazu
  14. *           0.03    15.01.97    - CDUpdateA() überarbeitet und CDx-Struktur erweitert
  15. *                               - ReadTOCA() angefangen
  16. *           0.04    19.01.97    - CDEjectA() erstellt
  17. *                               - CDUpdateA() testet jetzt auf Fehler
  18. *                   20.01.97    - Errortest in CDDoSCSIA() eingebaut
  19. *           0.05    21.01.97    - CDPlayA() erstellt
  20. *           0.06    23.01.97    - CDStopA() erstellt
  21. *                   24.01.97    - CDTestUnitA() hinzugefügt
  22. *           0.07    27.01.97    - CDJumpA() endlich fertig
  23. *                   28.01.97    - CDJumpA() kennt jetzt 4 Arbeitsmodi
  24. *                               - CDReadTOCA() beendet
  25. *                   30.01.97    - Funktion CDReadTOC() überarbeitet: leadout jetzt in Entry[0] und Entry[lasttrack+1]
  26. *                               - TOC wird abhängig vom AudioStatus in CDUpdateA() neu eingelesen
  27. *                               - CDOpenDeviceA() liest TOC nun auch ein
  28. *                               - DoSCSIA setzt Conditioncodes
  29. *           0.08    31.01.97    - MODE SENSE angefangen (Volume) usw.
  30. *                   01.02.97    - CDxRequest Struktur überholt
  31. *                               - CDUpdateA() überarbeitet: setzt jetzt Positionen ggf. auf 0
  32. *           0.09    07.02.97    - CDReadTOCA() sucht jetzt 1. AudioTrack heraus und errechnet die DiskID
  33. *                   08.02.97    - Argument singletrack aus CDPlayA() entfernt
  34. *                               - CDNextA() und CDPrevA() erstellt
  35. *           0.10    26.02.97    - FirstTrack usw. und TocEntry[0] wird gelöscht wenn keine Disk eingelegt
  36. *                   27.02.97    - LUN wird jetzt bei Fehler auch angezeigt
  37. *                               - cdx_TrackLength hinzu
  38. *           0.11    02.03.97    - Sensecodes in CDxRequest eingebaut
  39. *                               - Bugfix (falscher Returncode) in CDUpdateA()
  40. *                   05.03.97    - cdx_SimpleStatus zwecks Kompatibilität zur cdplayLib dazu
  41. *           0.12    08.03.97    - TrackNum in CDTOC Struktur eingefügt und kleine Änderungen in CDUpdateA()
  42. *                   11.03.97    - cdx_LUN und CDChangeDeviceA() dazu
  43. *                               - CDEjectA() setzt jetzt JUSTCLOSED-Flag
  44. *           0.13    23.03.97    - Datenrichtung von TestUnitCmd ist nicht mehr SCSIF_READ
  45. *                   25.03.97    - CDPlayA() funktioniert nun endlich mit NEC-Laufwerken
  46. *                               - CDFastModeA() dazu
  47. *                               - Eject jetzt mit Immediate-Bit
  48. *                   28.03.97    - CDSetVolumeA() ist endlich da :-)
  49. *           0.14    29.03.97    - Lautstärkefunktion in CDVolumeA() umbenannt und cdx_Volume hinzu
  50. *                               - cdx_SimpleStatus in cdx_Active umbenannt
  51. *           0.15    15.06.97    - DoATAPI() eingebaut und kleine Veränderungen am ATAPI-Kram
  52. *           0.16    09.07.97    - ATAPI-CDUpdateA() erweitert
  53. *                               - "Diskwechselbug" in CDUpdateA entfernt der Enforcerhits provuzierte
  54. *                   13.07.97    - cd.device wird 2 mal geöffnet
  55. *                               - Routinen etwas Geschwindigkeitsoptimiert
  56. *                   14.07.97    - ReadTOCA() arbeitet nun auch mit cd.device
  57. *                   15.07.97    - CDUpdateA(), CDStopA(), CDPlayA() und CDPauseA() fertig cd.deviciert
  58. *                   16.07.97    - CDJumpA() endlich auch ATAPI kompatibel
  59. *                               - cdx_VendorID hinzugefügt
  60. *           0.17    28.07.97    - CDLockA() setzt SPECIALF_LOCKED-Flag
  61. *                   29.07.97    - CDID sollte jetzt auch mit NEC-500 Laufwerken generiert werden können
  62. *                   30.07.97    - CDVolumeA(), CDNextA() und CDPrevA() jetzt ATAPI kompatibel
  63. *///
  64.  
  65. ;/// Includes
  66.  include 'exec/memory.i'
  67.  include 'devices/scsidisk.i'
  68.  include 'devices/cd.i'
  69.  
  70.  include "CD-ROM.i"
  71. ;///
  72.  
  73. *////*---------------- CDOpenDeviceA() ----------------------------<c>-*/
  74. */
  75. *  struct CDxRequest *CDOpenDeviceA(register __a0 char *devicename, register __d0 ULONG unitnumber, register __d1 ULONG lun);
  76. *
  77. * Öffnet das übergebene SCSI-Device und gibt einen Zeiger auf eine
  78. * ausgefüllte CDx-Struktur zurück. Konnte das Device nicht geöffnet werden,
  79. * wird der Fehler angezeigt und die Funktion gibt NULL zurück.
  80. * Achtung: Der Devicename wird nicht kopiert, und muß für CDChangeDeviceA()
  81. *          unbedingt erhalten bleiben!
  82. *
  83.   CNOP 0,2
  84.   xdef _CDOpenDeviceA
  85. _CDOpenDeviceA: movem.l d1-d7/a0-a6,-(sp)
  86.                 move.l  4.w,a6                      ; a6 = SysBase
  87.  
  88.                 move.l  a0,a4                       ; a4 = devicename
  89.                 move.l  d0,d2                       ; d2 = unitnumber
  90.                 move.l  d1,d3                       ; d3 = lun
  91.  
  92.                 moveq   #0,d4
  93.                 lea     CDDevice(pc),a1             ; 'cd.device' öffnen?
  94. .NextChar       move.b  (a0)+,d0
  95.                 cmp.b   (a1)+,d0
  96.                 bne     .GetCDxStruct               ; nein: CDx holen usw.
  97.                 tst.b   d0
  98.                 bne     .NextChar
  99.  
  100.                 moveq   #SPECIALF_ATAPI,d4
  101.  
  102. .GetCDxStruct   move.l  #cdx_SIZEOF+CDxTOC_SIZEOF+AUDIOPAGE_SIZEOF+15,d0  ; Speicher für CDxRequest, cdx_TOCData
  103.                 move.l  #MEMF_PUBLIC|MEMF_CLEAR,d1  ;  und cdx_ValidAudioPage
  104.                 bsr     _GetVecA
  105.                 tst.l   d0
  106.                 beq     .EXIT
  107.                 move.l  d0,a0
  108.                 moveq   #15,d5                      ; Diese Ausrichtung der CDx-Struktur
  109.                 add.l   d5,d0                       ;  erhöht die Zugriffsgeschwindigkeit
  110.                 not.l   d5                          ;  auf 030+ Systemen, da nur ein Buszyklus
  111.                 and.l   d5,d0                       ;  für ein Langwortzugriff notwendig wird
  112.                 move.l  d0,a3                       ; a3 = CDxRequest
  113.                 move.l  a0,cdx_MemStart(a3)
  114.                 addi.l  #cdx_SIZEOF,d0
  115.                 move.l  d0,cdx_TOCData(a3)          ; alle Adressen eintragen
  116.                 addi.l  #CDxTOC_SIZEOF,d0
  117.                 move.l  d0,cdx_ValidAudioPage(a3)
  118.  
  119. .InitCDxStruct  or.b    d4,cdx_Flags(a3)
  120.                 move.b  d2,cdx_Unit(a3)
  121.                 move.b  d3,cdx_LUN(a3)
  122.                 move.l  a6,cdx_SysBase(a3)
  123.                 move.l  a4,cdx_DeviceName(a3)
  124.  
  125.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a3)
  126.                 beq     .GetBufMem
  127.  
  128.                 move.l  #CDTOC_SIZE*100+CDINFO_SIZE+QCODE_SIZE,d0
  129.                 move.l  #MEMF_PUBLIC|MEMF_CLEAR,d1
  130.                 bsr     _GetVecA
  131.                 move.l  d0,cdx_SCSIDataStart(a3)
  132.                 beq     FreeCDxStruct
  133.                 move.l  d0,cdx_SCSIData(a3)
  134.                 addi.l  #CDTOC_SIZE*100,d0
  135.                 move.l  d0,cdx_Info(a3)
  136.                 addi.l  #CDINFO_SIZE,d0
  137.                 move.l  d0,cdx_QCode(a3)
  138.                 bra     .CreateIOPort
  139.  
  140. .GetBufMem      move.l  #DATA_LEN+SENSE_LEN+15,d0      ; 2 Puffer-Blöcke
  141.                 move.l  #MEMF_PUBLIC|MEMF_24BITDMA|MEMF_CLEAR,d1
  142.                 bsr     _GetVecA
  143.                 move.l  d0,cdx_SCSIDataStart(a3)    ; auch dies hier soll die Geschwindigkeit
  144.                 beq     FreeCDxStruct
  145.                 addi.l  #15,d0                      ;  auf 030+ CPUs mit DMA-Kontrollern
  146.                 and.l   d5,d0                       ;  "greatly improven"
  147.                 move.l  d0,cdx_SCSIData(a3)         ; Data-Block
  148.                 addi.l  #DATA_LEN,d0                ; Sense-Block errechnen
  149.                 move.l  d0,cdx_SCSISense(a3)
  150.  
  151. .CreateIOPort   jsr     _LVOCreateMsgPort(a6)
  152.                 move.l  d0,cdx_IOPort(a3)
  153.                 bne     .CreateIOReq
  154.                  lea     _ls,a0                     ;  auf den Stack schieben
  155.                  move.l  4*MSG_NO_MSGPORT(a0),-(a7)
  156.                  move.l  4*MSG_ERROR_TITLE(a0),-(a7)
  157.                  bsr     _ShowMsgA                  ; Error anzeigen
  158.                  lea     8(a7),a7                   ; Stack wieder runtersetzen
  159.                  bra    FreeBufMem
  160.  
  161. .CreateIOReq    movea.l d0,a0
  162.                 moveq   #IOSTD_SIZE,d0
  163.                 jsr     _LVOCreateIORequest(a6)
  164.                 move.l  d0,cdx_IORequest(a3)
  165.                 beq     DeleteIOPort
  166.                 move.l  d0,a2                       ; a2 = cdx_IORequest
  167.  
  168. .OpenDevice     move.l  d3,d0
  169.                 mulu.w  #10,d0
  170.                 add.l   d2,d0                       ; d0 = unit + lun*10
  171.                 moveq   #0,d1                       ; Flags
  172.                 move.l  a4,a0                       ; devicename
  173.                 move.l  a2,a1                       ; io_request
  174.                 jsr     _LVOOpenDevice(a6)
  175.                 tst.l   d0
  176.                 beq     .GetCmdStruct
  177.                  move.l  d3,-(a7)                   ; Argumente für ShowMsgA
  178.                  move.l  d2,-(a7)
  179.                  move.l  a4,-(a7)
  180.                  lea     _ls,a0                     ;  auf den Stack schieben
  181.                  move.l  4*MSG_NO_SCSIDEVICE(a0),-(a7)
  182.                  move.l  4*MSG_ERROR_TITLE(a0),-(a7)
  183.                  bsr     _ShowMsgA                  ; Error anzeigen
  184.                  lea     20(a7),a7                  ; Stack wieder runtersetzen
  185.                  bra     DeleteIOReq
  186.  
  187. .GetCmdStruct   move.l  #scsi_SIZEOF,d0
  188.                 move.l  #MEMF_PUBLIC|MEMF_CLEAR,d1  ; RAM für die SCSICmd-Struktur
  189.                 bsr     _GetVecA
  190.                 move.l  d0,cdx_Cmd(a3)
  191.                 beq     CloseDevice
  192.  
  193.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a3)
  194.                 beq     .GoOn
  195.  
  196. .Create2ndPort  jsr     _LVOCreateMsgPort(a6)
  197.                 move.l  d0,cdx_CDPlayIOPort(a3)
  198.                 bne     .Create2ndIOReq
  199.                  lea     _ls,a0                     ;  auf den Stack schieben
  200.                  move.l  4*MSG_NO_MSGPORT(a0),-(a7)
  201.                  move.l  4*MSG_ERROR_TITLE(a0),-(a7)
  202.                  bsr     _ShowMsgA                  ; Error anzeigen
  203.                  lea     8(a7),a7                   ; Stack wieder runtersetzen
  204.                  bra    CloseCmdStruct
  205.  
  206. .Create2ndIOReq movea.l d0,a0
  207.                 moveq   #IOSTD_SIZE,d0
  208.                 jsr     _LVOCreateIORequest(a6)
  209.                 move.l  d0,cdx_CDPlayIORequest(a3)
  210.                 beq     Delete2ndPort
  211.                 move.l  d0,a2                       ; a2 = cdx_IORequest
  212.  
  213. .Open2ndDevice  move.l  d3,d0
  214.                 mulu.w  #10,d0
  215.                 add.l   d2,d0                       ; d0 = unit + lun*10
  216.                 moveq   #0,d1                       ; Flags
  217.                 move.l  a4,a0                       ; devicename
  218.                 move.l  a2,a1                       ; io_request
  219.                 jsr     _LVOOpenDevice(a6)
  220.                 tst.l   d0
  221.                 beq     .GoOn
  222.                  move.l  d3,-(a7)                   ; Argumente für ShowMsgA
  223.                  move.l  d2,-(a7)
  224.                  move.l  a4,-(a7)
  225.                  lea     _ls,a0                     ;  auf den Stack schieben
  226.                  move.l  4*MSG_NO_SCSIDEVICE(a0),-(a7)
  227.                  move.l  4*MSG_ERROR_TITLE(a0),-(a7)
  228.                  bsr     _ShowMsgA                  ; Error anzeigen
  229.                  lea     20(a7),a7                  ; Stack wieder runtersetzen
  230.                  bra     Delete2ndIOReq
  231.  
  232. .GoOn           move.l  a3,a0
  233.                 bsr     _CDInitDeviceA              ; CDx Rest ausfüllen
  234.  
  235.                 move.l  a3,d0                       ; Return CDxRequest
  236. .EXIT           movem.l (sp)+,d1-d7/a0-a6
  237.                 rts
  238.  
  239. CDDevice:       dc.b    'cd.device',0
  240.  
  241. *///
  242. *////*---------------- CDInitDeviceA() ----------------------------<c>-*/
  243. */
  244. *  LONG CDInitDeviceA(register __a0 struct CDxRequest);
  245. *
  246. *
  247.   CNOP 0,2
  248.   xdef _CDInitDeviceA
  249. _CDInitDeviceA: movem.l d1-d7/a0-a6,-(sp)
  250.                 move.l  a0,a3                       ; a3 = cdx
  251.  
  252.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  253.                 beq     .Start
  254.                 move.b  #5,cdx_DeviceType(a0)
  255.                 bra     .FillCDx
  256.  
  257. .Start          move.l  cdx_IORequest(a0),a1
  258.                 move.w  #HD_SCSICMD,IO_COMMAND(a1)  ; IOStdRequest füllen
  259.                 move.l  cdx_Cmd(a0),IO_DATA(a1)
  260.                 move.l  #scsi_SIZEOF,IO_LENGTH(a1)
  261.  
  262. .DoInquiry      moveq   #6,d0
  263.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  264.                 lea     CMDInquiry,a1
  265.                 bsr     CDDoSCSIA                   ; Inquiry schicken
  266.                 bne     .EXIT
  267.  
  268.                 move.l  cdx_SCSIData(a3),a2
  269.                 move.b  (a2),d0
  270.                 andi.l  #%00011111,d0               ; nur Bits 0-4
  271.                 move.b  d0,cdx_DeviceType(a3)       ; Peripheral Device Type
  272.  
  273.                 adda.l  #8,a2                       ; a2 = CDROM-VendorID
  274.                 lea     cdx_VendorID(a3),a0
  275.                 move.l  (a2),(a0)+                  ; VendorID erstmal kopieren
  276.                 move.l  4(a2),(a0)
  277.                 lea     Vendors(pc),a1              ; a1 = Vendor-Tabelle
  278. .NextVendor     tst.b   (a1)                        ; keine weiteren IDs
  279.                 beq     .VendorUnknown              ;  in Tabelle
  280.                 move.l  a2,a0                       ; a0 = CDROM-VendorID
  281. .CmpLoop        move.b  (a1)+,d0                    ; VendorID zuende?
  282.                 beq     .VendorKnown                ; ja: beide identisch!
  283.                 cmp.b   (a0)+,d0                    ; ansonsten weitervergleichen
  284.                 beq     .CmpLoop
  285.                 moveq   #1,d0
  286. .SkipRest       tst.b   (a1)+                       ; Rest der Tabelle
  287.                 bne     .SkipRest                   ;  überspringen
  288.                 dbra    d0,.SkipRest
  289.                 bra     .NextVendor
  290.  
  291. .VendorUnknown  lea     UnknownVendor(pc),a0
  292.                 move.l  a0,cdx_Vendor(a3)
  293.                 move.l  (a2),(a0)+
  294.                 move.l  4(a2),(a0)
  295.                 bra     .Check
  296. .VendorKnown    move.l  a1,cdx_Vendor(a3)
  297.  
  298. .Check          lea     SpecialDevices(pc),a1       ; ist das benutzte LW in der
  299. .NextDevice     tst.b   (a1)                        ;  Liste der Geräte mit besonderen
  300.                 beq     .CopyRest                   ;  Eigenschaften (Flags)?
  301.                 move.l  a2,a0
  302. .CmpNextChar    move.b  (a1)+,d0
  303.                 beq     .FoundIt
  304.                 cmp.b   (a0)+,d0
  305.                 beq     .CmpNextChar
  306. .SkipRest2      tst.b   (a1)+
  307.                 bne     .SkipRest2
  308.                 addq.l  #1,a1
  309.                 bra     .NextDevice
  310.  
  311. .FoundIt        move.b  (a1),d0
  312.                 or.b    d0,cdx_Flags(a3)            ; cdx_Flag dazusetzen (SPECIALF_)
  313.  
  314. .CopyRest       adda.l  #8,a2                       ; nächte Bytes der Inquirydaten
  315.                 lea     cdx_Product(a3),a0          ; Product Identification
  316.                 moveq   #3,d0                       ; 16 Bytes (4 Langworte)
  317. .l1             move.l  (a2)+,(a0)+                 ;  kopieren
  318.                 dbra    d0,.l1
  319.                 lea     cdx_Revision(a3),a0         ; Product Revision Level
  320.                 move.l  (a2)+,(a0)                  ; nur 4 Bytes groß
  321.                 lea     cdx_Specific(a3),a0         ; Vendor Specific
  322.                 moveq   #4,d0                       ; 20 Bytes (4 Langworte)
  323. .l2             move.l  (a2)+,(a0)+                 ;  kopieren
  324.                 dbra    d0,.l2
  325.  
  326. .GetValidAPage  move.l  a3,a0
  327.                 lea     CMDModeSense(pc),a1         ; veränderbare Felder der AudioPage herausbekommen
  328.                 moveq   #6,d0
  329.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  330.                 bsr     CDDoSCSIA                       ; Kommando ausführen
  331.                 bne     .FillCDx
  332.                 move.l  cdx_SCSIData(a3),a1
  333.                 tst.b   (a1)                        ; Mode Data Length = 0
  334.                 beq     .FillCDx
  335.                 addq.l  #3,a1                       ; ans Ende des Mode Parameter Header springen
  336.                 move.b  (a1),d0                     ; Block Descriptor Length
  337. .sl             addq.l  #1,a1                       ; Block Descriptor(s) überspringen
  338.                 dbra    d0,.sl
  339.                 moveq   #0,d0
  340.                 move.b  (a1)+,d0
  341.                 andi.b  #%00111111,d0               ; Page Code ohne Bit 6-7
  342.                 cmp.b   #$e,d0                      ; wirklich Audio Control Page?
  343.                 bne     .FillCDx
  344.                 addq.l  #1,a1
  345.                 moveq   #AUDIOPAGE_SIZEOF-1,d0
  346.                 move.l  cdx_ValidAudioPage(a3),a0
  347. .cl             move.b  (a1)+,(a0)+                 ; Changeable Values der AudioPage kopieren
  348.                 dbra    d0,.cl
  349.  
  350. .FillCDx        move.b  #AUDIO_STAT_NO_DISK,cdx_AudioStatus(a3)
  351.                 move.b  #AUDIO_STAT_NO_DISK,cdx_Active(a3)
  352.                 move.l  a3,a0
  353.                 bsr     _CDUpdateA                  ; Rest der CDx-Struktur füllen
  354.                 bsr     _CDReadTOCA                 ; TOC einlesen
  355.                 moveq   #GET_VOLUME,d0
  356.                 bsr     _CDVolumeA
  357.  
  358. .EXIT           movem.l (sp)+,d1-d7/a0-a6
  359.                 rts
  360.  
  361. CMDInquiry:     dc.b    $12, 0, 0, 0, 96, 0
  362. CMDModeSense:   dc.b    $1a, 8, $4e, 0, 254, 0
  363.  
  364. *///
  365. *////*---------------- CDChangeDeviceA() --------------------------<c>-*/
  366. */
  367. *  BOOL CDChangeDeviceA(register __a0 struct CDxRequest, register __d0 ULONG unitnumber, register __d1 ULONG lun);
  368. *
  369. * Versucht das per CDOpenDeviceA() geöffnete Device unter einer anderen Unit
  370. * oder LUN neu zu öffnen und schließt gegebenenfalls das alte. Konnte das
  371. * neue Device geöffnet werden, oder sind Unit/LUN mit dem alten Device
  372. * identisch, wird TRUE zurückgegeben, andernfalls FALSE.
  373. *
  374.   CNOP 0,2
  375.   xdef _CDChangeDeviceA
  376. _CDChangeDeviceA: movem.l d1-d3/a0-a3/a6,-(sp)
  377.                 moveq   #TRUE,d7                    ; return-vorgabe
  378.                 cmp.b   cdx_Unit(a0),d0             ; Unit und LUN gleich?
  379.                 bne     .Begin
  380.                 cmp.b   cdx_LUN(a0),d1
  381.                 beq     .EXIT
  382.  
  383. .Begin          move.l  a0,a3                       ; a3=cdx
  384.                 moveq   #FALSE,d7                   ; return-vorgabe
  385.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a3)   ; mit cd.device klappt das nicht
  386.                 bne     .EXIT
  387.                 move.l  d0,d2                       ; d2 = unitnumber
  388.                 move.l  d1,d3                       ; d3 = lun
  389.  
  390. .CreateNewReq   move.l  cdx_SysBase(a3),a6
  391.                 movea.l cdx_IOPort(a3),a0
  392.                 moveq   #IOSTD_SIZE,d0
  393.                 jsr     _LVOCreateIORequest(a6)     ; neuen IORequest für
  394.                 tst.l   d0                          ;  'neues' Device...
  395.                 beq     .EXIT
  396.                 move.l  d0,a2                       ; a2 = neuer IORequest
  397.  
  398. .OpenNewDevice  move.l  d3,d0
  399.                 mulu.w  #10,d0
  400.                 add.l   d2,d0                       ; unit + lun*10
  401.                 moveq   #0,d1                       ; Flags
  402.                 move.l  cdx_DeviceName(a3),a0       ; devicename
  403.                 move.l  a2,a1                       ; neuer io_request
  404.                 jsr     _LVOOpenDevice(a6)
  405.                 tst.l   d0
  406.                 beq     .Ok                         ; 0 => erfolgreich
  407.                  move.l  a2,a0
  408.                  jsr     _LVODeleteIORequest(a6)
  409.                  bra     .EXIT
  410.  
  411. .Ok             move.l  cdx_IORequest(a3),a1        ; 'altes' Device schließen
  412.                 jsr     _LVOCloseDevice(a6)
  413.                 move.l  cdx_IORequest(a3),a0        ; alten IORequest löschen
  414.                 jsr     _LVODeleteIORequest(a6)
  415.                 move.l  a2,cdx_IORequest(a3)        ; neuen IORequest eintragen
  416.                 move.b  d2,cdx_Unit(a3)
  417.                 move.b  d3,cdx_LUN(a3)
  418.                 move.l  a3,a0
  419.                 bsr     _CDInitDeviceA              ; CDx neu ausfüllen/initialisieren
  420.                 moveq   #TRUE,d7
  421.  
  422. .EXIT           move.l  d7,d0
  423.                 movem.l (sp)+,d1-d3/a0-a3/a6
  424.                 rts
  425.  
  426. *///
  427. *////*---------------- CDCloseDeviceA() ---------------------------<c>-*/
  428. *
  429. *  BOOL CDCloseDeviceA(register __a0 struct CDxRequest);
  430. *
  431. * Schließt ein SCSI-Device, das mit CDOpenDeviceA() geöffnet wurde. Das
  432. * Ergebnis ist dabei immer NULL.
  433. *
  434.   CNOP 0,2
  435.   xdef _CDCloseDeviceA
  436. _CDCloseDeviceA: movem.l d1-d7/a0-a6,-(sp)
  437.                 move.l  a0,a3                       ; a3 = MyCDxReq
  438.                 move.l  cdx_SysBase(a3),a6
  439.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a3)
  440.                 beq     CloseCmdStruct
  441.                 bsr     _CDStopA                    ; muß leider sein, da kein laufender
  442.                                                     ; IORequest gefreed werden kann
  443. Close2ndDevice  move.l  cdx_CDPlayIORequest(a3),a1
  444.                 jsr     _LVOCloseDevice(a6)
  445.  
  446. Delete2ndIOReq  move.l  cdx_CDPlayIORequest(a3),a0
  447.                 jsr     _LVODeleteIORequest(a6)
  448.  
  449. Delete2ndPort   move.l  cdx_CDPlayIOPort(a3),a0
  450.                 jsr     _LVODeleteMsgPort(a6)
  451.  
  452. CloseCmdStruct  move.l  cdx_Cmd(a3),a1
  453.                 jsr     _LVOFreeVec(a6)
  454.  
  455. CloseDevice     move.l  cdx_IORequest(a3),a1
  456.                 jsr     _LVOCloseDevice(a6)
  457.  
  458. DeleteIOReq     move.l  cdx_IORequest(a3),a0
  459.                 jsr     _LVODeleteIORequest(a6)
  460.  
  461. DeleteIOPort    move.l  cdx_IOPort(a3),a0
  462.                 jsr     _LVODeleteMsgPort(a6)
  463.  
  464. FreeBufMem      move.l  cdx_SCSIDataStart(a3),a1
  465.                 jsr     _LVOFreeVec(a6)
  466.  
  467. FreeCDxStruct   move.l  cdx_MemStart(a3),a1
  468.                 jsr     _LVOFreeVec(a6)
  469.  
  470. .EXIT           moveq   #NULL,d0
  471.                 movem.l (sp)+,d1-d7/a0-a6
  472.                 rts
  473.  
  474. *///
  475.  
  476. *////*---------------- CDDoSCSIA() --------------------------------<c>-*/
  477. *
  478. *  LONG CDDoSCSIA(register __a0 struct CDxRequest *cdx, a1 APTR Command, d0 cmdlen. d1 flags);
  479. *
  480. * Führt das übergebene SCSI Direktkommando aus und gibt im Fehlerfall einen
  481. * Wert zurück, der sich aus dem Sense Key (Bit 16-31), dem Additional Sense
  482. * Code (Bit 8-15) und dem Additional Sense Code Qualifier (Bit 0-7)
  483. * zusammensetzt. Ist keine CD eingelegt werden alle CD-abhängigen Daten auf
  484. * 0 gesetzt. Die Conditioncodes werden in Abhängigkeit von d0 gesetzt.
  485. *
  486.   CNOP 0,2
  487. CDDoSCSIA:      movem.l d1/a0-a2/a6,-(sp)
  488.                 move.l  cdx_SysBase(a0),a6      ; SysBase...
  489.                 move.l  cdx_Cmd(a0),a2          ; a2=SCSICmd
  490.  
  491.                 move.l  cdx_SCSIData(a0),(a2)+  ; scsi_Data;
  492.                 move.l  #DATA_LEN,(a2)+         ; scsi_Length;
  493.                 clr.l   (a2)+                   ; scsi_Actual;
  494.                 move.l  a1,(a2)+                ; scsi_Command;
  495.                 move.w  d0,(a2)+                ; scsi_CmdLength;
  496.                 clr.w   (a2)+                   ; scsi_CmdActual;
  497.                 move.b  d1,(a2)+                ; scsi_Flags;
  498.                 clr.b   (a2)+                   ; scsi_Status;
  499.                 move.l  cdx_SCSISense(a0),(a2)+ ; scsi_SenseData;
  500.                 move.w  #SENSE_LEN,(a2)+        ; scsi_SenseLength;
  501.                 clr.w   (a2)                    ; scsi_SenseActual;
  502.  
  503.                 move.l  a0,a2                   ; CDxRequest retten
  504.  
  505.                 move.l  cdx_IORequest(a0),a1
  506.                 jsr     _LVODoIO(a6)            ; Kommando schicken und warten
  507.  
  508.                 moveq   #0,d0                   ; Return = 0
  509.                 move.l  cdx_Cmd(a2),a1          ; a1=SCSICmd
  510.                 tst.w   scsi_SenseActual(a1)    ; Sense Daten da?
  511.                 beq     .EXIT
  512.  
  513.                 move.l  scsi_SenseData(a1),a1   ; a1=SenseData
  514.                 move.b  (a1),d1                 ; Error Code ($70 oder $71)
  515.                 andi.b  #%01111111,d1           ; Valid-Bit auslassen
  516.                 cmp.b   #$70,d1                 ; Current Error ($70) ?
  517.                 bne     .EXIT
  518.                 move.b  2(a1),d0                ; d0=Sense Key
  519.                 andi.b  #%00001111,d0           ; nur 4 Bits
  520.                 tst.b   d0                      ; NO SENSE ($0) ?
  521.                 beq     .EXIT
  522.                 move.b  d0,cdx_SenseKey(a2)     ; in cdx schreiben (für ARexx)
  523.                 swap.w  d0                      ; Returnwert berechnen
  524.                 cmp.b   #6,7(a1)                ; ASC und ASCQ vorhanden?
  525.                 blt     .EXIT
  526.                 move.w  12(a1),d0               ; ASC und ASCQ zu Returnwert
  527.                 move.w  d0,cdx_AddSenseCode(a2) ; in cdx schreiben
  528.  
  529. .EXIT           movem.l (sp)+,d1/a0-a2/a6
  530.                 tst.l   d0                      ; Conditioncodes setzen
  531.                 rts
  532.  
  533. *///
  534. *////*---------------- DoATAPI() ----------------------------------<n>-*/
  535. *
  536. *  LONG DoATAPI(register __a0 struct IORequest *ioreq);
  537. *
  538. * Ergebnis ist io_Error.
  539. *
  540.   CNOP 0,2
  541. DoATAPI:        movem.l d1/a0-a1/a6,-(sp)
  542.                 move.l  4.w,a6
  543.                 move.l  a0,a1
  544.                 jsr     _LVODoIO(a6)
  545. .EXIT           movem.l (sp)+,d1/a0-a1/a6
  546.                 tst.l   d0
  547.                 rts
  548.  
  549. *///
  550.  
  551. *////*---------------- CDUpdateA() --------------------------------<c>-*/
  552. *
  553. *  LONG CDUpdateA(register __a0 struct CDxRequest *cdx);
  554. *
  555. * Aktualisiert den Track, Index, Adresssen und Audiostatus der übergebenen
  556. * CDx-Struktur. Tritt ein Fehler auf, liefert die Funktion einen Wert in
  557. * dem in CDDoSCSIA beschriebenem Format zurück. Wurde seit dem letztem
  558. * Update eine CD eingelegt, wird der TOC neu eingelesen. Entspricht der
  559. * aktuelle Audiostatus AUDIO_STAT_NO_DISK, AUDIO_STAT_NOT_VALID oder
  560. * AUDIO_STAT_NONE, werden die Positionsfelder der CDx-Struktur auf 0 gesetzt.
  561. *
  562.   CNOP 0,2
  563.   xdef _CDUpdateA
  564. _CDUpdateA:     movem.l d1-d3/a0-a2,-(sp)
  565.  
  566.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  567.                 bne     .ATAPI
  568.  
  569.                 moveq   #10,d0                  ; 10 Byte Kommando
  570.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  571.                 lea     CMDReadSubCh(pc),a1
  572.                 bsr     CDDoSCSIA               ; Kommando ausführen
  573.                 beq     .DiscChanged            ; alles korrekt
  574.  
  575.                 cmp.l   #$023A00,d0             ; NO MEDIA PRESENT ?
  576.                 bne     .EXIT                   ; unerwarteter Fehler
  577.  
  578. .NoCD           move.b  #AUDIO_STAT_NO_DISK,cdx_AudioStatus(a0)
  579.                 move.b  #AUDIO_STAT_NO_DISK,cdx_Active(a0)
  580.  
  581.                 move.l  cdx_TOCData(a0),a1      ; keine CD eingelegt: alles
  582.                 moveq   #3,d1                   ;  CD spezifische löschen
  583. .loop           clr.l   (a1)+                   ; FirstTrack/LastTrack/FirstAudioTrack/reserved3/FirstAudioAddress
  584.                 dbra    d1,.loop                ; und Entry[0] löschen
  585.  
  586. .Stopped        clr.w   (a0)+                   ; cdx_CurrentTrack/CurrentIndex
  587.                 moveq   #4,d1
  588. .loop2          clr.l   (a0)+                   ; cdx_CurrentAbsAddr/CurrentRelAddr/RelRemainBlocks/AbsRemainBlocks/TrackLength
  589.                 dbra    d1,.loop2
  590.                 bra     .EXIT
  591.  
  592. .SetSimpleStat  move.b  #AUDIO_STAT_STOPPED,cdx_Active(a0)
  593.                 bra     .Stopped
  594.  
  595. .DiscChanged    cmp.b   #AUDIO_STAT_NO_DISK,cdx_AudioStatus(a0)    ; neue Disk eingelegt?
  596.                 bne     .UpdateCDx
  597.                 bsr     _CDReadTOCA             ; ja: Table Of Contents neu einlesen
  598.                 moveq   #10,d0                  ; Subchannel muß nochmal gelesen werden, da die SCSI-Daten
  599.                 bsr     CDDoSCSIA               ; mit dem TOC überschrieben sind
  600.                 bne     .EXIT
  601.  
  602. .UpdateCDx      move.l  cdx_SCSIData(a0),a1
  603.                 addq.l  #1,a1
  604.                 move.b  (a1)+,d0                ; Audio Status lesen
  605.                 move.b  d0,cdx_AudioStatus(a0)  ;  und setzen
  606.                 move.b  d0,cdx_Active(a0)
  607.                 beq     .SetSimpleStat          ; AUDIO_STAT_NOT_VALID (=0)
  608.                 cmp.b   #AUDIO_STAT_COMPLETED,d0
  609.                 bcc     .SetSimpleStat          ; d0 >= AUDIO_STAT_COMPLETED (STOPPED/NONE)
  610.                                                 ; übrig bleibt PLAY und PAUSE: Positionen lesen
  611.                 tst.w   (a1)                    ; subchannel data length = 0 ?
  612.                 beq     .EXIT
  613.  
  614.                 addq.l  #4,a1
  615.                 move.l  cdx_TOCData(a0),a2      ; TOC-Struktur sichern
  616.                 move.b  (a1),d0                 ; d0=CurrentTrack
  617.                 move.w  (a1)+,(a0)+             ; cdx_CurrentTrack+CurrentIndex
  618.                 move.l  (a1)+,d2                ; d2=cdx_CurrentAbsAddr
  619.                 move.l  d2,(a0)+
  620.                 move.l  (a1),(a0)+              ; cdx_CurrentRelAddr
  621.  
  622. .calcrest       addq.l  #Entry,a2
  623.                 move.l  Address(a2),d1          ; Startaddresse der lead-out area
  624.                 ext.w   d0
  625.                 mulu.w  #CDTOCEntry_SIZEOF,d0
  626.                 adda.l  d0,a2
  627.                 move.l  Address(a2),d0          ; d0=Entry[CurTrack].Address
  628.                 addq.l  #CDTOCEntry_SIZEOF,a2   ; nachfolgender Track
  629.  
  630.                 move.l  Address(a2),d3          ; d3=Entry[CurTrack+1].Address
  631.                 move.l  d3,a1
  632.                 sub.l   d0,d3                   ; d3 = d3-d0
  633.                 move.l  a1,d0
  634.                 sub.l   d2,d0
  635.                 move.l  d0,(a0)+                ; cdx_RelRemainBlocks
  636.                 sub.l   d2,d1
  637.                 move.l  d1,(a0)+                ; cdx_AbsRemainBlocks
  638.                 move.l  d3,(a0)                 ; cdx_TrackLength
  639.                 moveq   #0,d0                   ; alles okay: RETURN_OK
  640.  
  641. .EXIT           movem.l (sp)+,d1-d3/a0-a2
  642.                 rts
  643.  
  644. .ATAPI          bsr     _CDTestUnitA
  645.                 tst.l   d0
  646.                 bne     .NoCD
  647.  
  648.                 move.l  a0,a2                   ; a2 = cdx
  649.                 cmp.b   #AUDIO_STAT_NO_DISK,cdx_AudioStatus(a2)    ; neue Disk eingelegt?
  650.                 bne     .goon
  651.                 bsr     _CDReadTOCA             ; ja: Table Of Contents neu einlesen
  652.  
  653. .goon           move.l  cdx_IORequest(a2),a0    ; a0 = IORequest
  654.                 move.w  #CD_INFO,IO_COMMAND(a0)
  655.                 move.l  cdx_Info(a2),a1         ; a1 = CDInfo
  656.                 move.l  a1,IO_DATA(a0)
  657.                 move.l  #CDINFO_SIZE,IO_LENGTH(a0)
  658.                 bsr     DoATAPI
  659.                 bne     .EXIT                           ; Fehler?
  660.  
  661. .ok             btst.w  #CDSTSB_PLAYING,CDINFO_Status(a1)
  662.                 beq     .stp
  663.                 move.b  #AUDIO_STAT_PLAYING,cdx_AudioStatus(a2)
  664.                 move.b  #AUDIO_STAT_PLAYING,cdx_Active(a2)
  665.                 move.w  #CD_QCODELSN,IO_COMMAND(a0)
  666.                 move.l  cdx_QCode(a2),a1                ; a1 = QCode
  667.                 move.l  a1,IO_DATA(a0)
  668.                 clr.l   IO_LENGTH(a0)
  669.                 bsr     DoATAPI                         ; Wenn die CD gestoppt ist, gibt CDQCODELSN
  670.                 bne     .stp                            ; einen Fehlercode zurück und die Werte
  671.                                                         ; müssen von Hand auf 0 gesetzt werden
  672.                 move.l  cdx_TOCData(a2),a0
  673.                 move.w  QCODE_Track(a1),(a2)+           ; cdx_CurrentTrack+CurrentIndex
  674.                 move.l  QCODE_DiskPosition(a1),d2
  675.                 move.l  d2,(a2)+                        ; d2=cdx_CurrentAbsAddr
  676.                 move.l  QCODE_TrackPosition(a1),(a2)+   ; cdx_CurrentRelAddr
  677.                 move.b  QCODE_Track(a1),d0              ; d0=CurrentTrack
  678.                 exg.l   a0,a2                           ; a2=TOCData, a0=*cdx_RelRemainBlocks
  679.                 bra     .calcrest
  680.  
  681. .stp            move.b  #AUDIO_STAT_STOPPED,cdx_AudioStatus(a2)
  682.                 move.b  #AUDIO_STAT_STOPPED,cdx_Active(a2)
  683.                 clr.w   (a2)+
  684.                 clr.l   (a2)+
  685.                 clr.l   (a2)+
  686.                 moveq   #0,d0
  687.                 bra     .EXIT
  688.  
  689. CMDReadSubCh:   dc.b    $42, 0, $40, 1, 0, 0, 0, 0, 16, 0
  690.  
  691. *///
  692. *////*---------------- CDReadTOCA() -------------------------------<c>-*/
  693. *
  694. *  LONG CDReadTOCA(register __a0 struct CDxRequest *cdx);
  695. *
  696.   CNOP 0,2
  697.   xdef _CDReadTOCA
  698. _CDReadTOCA:    movem.l d1-d3/a0-a3/a6,-(sp)
  699.  
  700.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  701.                 bne     .ATAPI
  702.  
  703.                 moveq   #10,d0
  704.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  705.                 lea     CMDReadTOC(pc),a1
  706.                 bsr     CDDoSCSIA
  707.                 bne     .EXIT
  708.  
  709.                 move.l  cdx_SCSIData(a0),a1
  710.                 move.w  (a1)+,d0                ; TOC Data Length
  711.                 beq     .EXIT
  712.  
  713.                 move.l  cdx_SysBase(a0),a6      ; für später...
  714.                 move.l  cdx_TOCData(a0),a0      ; da soll alles hin
  715.                 move.w  (a1),(a0)+              ; First und Last Track
  716.  
  717.                 move.b  (a1)+,d1                ; FirstTrack
  718.                 moveq   #0,d3
  719.                 move.b  (a1)+,d3                ; LastTrack
  720.                 sub.b   d1,d3
  721.                 addq.b  #1,d3                   ; d3=Track Anzahl
  722.  
  723.                 move.l  a0,a3                   ; für später
  724.                 clr.w   (a0)+                   ; wird nachher gefüllt...
  725.                 clr.l   (a0)+                   ; (TrackNum/FirstAudioTrack/FirstAudioAddress)
  726.  
  727.                 move.l  a0,a2                   ; Track 0 gibt's nicht, da kommt
  728.                 clr.l   (a0)+                   ;  nacher die logical block
  729.                 clr.l   (a0)+                   ;  address der lead-out area rein
  730.  
  731.                 clr.l   16(a0)                  ; Entry für Track 3 sicherheitshalber löschen
  732.                 clr.l   20(a0)                  ; (für ID Berechnung :-)
  733.  
  734.                 lsr.l   #3,d0                   ; durch 8 (pro Entry 8 bytes)
  735.                                                 ; die 2 bytes für F. und L. Track fallen raus
  736.                 subq.l  #1,d0                   ; dbra endet bei -1
  737.                 bmi     .Good                   ; jetzt schon -1?
  738.  
  739.                 moveq   #-1,d2                  ; noch keinen AudioTrack gefunden...
  740.  
  741. .loop           move.l  (a1)+,d1
  742.                 move.l  d1,(a0)+                ; ersten 4 bytes (8 bytes / Entry) kopieren
  743.                 tst.b   d2                      ; schon AudioTrack gefunden?
  744.                 beq     .loopend
  745.                 btst.l  #18,d1                  ; AudioTrack?
  746.                 bne     .loopend
  747.                 move.b  d3,(a3)+                ; TrackNum
  748.                 lsr.w   #8,d1
  749.                 move.b  d1,(a3)+                ; FirstAudioTrack
  750.                 move.l  (a1),-4(a2)             ; FirstAudioAddress
  751.                 moveq   #0,d2
  752. .loopend        move.l  (a1)+,(a0)+             ; letzten 4 Bytes kopieren
  753.                 dbra    d0,.loop
  754.  
  755. .LeadOut        subq.l  #8,a1                   ; letzten Entry nochmal holen
  756.                 cmp.b   #$AA,2(a1)              ; wirklich lead out area? (Tracknr. $AA)
  757.                 beq     .IsLeadOut
  758.                 cmp.b   #$A2,2(a1)              ; für NEC-500 u.ä. Laufwerke (Tracknr. $A2)
  759.                 bne     .Good
  760. .IsLeadOut      move.l  (a1)+,(a2)+             ; ja: nochmal nach Entry[0] kopieren
  761.                 move.l  (a1),(a2)
  762.  
  763. .CalcID         lea     IDFormat(pc),a0
  764.                 move.l  (a2),-(a7)              ; Entry[0].Address
  765.                 move.l  24(a2),-(a7)            ; Entry[3].Address
  766.                 move.l  d3,-(a7)                ; LastTrack-FirstTrack+1
  767.                 move.l  a7,a1
  768.                 lea     CDID-(Entry+4)(a2),a3
  769.                 lea     PutChProc(pc),a2
  770.                 jsr     _LVORawDoFmt(a6)        ; ID formatieren
  771.                 lea     12(a7),a7
  772.  
  773. .Good           moveq   #0,d0
  774.  
  775. .EXIT           movem.l (sp)+,d1-d3/a0-a3/a6
  776.                 rts
  777.  
  778. .ATAPI          move.l  a0,a2                           ; a2 = CDx
  779.                 move.l  cdx_IORequest(a2),a0            ; a0 = IORequest
  780.                 move.w  #CD_TOCLSN,IO_COMMAND(a0)
  781.                 move.l  cdx_SCSIData(a2),IO_DATA(a0)
  782.                 move.l  #100,IO_LENGTH(a0)
  783.                 clr.l   IO_OFFSET(a0)
  784.                 bsr     DoATAPI
  785.                 beq     .Ok
  786.                  move.l  #$00090000,d0                   ; Vendor specific Error
  787.                  bra     .EXIT
  788.  
  789. .Ok             move.l  cdx_SysBase(a2),a6
  790.                 move.l  cdx_SCSIData(a2),a1
  791.                 move.l  cdx_TOCData(a2),a0
  792.                 move.w  (a1),(a0)+              ; First- und LastTrack
  793.  
  794.                 moveq   #0,d1
  795.                 move.b  (a1)+,d1                ; FirstTrack
  796.                 moveq   #0,d3
  797.                 move.b  (a1)+,d3                ; LastTrack
  798.                 sub.b   d1,d3
  799.                 addq.b  #1,d3                   ; d3=Track Anzahl
  800.                 move.b  d3,(a0)+
  801.  
  802.                 move.l  a0,a3                   ; für später
  803.                 clr.b   (a0)+                   ; FirstAudioTrack
  804.                 clr.l   (a0)+                   ; FirstAudioAddress
  805.  
  806.                 clr.w   (a0)+                   ; Entry[0].reserved1/CtlAdr
  807.                 move.w  #$AA00,(a0)+            ; Entry[0].Track/reserved2
  808.                 move.l  a0,a2
  809.                 move.l  (a1)+,(a0)+             ; Entry[0].Address (LeadOut)
  810.  
  811.                 move.l  d3,d0
  812.                 subq.l  #1,d0
  813.                 moveq   #-1,d2                  ; noch keinen AudioTrack gefunden
  814.  
  815. .aloop          move.b  (a1)+,d1                ; CtlAdr
  816.                 move.w  d1,(a0)+                ; Entry->reserved1/CtlAdr
  817.                 tst.b   d2                      ; ersten Audiotrack schon gefunden?
  818.                 beq     .aloopend               ; ja  -->
  819.                 btst    #2,d1                   ; ist Track ein Audiotrack?
  820.                 bne     .aloopend               ; nein -->
  821.                 move.b  (a1),(a3)+              ; FirstAudioTrack
  822.                 move.l  1(a1),(a3)              ; FirstAudioAddress
  823.                 moveq   #0,d2                   ; ersten Audiotrack gefunden
  824. .aloopend       move.b  (a1)+,(a0)+             ; Entry->Track
  825.                 clr.b   (a0)+                   ; Entry->reserved2
  826.                 move.l  (a1)+,(a0)+             ; Entry->Address
  827.                 dbra    d0,.aloop
  828.  
  829.                 clr.w   (a0)+                   ; Entry[TrackNum+1].reserved1/CtlAdr
  830.                 move.w  #$AA00,(a0)+            ; Entry[TrackNum+1].Track/reserved2
  831.                 move.l  (a2),(a0)               ; Entry[TrackNum+1].Address (LeadOut)
  832.                 bra     .CalcID
  833.  
  834. CMDReadTOC: dc.b $43, 0, 0, 0, 0, 0, 0, $3, $24, 0
  835. IDFormat:   dc.b '%02ld%06lx%06lx',0
  836.  
  837. *///
  838. *////*---------------- CDTestUnitA() ------------------------------<c>-*/
  839. *
  840. *  LONG CDTestUnitA(register __a0 struct CDxRequest *cdx);
  841. *
  842. * Testet die logical unit ob sie bereit ist. Wenn sie das ist, wird GOOD (0)
  843. * zurückgegeben, ansonsten ein Returncode im in CDDoSCSIA beschriebenem
  844. * Format.
  845. *
  846.   CNOP 0,2
  847.   xdef _CDTestUnitA
  848. _CDTestUnitA:   movem.l d1/a0-a1,-(sp)
  849.  
  850.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  851.                 bne     .ATAPI
  852.  
  853.                 moveq   #6,d0                           ; 6 Byte Kommando
  854.                 moveq   #SCSIF_AUTOSENSE,d1             ; REQUEST SENSE ausführen lassen
  855.                 lea     CMDReadSubCh(pc),a1
  856.                 bsr     CDDoSCSIA                       ; Kommando ausführen
  857.  
  858. .EXIT           movem.l (sp)+,d1/a0-a1
  859.                 rts
  860.  
  861. .ATAPI          move.l  cdx_IORequest(a0),a0
  862.                 move.w  #CD_CHANGESTATE,IO_COMMAND(a0)
  863.                 bsr     DoATAPI
  864.                 beq     .Ok
  865.                 move.l  #$00090000,d0                   ; Vendor specific Error
  866.                 bra     .EXIT
  867.  
  868. .Ok             move.l  IO_ACTUAL(a0),d0                ; gültige CD eingelegt?
  869.                 beq     .EXIT                           ; ja: return(0)
  870.                 move.l  #$0023A00,d0                    ; MEDIUM NOT PRESENT
  871.                 bra     .EXIT
  872.  
  873. CMDTestUnitReady:   dc.b    $0, 0, 0, 0, 0, 0
  874.  
  875. *///
  876.  
  877. *////*---------------- CDEjectA() ---------------------------------<c>-*/
  878. *
  879. *  LONG CDEjectA(register __a0 struct CDxRequest *cdx, register __d0 BOOL really);
  880. *
  881. * TRUE öffnet die Schublade, FALSE schließt sie. Im Fehlerfall (Schublade
  882. * schon offen/geschlossen) wird ein Fehlercode zurückgegeben. Außerdem
  883. * wird das Flag JUSTCLOSED gesetzt, wenn die Schublade geschlossen werden
  884. * soll, sonst wird es gelöscht.
  885. *
  886.   CNOP 0,2
  887.   xdef _CDEjectA
  888. _CDEjectA:      movem.l d1/a0-a1,-(sp)
  889.  
  890.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  891.                 bne     .ATAPI
  892.  
  893.                 lea     CMDStartStopUnit(pc),a1
  894.  
  895. .eject          moveq   #2,d1
  896.                 and.b   #~SPECIALF_JUSTCLOSED,cdx_Flags(a0)
  897.                 tst.l   d0
  898.                 bne     .goon
  899. .load           moveq   #3,d1
  900.                 or.b    #SPECIALF_JUSTCLOSED,cdx_Flags(a0)
  901. .goon           move.b  d1,4(a1)
  902.                 moveq   #6,d0
  903.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  904.                 bsr     CDDoSCSIA
  905.  
  906. .EXIT           movem.l (sp)+,d1/a0-a1
  907.                 rts
  908.  
  909. .ATAPI          and.b   #~SPECIALF_JUSTCLOSED,cdx_Flags(a0)
  910.                 moveq   #1,d1
  911.                 tst.l   d0
  912.                 bne     .goon2
  913.                 or.b    #SPECIALF_JUSTCLOSED,cdx_Flags(a0)
  914.                 moveq   #0,d1
  915. .goon2          move.l  cdx_IORequest(a0),a0
  916.                 lea     IO_ACTUAL(a0),a1
  917.                 clr.l   (a1)+                       ; IO_ACTUAL
  918.                 move.l  d1,(a1)+                    ; IO_LENGTH
  919.                 clr.l   (a1)+                       ; IO_DATA
  920.                 clr.l   (a1)                        ; IO_OFFSET
  921.                 move.w  #CD_EJECT,IO_COMMAND(a0)
  922.                 bsr     DoATAPI
  923.                 move.l  IO_ACTUAL(a0),d0
  924.                 bra     .EXIT
  925.  
  926. CMDStartStopUnit:   dc.b $1B, 1, 0, 0, 2, 0
  927.  
  928. *///
  929. *////*---------------- CDLockA() ----------------------------------<c>-*/
  930. *
  931. *  LONG CDLockA(register __a0 struct CDxRequest *cdx, register __d0 BOOL really);
  932. *
  933. * TRUE verbietet das Öffnen der Schublade am Gerät. In diesem Falle wird
  934. * das Flag SPECIALF_LOCKED gesetzt.
  935. *
  936.   CNOP 0,2
  937.   xdef _CDLockA
  938. _CDLockA:       movem.l d1/a0-a1,-(sp)
  939.  
  940.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  941.                 bne     .EXIT
  942.  
  943.                 lea     CMDPreventAllow(pc),a1
  944.  
  945. .prevent        moveq   #1,d1
  946.                 or.b    #SPECIALF_LOCKED,cdx_Flags(a0)
  947.                 tst.l   d0
  948.                 bne     .goon
  949. .allow          moveq   #0,d1
  950.                 and.b   #~SPECIALF_LOCKED,cdx_Flags(a0)
  951. .goon           move.b  d1,4(a1)
  952.                 moveq   #6,d0
  953.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  954.                 bsr     CDDoSCSIA
  955.  
  956. .EXIT           movem.l (sp)+,d1/a0-a1
  957.                 rts
  958.  
  959. CMDPreventAllow:    dc.b    $1e, 0, 0, 0, 0, 0
  960.  
  961. *///
  962.  
  963. *////*---------------- CDPlayA() ----------------------------------<c>-*/
  964. *
  965. *  LONG CDPlayA(register __a0 struct CDxRequest *cdx, register __d0 UBYTE starttrack, register __d1 UBYTE startindex);
  966. *
  967.   CNOP 0,2
  968.   xdef _CDPlayA
  969. _CDPlayA:       movem.l d1/a0-a1,-(sp)
  970.  
  971.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  972.                 bne     .Playerjump
  973.  
  974.                 lea     CMDPlayTI(pc),a1
  975.                 move.b  #99,8(a1)
  976.                 btst.b  #SPECIALB_NEC,cdx_Flags(a0)     ; NEC Laufwerk vorhanden?
  977.                 beq     .PlayNormal                     ; Flag ist nicht gesetzt
  978.                 clr.b   8(a1)                           ; NEC braucht End-Index 0
  979.  
  980. .PlayNormal     move.b  d0,4(a1)                        ; Track
  981.                 move.b  d1,5(a1)                        ; Index
  982.  
  983.                 moveq   #10,d0
  984.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  985.                 bsr     CDDoSCSIA
  986.                 bra     .EXIT
  987.  
  988. .Playerjump     move.l  cdx_TOCData(a0),a1
  989.                 cmp.b   FirstTrack(a1),d0               ; Liegt Track im
  990.                 bcs     .error                          ;  gültigen Bereich
  991.                 cmp.b   LastTrack(a1),d0
  992.                 bhi     .error
  993.                 addq.l  #Entry,a1
  994.                 ext.w   d0
  995.                 mulu.w  #CDTOCEntry_SIZEOF,d0
  996.                 adda.l  d0,a1
  997.                 move.l  Address(a1),d0                  ; d0=Entry[Track].Address
  998.  
  999.                 moveq   #JUMPMODE_PLAY_ABS,d1
  1000.                 bsr     _CDJumpA                        ; Audio Play 12
  1001.                 bra     .EXIT
  1002.  
  1003. .error          move.l  #$052400,d0                     ; "ILLEGAL REQUEST: INVALID FIELD IN CDB" setzen
  1004.  
  1005. .EXIT           movem.l (sp)+,d1/a0-a1
  1006.                 rts
  1007.  
  1008. CMDPlayTI: dc.b $48, 0, 0, 0, 0, 0, 0, 99, 99, 0
  1009.  
  1010. *///
  1011. *////*---------------- CDPauseA() ---------------------------------<c>-*/
  1012. *
  1013. *  LONG CDPauseA(register __a0 struct CDxRequest *cdx, register __d0 BOOL really);
  1014. *
  1015. * TRUE läßt das Laufwerk anhalten, während FALSE den Pause-Modus wieder
  1016. * aufhebt.
  1017. *
  1018.   CNOP 0,2
  1019.   xdef _CDPauseA
  1020. _CDPauseA:      movem.l d1/a0-a1,-(sp)
  1021.  
  1022.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  1023.                 bne     .ATAPI
  1024.  
  1025.                 lea     CMDPauseResume(pc),a1
  1026.  
  1027. .pause          moveq   #0,d1
  1028.                 tst.l   d0
  1029.                 bne     .goon
  1030. .resume         moveq   #1,d1
  1031. .goon           move.b  d1,8(a1)
  1032.                 moveq   #10,d0
  1033.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  1034.                 bsr     CDDoSCSIA
  1035.  
  1036. .EXIT           movem.l (sp)+,d1/a0-a1
  1037.                 rts
  1038.  
  1039. .ATAPI          moveq   #1,d1
  1040.                 tst.l   d0
  1041.                 bne     .start
  1042.                 moveq   #0,d1
  1043. .start          move.l  cdx_IORequest(a0),a0
  1044.                 move.w  #CD_PAUSE,IO_COMMAND(a0)
  1045.                 clr.l   IO_DATA(a0)
  1046.                 move.l  d1,IO_LENGTH(a0)
  1047.                 clr.l   IO_OFFSET(a0)
  1048.                 bsr     DoATAPI
  1049.                 bra     .EXIT
  1050.  
  1051. CMDPauseResume: dc.b    $4b, 0, 0, 0, 0, 0, 0, 0, 0, 0
  1052.  
  1053. *///
  1054. *////*---------------- CDStopA() ----------------------------------<c>-*/
  1055. *
  1056. *  LONG CDStopA(register __a0 struct CDxRequest *cdx);
  1057. *
  1058.   CNOP 0,2
  1059.   xdef _CDStopA
  1060. _CDStopA:       movem.l d1/a0-a2/a6,-(sp)
  1061.  
  1062.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  1063.                 bne     .ATAPI
  1064.  
  1065.                 moveq   #6,d0
  1066.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  1067.                 lea     CMDStartStopUnit2(pc),a1
  1068.                 bsr     CDDoSCSIA
  1069.  
  1070. .EXIT           movem.l (sp)+,d1/a0-a2/a6
  1071.                 rts
  1072.  
  1073. .ATAPI          move.l  cdx_SysBase(a0),a6
  1074.                 move.l  cdx_CDPlayIORequest(a0),a2
  1075.                 move.l  a2,a1
  1076.                 jsr     _LVOCheckIO(a6)
  1077.                 tst.l   d0
  1078.                 bne     .ok
  1079.                 move.l  a2,a0
  1080.                 jsr     _LVOAbortIO(a6)
  1081.                 move.l  a2,a1
  1082.                 jsr     _LVOWaitIO(a6)
  1083. .ok             moveq   #0,d0
  1084.                 bra     .EXIT
  1085.  
  1086.  
  1087.  
  1088. CMDStartStopUnit2:  dc.b $1B, 0, 0, 0, 0, 0
  1089.  
  1090. *///
  1091.  
  1092. *////*---------------- CDPrevA() ----------------------------------<c>-*/
  1093. *
  1094. *  LONG CDPrevA(register __a0 struct CDxRequest *cdx);
  1095. *
  1096.   CNOP 0,2
  1097.   xdef _CDPrevA
  1098. _CDPrevA:       movem.l d1/a0-a1,-(sp)
  1099.  
  1100.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  1101.                 bne     .ATAPI
  1102.  
  1103.                 moveq   #10,d0                          ; 10 Byte Kommando
  1104.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  1105.                 lea     CMDReadSubCh(pc),a1
  1106.                 bsr     CDDoSCSIA                       ; Kommando ausführen
  1107.                 bne     .EXIT
  1108.  
  1109.                 move.l  cdx_SCSIData(a0),a1
  1110.                 move.b  6(a1),d0                        ; Current Track
  1111. .Start          subq.b  #1,d0
  1112.                 moveq   #1,d1                           ; Index
  1113.  
  1114.                 bsr     _CDPlayA
  1115.  
  1116. .EXIT           movem.l (sp)+,d1/a0-a1
  1117.                 rts
  1118.  
  1119. .ATAPI          bsr     _CDUpdateA
  1120.                 move.b  cdx_CurrentTrack(a0),d0
  1121.                 bra     .Start
  1122.  
  1123. *///
  1124. *////*---------------- CDNextA() ----------------------------------<c>-*/
  1125. *
  1126. *  LONG CDNextA(register __a0 struct CDxRequest *cdx);
  1127. *
  1128.   CNOP 0,2
  1129.   xdef _CDNextA
  1130. _CDNextA:       movem.l d1/a0-a1,-(sp)
  1131.  
  1132.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  1133.                 bne     .ATAPI
  1134.  
  1135.                 moveq   #10,d0                          ; 10 Byte Kommando
  1136.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  1137.                 lea     CMDReadSubCh(pc),a1
  1138.                 bsr     CDDoSCSIA                       ; Kommando ausführen
  1139.                 bne     .EXIT
  1140.  
  1141.                 move.l  cdx_SCSIData(a0),a1
  1142.                 move.b  6(a1),d0                        ; Current Track
  1143. .Start          addq.b  #1,d0
  1144.                 moveq   #1,d1                           ; Index
  1145.  
  1146.                 bsr     _CDPlayA
  1147.  
  1148. .EXIT           movem.l (sp)+,d1/a0-a1
  1149.                 rts
  1150.  
  1151. .ATAPI          bsr     _CDUpdateA
  1152.                 move.b  cdx_CurrentTrack(a0),d0
  1153.                 bra     .Start
  1154.  
  1155. *///
  1156. *////*---------------- CDJumpA() ----------------------------------<c>-*/
  1157. *
  1158. *  LONG CDJumpA(register __a0 struct CDxRequest *cdx, register __d0 LONG address, register __d1 BYTE jumpmode);
  1159. *
  1160.   CNOP 0,2
  1161.   xdef _CDJumpA
  1162. _CDJumpA:       movem.l d1-d3/a0-a2/a6,-(sp)
  1163.  
  1164.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  1165.                 bne     .ATAPI
  1166.  
  1167.                 move.b  d1,d3                           ; d3 = JumpMode
  1168.                 btst.b  #0,d3                           ; relativ?
  1169.                 beq     .absolute
  1170.  
  1171. .relative       move.l  d0,d2                           ; offset retten
  1172.  
  1173.                 moveq   #10,d0                          ; 10 Byte Kommando
  1174.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  1175.                 lea     CMDReadSubCh(pc),a1
  1176.                 bsr     CDDoSCSIA                       ; Kommando ausführen
  1177.                 bne     .EXIT
  1178.  
  1179.                 move.l  cdx_SCSIData(a0),a1
  1180.                 move.l  8(a1),d0                        ; d0=current logical block addr.
  1181.                 add.l   d2,d0                           ; neue Adresse berechnen
  1182.  
  1183. .absolute       btst.b  #1,d3                           ; play?
  1184.                 beq     .seek
  1185.  
  1186. .play           lea     CMDPlay12(pc),a1                ; Play(12) Command
  1187.                 moveq   #12,d1
  1188.                 bra     .start
  1189. .seek           lea     CMDSeek10(pc),a1                ; Seek(10) Command
  1190.                 moveq   #10,d1
  1191.  
  1192. .start          move.l  d0,2(a1)
  1193.                 move.l  d1,d0
  1194.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  1195.                 bsr     CDDoSCSIA
  1196.  
  1197. .EXIT           movem.l (sp)+,d1-d3/a0-a2/a6
  1198.                 rts
  1199.  
  1200. .ATAPI          move.l  d0,d2                           ; d2 = address
  1201.                 move.l  d1,d3                           ; d3 = Jumpmode
  1202.                 move.l  a0,a2                           ; a2 = cdx
  1203.                 move.l  cdx_SysBase(a2),a6
  1204.                 move.l  cdx_CDPlayIORequest(a2),a1
  1205.                 jsr     _LVOCheckIO(a6)
  1206.                 tst.l   d0                              ; läuft noch der Play IORequest
  1207.                 bne     .goon
  1208.                 bsr     _CDStopA                        ; ja: abbrechen
  1209.  
  1210. .goon           btst.b  #0,d3                           ; relativ?
  1211.                 beq     .aabsolute
  1212.                 move.l  cdx_IORequest(a2),a0
  1213.                 move.w  #CD_QCODELSN,IO_COMMAND(a0)
  1214.                 move.l  cdx_QCode(a2),a1                ; a1 = QCode
  1215.                 move.l  a1,IO_DATA(a0)
  1216.                 clr.l   IO_LENGTH(a0)
  1217.                 bsr     DoATAPI                         ; aktuelle Position bestimmen
  1218.                 bne     .aabsolute
  1219.                 move.l  QCODE_DiskPosition(a1),d0
  1220.                 add.l   d0,d2                           ; neue Adresse berechnen
  1221.  
  1222. .aabsolute      btst.b  #1,d3                           ; play?
  1223.                 beq     .aseek
  1224.  
  1225. .aplay          move.l  cdx_CDPlayIORequest(a2),a1
  1226.                 move.w  #CD_PLAYLSN,IO_COMMAND(a1)
  1227.                 clr.l   IO_DATA(a1)
  1228.                 move.l  cdx_TOCData(a2),a0
  1229.                 move.l  Entry+Address(a0),d0            ; Adresse der LeadOut-Area (Ende)
  1230.                 sub.l   d2,d0                           ; Länge=Ende-Start
  1231.                 move.l  d0,IO_LENGTH(a1)
  1232.                 move.l  d2,IO_OFFSET(a1)
  1233.                 jsr     _LVOSendIO(a6)
  1234.                 move.b  #AUDIO_STAT_PLAYING,cdx_Active(a2)
  1235.                 move.b  #AUDIO_STAT_PLAYING,cdx_AudioStatus(a2)
  1236.                 moveq   #0,d0
  1237.                 bra     .EXIT
  1238.  
  1239. .aseek          move.l  cdx_IORequest(a2),a0
  1240.                 move.w  #CD_SEEK,IO_COMMAND(a0)
  1241.                 move.l  d2,IO_OFFSET(a0)
  1242.                 bsr     DoATAPI
  1243.                 bra     .EXIT
  1244.  
  1245. CMDPlay12:  dc.b    $A5, 0, 0,0,0,0, 0,$4,$20,$00, 0, 0
  1246. CMDSeek10:  dc.b    $2B, 0, 0,0,0,0, 0, 0, 0, 0
  1247.  
  1248. *///
  1249.  
  1250. *////*---------------- CDFastModeA() ------------------------------< >-*/
  1251. *
  1252. *  LONG CDFastModeA(register __a0 struct CDxRequest *cdx, register __d0 UBYTE mode, register __d1 UBYTE vendor);
  1253. *
  1254.   CNOP 0,2
  1255.   xdef _CDFastModeA
  1256. _CDFastModeA:   movem.l d1/a0-a1,-(sp)
  1257.  
  1258.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  1259.                 bne     .EXIT
  1260.  
  1261.                 tst.b   d0
  1262.                 bne     .FAST
  1263.  
  1264.                 moveq   #0,d0
  1265.                 moveq   #JUMPMODE_PLAY_REL,d1
  1266.                 bsr     _CDJumpA
  1267.                 bra     .EXIT
  1268.  
  1269. .FAST           tst.b   d1
  1270.                 bne     .SONY
  1271.                 lea     CMDStartFP(pc),a1
  1272.                 move.b  #$3,1(a1)
  1273.                 cmp.b   #FASTMODE_FORWARD,d0
  1274.                 bne     .goon
  1275.                 move.b  #$2,1(a1)
  1276. .goon           moveq   #10,d0
  1277.                 moveq   #SCSIF_AUTOSENSE,d1
  1278.                 bsr     CDDoSCSIA
  1279.                 bra     .EXIT
  1280.  
  1281. .SONY           lea     CMDSonyStartFP(pc),a1
  1282.                 move.b  #$11,1(a1)
  1283.                 cmp.b   #FASTMODE_FORWARD,d0
  1284.                 bne     .goon2
  1285.                 move.b  #$1,1(a1)
  1286. .goon2          moveq   #12,d0
  1287.                 moveq   #SCSIF_AUTOSENSE,d1
  1288.                 bsr     CDDoSCSIA
  1289.  
  1290. .EXIT           movem.l (sp)+,d1/a0-a1
  1291.                 rts
  1292.  
  1293. CMDStartFP: dc.b $CA, 2, 0, 0, 0, 0, 0, 0, 0, $C0
  1294.  
  1295. CMDSonyStartFP: dc.b $DC, 1, 0,0,2,0, 0,0,0,0, 0, $40
  1296.  
  1297. *///
  1298. *////*---------------- CDVolumeA() --------------------------------<c>-*/
  1299. *
  1300. *  LONG CDVolumeA(register __a0 struct CDxRequest *cdx, register __d0 UBYTE mode);
  1301. *
  1302.   CNOP 0,2
  1303.   xdef _CDVolumeA
  1304. _CDVolumeA:     movem.l d1-d3/a0-a3,-(sp)
  1305.  
  1306.                 btst.b  #SPECIALB_ATAPI,cdx_Flags(a0)
  1307.                 bne     .ATAPI
  1308.  
  1309.                 move.b  d0,d2                       ; SET/GET-Flag sichern
  1310.  
  1311.                 lea     CMDModeSense2(pc),a1
  1312.                 moveq   #6,d0
  1313.                 moveq   #SCSIF_AUTOSENSE|SCSIF_READ,d1
  1314.                 bsr     CDDoSCSIA                   ; aktuelle Audio Page auslesen
  1315.                 bne     .EXIT                       ; erfolgreich?
  1316.  
  1317.                 move.l  cdx_SCSIData(a0),a1
  1318.                 tst.b   (a1)                        ; Mode Data Length = 0
  1319.                 beq     .EXIT                       ; ja: Ende
  1320.                 addq.l  #3,a1
  1321.                 move.b  (a1),d0                     ; Block Descriptor Length
  1322. .sl             addq.l  #1,a1                       ; Block Descriptor(s) überspringen
  1323.                 dbra    d0,.sl
  1324.                 moveq   #0,d0
  1325.                 move.b  (a1)+,d0
  1326.                 andi.b  #%00111111,d0               ; Page Code ohne Bit 6-7
  1327.                 cmp.b   #$e,d0                      ; wirklich Audio Control Page? (Eh)
  1328.                 bne     .EXIT
  1329.                 addq.l  #1,a1
  1330.                 lea     cdx_Volume0(a0),a2
  1331.                 tst.b   d2                          ; Lautstärke lesen oder setzen?
  1332.                 bne     .SetVolume
  1333.  
  1334. .GetVolume      move.b  7(a1),(a2)+                 ; Lautstärke in CDx eintragen
  1335.                 move.b  9(a1),(a2)+
  1336.                 move.b  11(a1),(a2)+
  1337.                 move.b  13(a1),(a2)
  1338.                 moveq   #0,d0                       ; alles glatt verlaufen
  1339.                 bra     .EXIT
  1340.  
  1341. .SetVolume      move.b  (a2)+,7(a1)                 ; CDx-Werte in Audio Page eintragen
  1342.                 move.b  (a2)+,9(a1)
  1343.                 move.b  (a2)+,11(a1)
  1344.                 move.b  (a2)+,13(a1)
  1345.                 moveq   #AUDIOPAGE_SIZEOF-1,d0
  1346.                 lea     AudioPage(pc),a2
  1347.                 move.l  cdx_ValidAudioPage(a0),a3
  1348. .cl             move.b  (a1)+,(a2)                  ; Page durch "Changeable Fields
  1349.                 move.b  (a3)+,d1                    ;  Maske" kopieren
  1350.                 and.b   d1,(a2)+
  1351.                 dbra    d0,.cl
  1352.                 move.l  cdx_SCSIData(a0),a1
  1353.                 lea     Header(pc),a2
  1354.                 moveq   #6+AUDIOPAGE_SIZEOF,d0
  1355. .huil              move.b  (a2)+,(a1)+                 ; fertige Page (plus Header) in SCSI-Datenpuffer kopieren
  1356.                 dbra    d0,.huil
  1357.                 lea     CMDModeSelect(pc),a1
  1358.                 moveq   #6,d0
  1359.                 moveq   #SCSIF_AUTOSENSE|SCSIF_WRITE,d1
  1360.                 bsr     CDDoSCSIA                   ; Mode Select: Audiopage setzen
  1361.  
  1362. .EXIT           movem.l (sp)+,d1-d3/a0-a3
  1363.                 rts
  1364.  
  1365. .ATAPI          moveq   #-1,d1                          ; Lautstärke nur auslesen
  1366.                 tst.l   d0
  1367.                 beq     .GET                            ; SET/GET?
  1368.  
  1369. .SET            moveq   #0,d1
  1370.                 move.b  cdx_Volume0(a0),d1
  1371.                 mulu.w  #$7fff,d1                       ; volle Lautstärke = 0x7fff
  1372.                 divu.w  #255,d1                         ; Verhältnisgleichung...
  1373.                 ext.l   d1
  1374. .GET            move.l  a0,a1                           ; a1 = cdx
  1375.                 move.l  cdx_IORequest(a1),a0            ; a0 = IORequest
  1376.                 move.w  #CD_ATTENUATE,IO_COMMAND(a0)
  1377.                 clr.l   IO_DATA(a0)                     ; NULL
  1378.                 clr.l   IO_LENGTH(a0)                   ; Änderung sofort vornehmen
  1379.                 move.l  d1,IO_OFFSET(a0)
  1380.                 bsr     DoATAPI
  1381.                 move.l  IO_ACTUAL(a0),d1                ; aktuelle Lautstärke
  1382.                 mulu.w  #255,d1                         ; umrechnen
  1383.                 divu.w  #$7fff,d1
  1384.                 move.b  d1,cdx_Volume0(a1)              ; und eintragen
  1385.                 move.b  d1,cdx_Volume1(a1)
  1386.                 bra     .EXIT
  1387.  
  1388. CMDModeSense2:  dc.b    $1a, 8, $e, 0, 254, 0
  1389. CMDModeSelect:  dc.b    $15, 16, 0, 0, 20, 0
  1390.  
  1391. *///
  1392.  
  1393. ;/// Sonstiges
  1394. Header:     dc.b 0, 0, 0, 0     ; Mode Data Length, Medium Type, Device-Specific Parameter, Block Descriptor Length
  1395.             dc.b $e, $e         ; Page Code (0Eh), Parameter Length (0Eh)
  1396. AudioPage:  dcb.b AUDIOPAGE_SIZEOF
  1397.  
  1398. UnknownVendor:  dcb.b   9
  1399.  
  1400. Vendors:
  1401.     dc.b "APPLE"    ,0, "Apple Computer, Inc.",0
  1402.     dc.b "CONNER"   ,0, "Conner Peripherals",0
  1403.     dc.b "DENON"    ,0, "Denon/Nippon Columbia",0
  1404.     dc.b "FUJI"     ,0, "Fuji Electric Co., Ltd. (Japan)",0
  1405.     dc.b "FUJITSU"  ,0, "Fujitsu",0
  1406.     dc.b "JVC"      ,0, "JVC Information Products Co.",0
  1407.     dc.b "MATSHITA" ,0, "Matsushita",0
  1408.     dc.b "MAXTOR"   ,0, "Maxtor Corp.",0
  1409.     dc.b "MELA"     ,0, "Mitsubishi Electronics America",0
  1410.     dc.b "MELCO"    ,0, "Mitsubishi Electric (Japan)",0
  1411.     dc.b "MICROP"   ,0, "Micropolis",0
  1412.     dc.b "NEC"      ,0, "NEC",0
  1413.     dc.b "OSI"      ,0, "Optical Storage International",0
  1414.     dc.b "QUANTUM"  ,0, "Quantum Corp.",0
  1415.     dc.b "SANYO"    ,0, "SANYO Electric Co., Ltd.",0
  1416.     dc.b "SEAGATE"  ,0, "Seagate",0
  1417.     dc.b "SONY"     ,0, "Sony Corporation Japan",0
  1418.     dc.b "TEAC"     ,0, "TEAC Japan",0
  1419.     dc.b "TOSHIBA"  ,0, "Toshiba Japan",0
  1420.     dc.b "WDIGTL"   ,0, "Western Digital",0
  1421.     dc.b 0
  1422.  
  1423. SpecialDevices:
  1424.     dc.b 'NEC '    , 0, SPECIALF_NEC
  1425.     dc.b 0
  1426.  
  1427. ;///
  1428.  
  1429.