ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/AmigaOS/sys_amiga.cpp
Revision: 1.7
Committed: 2002-01-15T14:58:34Z (22 years, 10 months ago) by cebix
Branch: MAIN
CVS Tags: snapshot-15012002
Changes since 1.6: +1 -1 lines
Log Message:
- documentation updates
- 2001 -> 2002
- version 0.9 -> 1.0

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * sys_amiga.cpp - System dependent routines, Amiga implementation
3     *
4 cebix 1.7 * Basilisk II (C) 1997-2002 Christian Bauer
5 cebix 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #include <exec/types.h>
22     #include <exec/memory.h>
23     #include <devices/newstyle.h>
24     #include <devices/trackdisk.h>
25     #include <devices/scsidisk.h>
26     #include <resources/disk.h>
27     #include <proto/dos.h>
28     #include <proto/exec.h>
29     #include <proto/disk.h>
30    
31     #include "sysdeps.h"
32     #include "main.h"
33     #include "macos_util.h"
34     #include "prefs.h"
35     #include "user_strings.h"
36     #include "sys.h"
37    
38     #define DEBUG 0
39     #include "debug.h"
40    
41    
42     // File handles are pointers to these structures
43     struct file_handle {
44     bool is_file; // Flag: plain file or /dev/something?
45     bool read_only; // Copy of Sys_open() flag
46     loff_t start_byte; // Size of file header (if any)
47     loff_t size; // Size of file/device (minus header)
48    
49     BPTR f; // AmigaDOS file handle (if is_file == true)
50    
51     struct IOStdReq *io; // Pointer to IORequest (if is_file == false)
52     ULONG block_size; // Block size of device (must be a power of two)
53     bool is_nsd; // New style device?
54     bool does_64bit; // Supports 64 bit trackdisk commands?
55 cebix 1.4 bool is_ejected; // Volume has been (logically) ejected
56     bool is_2060scsi; // Enable workaround for 2060scsi.device CD-ROM TD_READ bug
57 cebix 1.1 };
58    
59    
60     // FileInfoBlock (must be global because it has to be on a longword boundary)
61     static struct FileInfoBlock FIB;
62    
63     // Message port for device communication
64     static struct MsgPort *the_port = NULL;
65    
66     // Temporary buffer in chip memory
67     const int TMP_BUF_SIZE = 0x10000;
68     static UBYTE *tmp_buf = NULL;
69    
70    
71     /*
72     * Initialization
73     */
74    
75     void SysInit(void)
76     {
77     // Create port and temporary buffer
78     the_port = CreateMsgPort();
79     tmp_buf = (UBYTE *)AllocMem(TMP_BUF_SIZE, MEMF_CHIP | MEMF_PUBLIC);
80     if (the_port == NULL || tmp_buf == NULL) {
81 cebix 1.6 ErrorAlert(STR_NO_MEM_ERR);
82 cebix 1.1 QuitEmulator();
83     }
84     }
85    
86    
87     /*
88     * Deinitialization
89     */
90    
91     void SysExit(void)
92     {
93     // Delete port and temporary buffer
94 cebix 1.4 if (the_port) {
95 cebix 1.1 DeleteMsgPort(the_port);
96 cebix 1.4 the_port = NULL;
97     }
98     if (tmp_buf) {
99 cebix 1.1 FreeMem(tmp_buf, TMP_BUF_SIZE);
100 cebix 1.4 tmp_buf = NULL;
101     }
102 cebix 1.1 }
103    
104    
105     /*
106     * This gets called when no "floppy" prefs items are found
107     * It scans for available floppy drives and adds appropriate prefs items
108     */
109    
110     void SysAddFloppyPrefs(void)
111     {
112     for (int i=0; i<4; i++) {
113     ULONG id = GetUnitID(i);
114     if (id == DRT_150RPM) { // We need an HD drive
115     char str[256];
116 cebix 1.4 sprintf(str, "/dev/mfm.device/%d/0/0/2880/512", i);
117 cebix 1.1 PrefsAddString("floppy", str);
118     }
119     }
120     }
121    
122    
123     /*
124     * This gets called when no "disk" prefs items are found
125     * It scans for available HFS volumes and adds appropriate prefs items
126     */
127    
128     void SysAddDiskPrefs(void)
129     {
130     // AmigaOS doesn't support MacOS partitioning, so this probably doesn't make much sense...
131     }
132    
133    
134     /*
135     * This gets called when no "cdrom" prefs items are found
136     * It scans for available CD-ROM drives and adds appropriate prefs items
137     */
138    
139     void SysAddCDROMPrefs(void)
140     {
141     // Don't scan for drives if nocdrom option given
142     if (PrefsFindBool("nocdrom"))
143     return;
144    
145     //!!
146     }
147    
148    
149     /*
150     * Add default serial prefs (must be added, even if no ports present)
151     */
152    
153     void SysAddSerialPrefs(void)
154     {
155     PrefsAddString("seriala", "serial.device/0");
156     PrefsAddString("serialb", "*parallel.device/0");
157     }
158    
159    
160     /*
161     * Open file/device, create new file handle (returns NULL on error)
162     *
163     * Format for device names: /dev/<name>/<unit>/<open flags>/<start block>/<size (blocks)>/<block size>
164     */
165    
166     void *Sys_open(const char *name, bool read_only)
167     {
168     bool is_file = (strstr(name, "/dev/") != name);
169    
170     D(bug("Sys_open(%s, %s)\n", name, read_only ? "read-only" : "read/write"));
171    
172     // File or device?
173     if (is_file) {
174    
175     // File, open it and get stats
176     BPTR f = Open((char *)name, MODE_OLDFILE);
177     if (!f)
178     return NULL;
179     if (!ExamineFH(f, &FIB)) {
180     Close(f);
181     return NULL;
182     }
183    
184     // Check if file is write protected
185     if (FIB.fib_Protection & FIBF_WRITE)
186     read_only = true;
187    
188     // Create file_handle
189     file_handle *fh = new file_handle;
190     fh->f = f;
191     fh->is_file = true;
192     fh->read_only = read_only;
193    
194     // Detect disk image file layout
195     loff_t size = FIB.fib_Size;
196     Seek(fh->f, 0, OFFSET_BEGINNING);
197     Read(fh->f, tmp_buf, 256);
198     FileDiskLayout(size, tmp_buf, fh->start_byte, fh->size);
199     return fh;
200    
201     } else {
202    
203     // Device, parse string
204     char dev_name[256];
205     ULONG dev_unit = 0, dev_flags = 0, dev_start = 0, dev_size = 16, dev_bsize = 512;
206     if (sscanf(name, "/dev/%[^/]/%ld/%ld/%ld/%ld/%ld", dev_name, &dev_unit, &dev_flags, &dev_start, &dev_size, &dev_bsize) < 2)
207     return NULL;
208    
209     // Create IORequest
210     struct IOStdReq *io = (struct IOStdReq *)CreateIORequest(the_port, sizeof(struct IOExtTD));
211     if (io == NULL)
212     return NULL;
213    
214     // Open device
215     if (OpenDevice((UBYTE *)dev_name, dev_unit, (struct IORequest *)io, dev_flags)) {
216     D(bug(" couldn't open device\n"));
217     DeleteIORequest(io);
218     return NULL;
219     }
220    
221     // Check for new style device
222     bool is_nsd = false, does_64bit = false;
223     struct NSDeviceQueryResult nsdqr;
224     nsdqr.DevQueryFormat = 0;
225     nsdqr.SizeAvailable = 0;
226     io->io_Command = NSCMD_DEVICEQUERY;
227     io->io_Length = sizeof(nsdqr);
228     io->io_Data = (APTR)&nsdqr;
229     LONG error = DoIO((struct IORequest *)io);
230     D(bug("DEVICEQUERY returned %ld (length %ld, actual %ld)\n", error, io->io_Length, io->io_Actual));
231     if ((!error) && (io->io_Actual >= 16) && (io->io_Actual <= sizeof(nsdqr)) && (nsdqr.SizeAvailable == io->io_Actual)) {
232    
233     // Looks like an NSD
234     is_nsd = true;
235     D(bug(" new style device, type %ld\n", nsdqr.DeviceType));
236    
237     // We only work with trackdisk-like devices
238     if (nsdqr.DeviceType != NSDEVTYPE_TRACKDISK) {
239     CloseDevice((struct IORequest *)io);
240     DeleteIORequest(io);
241     return NULL;
242     }
243    
244     // Check whether device is 64 bit capable
245     UWORD *cmdcheck;
246     for (cmdcheck = nsdqr.SupportedCommands; *cmdcheck; cmdcheck++) {
247     if (*cmdcheck == NSCMD_TD_READ64) {
248     D(bug(" supports 64 bit commands\n"));
249     does_64bit = true;
250     }
251     }
252     }
253    
254     // Create file_handle
255     file_handle *fh = new file_handle;
256     fh->io = io;
257     fh->is_file = false;
258     fh->read_only = read_only;
259     fh->start_byte = (loff_t)dev_start * dev_bsize;
260     fh->size = (loff_t)dev_size * dev_bsize;
261     fh->block_size = dev_bsize;
262     fh->is_nsd = is_nsd;
263     fh->does_64bit = does_64bit;
264 cebix 1.4 fh->is_ejected = false;
265     fh->is_2060scsi = (strcmp(dev_name, "2060scsi.device") == 0);
266 cebix 1.1 return fh;
267     }
268     }
269    
270    
271     /*
272     * Close file/device, delete file handle
273     */
274    
275     void Sys_close(void *arg)
276     {
277     file_handle *fh = (file_handle *)arg;
278     if (!fh)
279     return;
280    
281     D(bug("Sys_close(%08lx)\n", arg));
282    
283     // File or device?
284     if (fh->is_file) {
285    
286     // File, simply close it
287     Close(fh->f);
288    
289     } else {
290    
291     // Device, close it and delete IORequest
292     fh->io->io_Command = CMD_UPDATE;
293     DoIO((struct IORequest *)fh->io);
294    
295     fh->io->io_Command = TD_MOTOR;
296     fh->io->io_Length = 0;
297     DoIO((struct IORequest *)fh->io);
298    
299     CloseDevice((struct IORequest *)fh->io);
300     DeleteIORequest(fh->io);
301     }
302     delete fh;
303     }
304    
305    
306     /*
307 cebix 1.2 * Send one I/O request, using 64-bit addressing if the device supports it
308     */
309    
310     static loff_t send_io_request(file_handle *fh, bool writing, ULONG length, loff_t offset, APTR data)
311     {
312     if (fh->does_64bit) {
313     fh->io->io_Command = writing ? NSCMD_TD_WRITE64 : NSCMD_TD_READ64;
314     fh->io->io_Actual = offset >> 32;
315     } else {
316     fh->io->io_Command = writing ? CMD_WRITE : CMD_READ;
317     fh->io->io_Actual = 0;
318     }
319     fh->io->io_Length = length;
320     fh->io->io_Offset = offset;
321     fh->io->io_Data = data;
322 cebix 1.4
323     if (fh->is_2060scsi && fh->block_size == 2048) {
324    
325     // 2060scsi.device has serious problems reading CD-ROMs via TD_READ
326     static struct SCSICmd scsi;
327     const int SENSE_LENGTH = 256;
328     static UBYTE sense_buffer[SENSE_LENGTH]; // Buffer for autosense data
329     static UBYTE cmd_buffer[10] = { 0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
330    
331     D(bug("send_io_request length=%lu offset=%lu\n", length, (ULONG) offset));
332    
333     memset(sense_buffer, 0, sizeof(sense_buffer));
334    
335     scsi.scsi_Command = cmd_buffer;
336     scsi.scsi_CmdLength = sizeof(cmd_buffer);
337     scsi.scsi_SenseData = sense_buffer;
338     scsi.scsi_SenseLength = SENSE_LENGTH;
339     scsi.scsi_Flags = SCSIF_AUTOSENSE | (writing ? SCSIF_WRITE : SCSIF_READ);
340     scsi.scsi_Data = (UWORD *) data;
341     scsi.scsi_Length = length;
342    
343     ULONG block_offset = (ULONG) offset / fh->block_size;
344     ULONG block_length = length / fh->block_size;
345    
346     cmd_buffer[2] = block_offset >> 24;
347     cmd_buffer[3] = block_offset >> 16;
348     cmd_buffer[4] = block_offset >> 8;
349     cmd_buffer[5] = block_offset & 0xff;
350    
351     cmd_buffer[7] = block_length >> 8;
352     cmd_buffer[8] = block_length & 0xff;
353    
354     fh->io->io_Command = HD_SCSICMD;
355     fh->io->io_Actual = 0;
356     fh->io->io_Offset = 0;
357     fh->io->io_Data = &scsi;
358     fh->io->io_Length = sizeof(scsi);
359    
360     BYTE result = DoIO((struct IORequest *)fh->io);
361    
362     if (result) {
363     D(bug("send_io_request SCSI FAIL result=%lu\n", result));
364    
365     if (result == HFERR_BadStatus) {
366     D(bug("send_io_request SCSI Status=%lu\n", scsi.scsi_Status));
367     if (scsi.scsi_Status == 2) {
368     D(bug("send_io_request Sense Key=%02lx\n", sense_buffer[2] & 0x0f));
369     D(bug("send_io_request ASC=%02lx ASCQ=%02lx\n", sense_buffer[12], sense_buffer[13]));
370     }
371     }
372     return 0;
373     }
374    
375     D(bug("send_io_request SCSI Actual=%lu\n", scsi.scsi_Actual));
376    
377     if (scsi.scsi_Actual != length)
378     return 0;
379    
380     return scsi.scsi_Actual;
381    
382     } else {
383    
384     if (DoIO((struct IORequest *)fh->io) || fh->io->io_Actual != length)
385     return 0;
386     return fh->io->io_Actual;
387     }
388 cebix 1.2 }
389    
390    
391     /*
392 cebix 1.1 * Read "length" bytes from file/device, starting at "offset", to "buffer",
393     * returns number of bytes read (or 0)
394     */
395    
396     size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length)
397     {
398     file_handle *fh = (file_handle *)arg;
399     if (!fh)
400     return 0;
401    
402     // File or device?
403     if (fh->is_file) {
404    
405     // File, seek to position
406     if (Seek(fh->f, offset + fh->start_byte, OFFSET_BEGINNING) == -1)
407     return 0;
408    
409     // Read data
410     LONG actual = Read(fh->f, buffer, length);
411     if (actual == -1)
412     return 0;
413     else
414     return actual;
415    
416     } else {
417    
418 cebix 1.2 // Device, pre-read (partial read of first block) necessary?
419 cebix 1.1 loff_t pos = offset + fh->start_byte;
420     size_t actual = 0;
421     uint32 pre_offset = pos % fh->block_size;
422     if (pre_offset) {
423    
424     // Yes, read one block
425 cebix 1.2 if (send_io_request(fh, false, fh->block_size, pos - pre_offset, tmp_buf) == 0)
426 cebix 1.1 return 0;
427    
428     // Copy data to destination buffer
429     size_t pre_length = fh->block_size - pre_offset;
430     if (pre_length > length)
431     pre_length = length;
432     memcpy(buffer, tmp_buf + pre_offset, pre_length);
433    
434     // Adjust data pointers
435     buffer = (uint8 *)buffer + pre_length;
436     pos += pre_length;
437     length -= pre_length;
438     actual += pre_length;
439     }
440    
441     // Main read (complete reads of middle blocks) possible?
442     if (length >= fh->block_size) {
443    
444     // Yes, read blocks
445     size_t main_length = length & ~(fh->block_size - 1);
446 cebix 1.2 if (send_io_request(fh, false, main_length, pos, buffer) == 0)
447 cebix 1.1 return 0;
448    
449     // Adjust data pointers
450     buffer = (uint8 *)buffer + main_length;
451     pos += main_length;
452     length -= main_length;
453     actual += main_length;
454     }
455    
456 cebix 1.2 // Post-read (partial read of last block) necessary?
457 cebix 1.1 if (length) {
458    
459     // Yes, read one block
460 cebix 1.2 if (send_io_request(fh, false, fh->block_size, pos, tmp_buf) == 0)
461 cebix 1.1 return 0;
462    
463     // Copy data to destination buffer
464     memcpy(buffer, tmp_buf, length);
465     actual += length;
466     }
467    
468     return actual;
469     }
470     }
471    
472    
473     /*
474     * Write "length" bytes from "buffer" to file/device, starting at "offset",
475     * returns number of bytes written (or 0)
476     */
477    
478     size_t Sys_write(void *arg, void *buffer, loff_t offset, size_t length)
479     {
480     file_handle *fh = (file_handle *)arg;
481     if (!fh)
482     return 0;
483    
484     // File or device?
485     if (fh->is_file) {
486    
487 cebix 1.2 // File, seek to position if necessary
488 cebix 1.1 if (Seek(fh->f, offset + fh->start_byte, OFFSET_BEGINNING) == -1)
489     return 0;
490    
491     // Write data
492     LONG actual = Write(fh->f, buffer, length);
493     if (actual == -1)
494     return 0;
495     else
496     return actual;
497    
498     } else {
499    
500 cebix 1.2 // Device, pre-write (partial write of first block) necessary
501     loff_t pos = offset + fh->start_byte;
502     size_t actual = 0;
503     uint32 pre_offset = pos % fh->block_size;
504     if (pre_offset) {
505    
506     // Yes, read one block
507     if (send_io_request(fh, false, fh->block_size, pos - pre_offset, tmp_buf) == 0)
508     return 0;
509    
510     // Copy data from source buffer
511     size_t pre_length = fh->block_size - pre_offset;
512     if (pre_length > length)
513     pre_length = length;
514     memcpy(tmp_buf + pre_offset, buffer, pre_length);
515    
516     // Write block back
517     if (send_io_request(fh, true, fh->block_size, pos - pre_offset, tmp_buf) == 0)
518     return 0;
519    
520     // Adjust data pointers
521     buffer = (uint8 *)buffer + pre_length;
522     pos += pre_length;
523     length -= pre_length;
524     actual += pre_length;
525     }
526    
527     // Main write (complete writes of middle blocks) possible?
528     if (length >= fh->block_size) {
529    
530     // Yes, write blocks
531     size_t main_length = length & ~(fh->block_size - 1);
532     if (send_io_request(fh, true, main_length, pos, buffer) == 0)
533     return 0;
534    
535     // Adjust data pointers
536     buffer = (uint8 *)buffer + main_length;
537     pos += main_length;
538     length -= main_length;
539     actual += main_length;
540     }
541    
542     // Post-write (partial write of last block) necessary?
543     if (length) {
544    
545     // Yes, read one block
546     if (send_io_request(fh, false, fh->block_size, pos, tmp_buf) == 0)
547     return 0;
548    
549     // Copy data from source buffer
550     memcpy(buffer, tmp_buf, length);
551    
552     // Write block back
553     if (send_io_request(fh, true, fh->block_size, pos, tmp_buf) == 0)
554     return 0;
555     actual += length;
556     }
557    
558     return actual;
559 cebix 1.1 }
560     }
561    
562    
563     /*
564     * Return size of file/device (minus header)
565     */
566    
567     loff_t SysGetFileSize(void *arg)
568     {
569     file_handle *fh = (file_handle *)arg;
570     if (!fh)
571     return true;
572    
573     return fh->size;
574     }
575    
576    
577     /*
578     * Eject volume (if applicable)
579     */
580    
581     void SysEject(void *arg)
582     {
583     file_handle *fh = (file_handle *)arg;
584     if (!fh)
585     return;
586    
587     if (!fh->is_file) {
588    
589     // Flush buffer, turn off the drive motor and eject volume
590     fh->io->io_Command = CMD_UPDATE;
591     DoIO((struct IORequest *)fh->io);
592    
593     fh->io->io_Command = TD_MOTOR;
594     fh->io->io_Length = 0;
595     DoIO((struct IORequest *)fh->io);
596    
597     fh->io->io_Command = TD_EJECT;
598     fh->io->io_Length = 1;
599     DoIO((struct IORequest *)fh->io);
600 cebix 1.4
601     fh->is_ejected = true;
602 cebix 1.1 }
603     }
604    
605    
606     /*
607     * Format volume (if applicable)
608     */
609    
610     bool SysFormat(void *arg)
611     {
612     file_handle *fh = (file_handle *)arg;
613     if (!fh)
614     return false;
615    
616     //!!
617     return true;
618     }
619    
620    
621     /*
622     * Check if file/device is read-only (this includes the read-only flag on Sys_open())
623     */
624    
625     bool SysIsReadOnly(void *arg)
626     {
627     file_handle *fh = (file_handle *)arg;
628     if (!fh)
629     return true;
630    
631     if (fh->is_file) {
632    
633     // File, return flag given to Sys_open
634     return fh->read_only;
635    
636     } else {
637    
638     // Device, check write protection
639     fh->io->io_Command = TD_PROTSTATUS;
640 cebix 1.4 DoIO((struct IORequest *)fh->io);
641 cebix 1.1 if (fh->io->io_Actual)
642     return true;
643     else
644     return fh->read_only;
645     }
646     }
647    
648    
649     /*
650     * Check if the given file handle refers to a fixed or a removable disk
651     */
652    
653     bool SysIsFixedDisk(void *arg)
654     {
655     file_handle *fh = (file_handle *)arg;
656     if (!fh)
657     return true;
658    
659     return true;
660     }
661    
662    
663     /*
664     * Check if a disk is inserted in the drive (always true for files)
665     */
666    
667     bool SysIsDiskInserted(void *arg)
668     {
669     file_handle *fh = (file_handle *)arg;
670     if (!fh)
671     return false;
672    
673     if (fh->is_file)
674     return true;
675     else {
676    
677     // Check medium status
678     fh->io->io_Command = TD_CHANGESTATE;
679     fh->io->io_Actual = 0;
680 cebix 1.4 DoIO((struct IORequest *)fh->io);
681     bool inserted = (fh->io->io_Actual == 0);
682    
683     if (!inserted) {
684     // Disk was ejected and has now been taken out
685     fh->is_ejected = false;
686     }
687    
688     if (fh->is_ejected) {
689     // Disk was ejected but has not yet been taken out, report it as
690     // no longer in the drive
691     return false;
692     } else
693     return inserted;
694 cebix 1.1 }
695     }
696    
697    
698     /*
699     * Prevent medium removal (if applicable)
700     */
701    
702     void SysPreventRemoval(void *arg)
703     {
704     file_handle *fh = (file_handle *)arg;
705     if (!fh)
706     return;
707    
708     if (!fh->is_file) {
709    
710     // Send PREVENT ALLOW MEDIUM REMOVAL SCSI command
711     struct SCSICmd scsi;
712     static const UBYTE the_cmd[6] = {0x1e, 0, 0, 0, 1, 0};
713     scsi.scsi_Length = 0;
714     scsi.scsi_Command = (UBYTE *)the_cmd;
715     scsi.scsi_CmdLength = 6;
716     scsi.scsi_Flags = SCSIF_READ;
717     scsi.scsi_Status = 0;
718     fh->io->io_Data = &scsi;
719     fh->io->io_Length = sizeof(scsi);
720     fh->io->io_Command = HD_SCSICMD;
721     DoIO((struct IORequest *)fh->io);
722     }
723     }
724    
725    
726     /*
727     * Allow medium removal (if applicable)
728     */
729    
730     void SysAllowRemoval(void *arg)
731     {
732     file_handle *fh = (file_handle *)arg;
733     if (!fh)
734     return;
735    
736     if (!fh->is_file) {
737    
738     // Send PREVENT ALLOW MEDIUM REMOVAL SCSI command
739     struct SCSICmd scsi;
740     static const UBYTE the_cmd[6] = {0x1e, 0, 0, 0, 0, 0};
741     scsi.scsi_Length = 0;
742     scsi.scsi_Command = (UBYTE *)the_cmd;
743     scsi.scsi_CmdLength = 6;
744     scsi.scsi_Flags = SCSIF_READ;
745     scsi.scsi_Status = 0;
746     fh->io->io_Data = &scsi;
747     fh->io->io_Length = sizeof(scsi);
748     fh->io->io_Command = HD_SCSICMD;
749     DoIO((struct IORequest *)fh->io);
750     }
751     }
752    
753    
754     /*
755     * Read CD-ROM TOC (binary MSF format, 804 bytes max.)
756     */
757    
758     bool SysCDReadTOC(void *arg, uint8 *toc)
759     {
760     file_handle *fh = (file_handle *)arg;
761     if (!fh)
762     return false;
763    
764     if (fh->is_file)
765     return false;
766     else {
767    
768     // Send READ TOC MSF SCSI command
769     struct SCSICmd scsi;
770     static const UBYTE read_toc_cmd[10] = {0x43, 0x02, 0, 0, 0, 0, 0, 0x03, 0x24, 0};
771     scsi.scsi_Data = (UWORD *)tmp_buf;
772     scsi.scsi_Length = 804;
773     scsi.scsi_Command = (UBYTE *)read_toc_cmd;
774     scsi.scsi_CmdLength = 10;
775     scsi.scsi_Flags = SCSIF_READ;
776     scsi.scsi_Status = 0;
777     fh->io->io_Data = &scsi;
778     fh->io->io_Length = sizeof(scsi);
779     fh->io->io_Command = HD_SCSICMD;
780     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
781     return false;
782     memcpy(toc, tmp_buf, 804);
783     return true;
784     }
785     }
786    
787    
788     /*
789     * Read CD-ROM position data (Sub-Q Channel, 16 bytes, see SCSI standard)
790     */
791    
792     bool SysCDGetPosition(void *arg, uint8 *pos)
793     {
794     file_handle *fh = (file_handle *)arg;
795     if (!fh)
796     return false;
797    
798     if (fh->is_file)
799     return false;
800     else {
801    
802     // Send READ SUB-CHANNEL SCSI command
803     struct SCSICmd scsi;
804     static const UBYTE read_subq_cmd[10] = {0x42, 0x02, 0x40, 0x01, 0, 0, 0, 0, 0x10, 0};
805     scsi.scsi_Data = (UWORD *)tmp_buf;
806     scsi.scsi_Length = 16;
807     scsi.scsi_Command = (UBYTE *)read_subq_cmd;
808     scsi.scsi_CmdLength = 10;
809     scsi.scsi_Flags = SCSIF_READ;
810     scsi.scsi_Status = 0;
811     fh->io->io_Data = &scsi;
812     fh->io->io_Length = sizeof(scsi);
813     fh->io->io_Command = HD_SCSICMD;
814     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
815     return false;
816     memcpy(pos, tmp_buf, 16);
817     return true;
818     }
819     }
820    
821    
822     /*
823     * Play CD audio
824     */
825    
826     bool SysCDPlay(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, uint8 end_m, uint8 end_s, uint8 end_f)
827     {
828     file_handle *fh = (file_handle *)arg;
829     if (!fh)
830     return false;
831    
832     if (fh->is_file)
833     return false;
834     else {
835    
836     // Send PLAY AUDIO MSF SCSI command
837     struct SCSICmd scsi;
838     UBYTE play_cmd[10] = {0x47, 0, 0, start_m, start_s, start_f, end_m, end_s, end_f, 0};
839     scsi.scsi_Data = (UWORD *)tmp_buf;
840     scsi.scsi_Length = 0;
841     scsi.scsi_Command = play_cmd;
842     scsi.scsi_CmdLength = 10;
843     scsi.scsi_Flags = SCSIF_READ;
844     scsi.scsi_Status = 0;
845     fh->io->io_Data = &scsi;
846     fh->io->io_Length = sizeof(scsi);
847     fh->io->io_Command = HD_SCSICMD;
848     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
849     return false;
850     return true;
851     }
852     }
853    
854    
855     /*
856     * Pause CD audio
857     */
858    
859     bool SysCDPause(void *arg)
860     {
861     file_handle *fh = (file_handle *)arg;
862     if (!fh)
863     return false;
864    
865     if (fh->is_file)
866     return false;
867     else {
868    
869     // Send PAUSE RESUME SCSI command
870     struct SCSICmd scsi;
871     static const UBYTE pause_cmd[10] = {0x4b, 0, 0, 0, 0, 0, 0, 0, 0, 0};
872     scsi.scsi_Data = (UWORD *)tmp_buf;
873     scsi.scsi_Length = 0;
874     scsi.scsi_Command = (UBYTE *)pause_cmd;
875     scsi.scsi_CmdLength = 10;
876     scsi.scsi_Flags = SCSIF_READ;
877     scsi.scsi_Status = 0;
878     fh->io->io_Data = &scsi;
879     fh->io->io_Length = sizeof(scsi);
880     fh->io->io_Command = HD_SCSICMD;
881     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
882     return false;
883     return true;
884     }
885     }
886    
887    
888     /*
889     * Resume paused CD audio
890     */
891    
892     bool SysCDResume(void *arg)
893     {
894     file_handle *fh = (file_handle *)arg;
895     if (!fh)
896     return false;
897    
898     if (fh->is_file)
899     return false;
900     else {
901    
902     // Send PAUSE RESUME SCSI command
903     struct SCSICmd scsi;
904     static const UBYTE resume_cmd[10] = {0x4b, 0, 0, 0, 0, 0, 0, 0, 1, 0};
905     scsi.scsi_Data = (UWORD *)tmp_buf;
906     scsi.scsi_Length = 0;
907     scsi.scsi_Command = (UBYTE *)resume_cmd;
908     scsi.scsi_CmdLength = 10;
909     scsi.scsi_Flags = SCSIF_READ;
910     scsi.scsi_Status = 0;
911     fh->io->io_Data = &scsi;
912     fh->io->io_Length = sizeof(scsi);
913     fh->io->io_Command = HD_SCSICMD;
914     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
915     return false;
916     return true;
917     }
918     }
919    
920    
921     /*
922     * Stop CD audio
923     */
924    
925     bool SysCDStop(void *arg, uint8 lead_out_m, uint8 lead_out_s, uint8 lead_out_f)
926     {
927     file_handle *fh = (file_handle *)arg;
928     if (!fh)
929     return false;
930    
931     if (fh->is_file)
932     return false;
933     else {
934    
935     uint8 end_m = lead_out_m;
936     uint8 end_s = lead_out_s;
937     uint8 end_f = lead_out_f + 1;
938     if (end_f >= 75) {
939     end_f = 0;
940     end_s++;
941     if (end_s >= 60) {
942     end_s = 0;
943     end_m++;
944     }
945     }
946    
947     // Send PLAY AUDIO MSF SCSI command (play first frame of lead-out area)
948     struct SCSICmd scsi;
949     UBYTE play_cmd[10] = {0x47, 0, 0, lead_out_m, lead_out_s, lead_out_f, end_m, end_s, end_f, 0};
950     scsi.scsi_Data = (UWORD *)tmp_buf;
951     scsi.scsi_Length = 0;
952     scsi.scsi_Command = play_cmd;
953     scsi.scsi_CmdLength = 10;
954     scsi.scsi_Flags = SCSIF_READ;
955     scsi.scsi_Status = 0;
956     fh->io->io_Data = &scsi;
957     fh->io->io_Length = sizeof(scsi);
958     fh->io->io_Command = HD_SCSICMD;
959     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
960     return false;
961     return true;
962     }
963     }
964    
965    
966     /*
967     * Perform CD audio fast-forward/fast-reverse operation starting from specified address
968     */
969    
970     bool SysCDScan(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, bool reverse)
971     {
972     file_handle *fh = (file_handle *)arg;
973     if (!fh)
974     return false;
975    
976     //!!
977     return false;
978     }
979    
980    
981     /*
982     * Set CD audio volume (0..255 each channel)
983     */
984    
985     void SysCDSetVolume(void *arg, uint8 left, uint8 right)
986     {
987     file_handle *fh = (file_handle *)arg;
988     if (!fh)
989     return;
990    
991     if (!fh->is_file) {
992    
993     // Send MODE SENSE (CD-ROM Audio Control Parameters Page) SCSI command
994     struct SCSICmd scsi;
995     static const UBYTE mode_sense_cmd[6] = {0x1a, 0x08, 0x0e, 0, 20, 0};
996     scsi.scsi_Data = (UWORD *)tmp_buf;
997     scsi.scsi_Length = 20;
998     scsi.scsi_Command = (UBYTE *)mode_sense_cmd;
999     scsi.scsi_CmdLength = 6;
1000     scsi.scsi_Flags = SCSIF_READ;
1001     scsi.scsi_Status = 0;
1002     fh->io->io_Data = &scsi;
1003     fh->io->io_Length = sizeof(scsi);
1004     fh->io->io_Command = HD_SCSICMD;
1005     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
1006     return;
1007    
1008     tmp_buf[6] = 0x04; // Immed
1009     tmp_buf[9] = 0; // LBA/sec format
1010     tmp_buf[10] = 0; // LBA/sec
1011     tmp_buf[11] = 0;
1012     tmp_buf[13] = left; // Port 0 volume
1013     tmp_buf[15] = right; // Port 1 volume
1014    
1015     // Send MODE SELECT (CD-ROM Audio Control Parameters Page) SCSI command
1016     static const UBYTE mode_select_cmd[6] = {0x15, 0x10, 0, 0, 20, 0};
1017     scsi.scsi_Data = (UWORD *)tmp_buf;
1018     scsi.scsi_Length = 20;
1019     scsi.scsi_Command = (UBYTE *)mode_select_cmd;
1020     scsi.scsi_CmdLength = 6;
1021     scsi.scsi_Flags = SCSIF_WRITE;
1022     scsi.scsi_Status = 0;
1023     fh->io->io_Data = &scsi;
1024     fh->io->io_Length = sizeof(scsi);
1025     fh->io->io_Command = HD_SCSICMD;
1026     DoIO((struct IORequest *)fh->io);
1027     }
1028     }
1029    
1030    
1031     /*
1032     * Get CD audio volume (0..255 each channel)
1033     */
1034    
1035     void SysCDGetVolume(void *arg, uint8 &left, uint8 &right)
1036     {
1037     file_handle *fh = (file_handle *)arg;
1038     if (!fh)
1039     return;
1040    
1041     if (!fh->is_file) {
1042    
1043     // Send MODE SENSE (CD-ROM Audio Control Parameters Page) SCSI command
1044     struct SCSICmd scsi;
1045     static const UBYTE mode_sense_cmd[6] = {0x1a, 0x08, 0x0e, 0, 20, 0};
1046     scsi.scsi_Data = (UWORD *)tmp_buf;
1047     scsi.scsi_Length = 20;
1048     scsi.scsi_Command = (UBYTE *)mode_sense_cmd;
1049     scsi.scsi_CmdLength = 6;
1050     scsi.scsi_Flags = SCSIF_READ;
1051     scsi.scsi_Status = 0;
1052     fh->io->io_Data = &scsi;
1053     fh->io->io_Length = sizeof(scsi);
1054     fh->io->io_Command = HD_SCSICMD;
1055     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
1056     return;
1057     left = tmp_buf[13]; // Port 0 volume
1058     right = tmp_buf[15]; // Port 1 volume
1059     }
1060     }