/* CDROM AUDIO ROUTINES By Barry Egerter Written September 24, 1994 Using Watcom C/C++ 10.0 Code : FREEWARE - alter and use at will. Internet Email: barry.egerter@softnet.com */ #include #include #include #define CDROM 0x21 #define EJECT_TRAY 0 #define RESET 2 #define CLOSE_TRAY 5 #define MEDIA_CHANGE 9 #define BUSY 512 #define TRACK_MASK 208 typedef struct playinfo { unsigned char control; unsigned char adr; unsigned char track; unsigned char index; unsigned char min; unsigned char sec; unsigned char frame; unsigned char zero; unsigned char amin; unsigned char asec; unsigned char aframe; }; typedef struct volumeinfo { unsigned char mode; unsigned char input0; unsigned char volume0; unsigned char input1; unsigned char volume1; unsigned char input2; unsigned char volume2; unsigned char input3; unsigned char volume3; }; struct { unsigned short drives; unsigned char first_drive; unsigned short current_track; unsigned int track_position; unsigned char track_type; unsigned char low_audio; unsigned char high_audio; unsigned char disk_length_min; unsigned char disk_length_sec; unsigned char disk_length_frames; unsigned int endofdisk; unsigned char upc[7]; unsigned char diskid[6]; unsigned int status; unsigned short error; /* See description below */ } cdrom_data; /* CDROM_DATA.ERROR Description Bit 15 - Error bit Bit 14-10 - Reserved Bit 9 - Busy Bit 8 - Done Bit 7-0 - Error code (bit 15 on) Error codes are the following: 0 Write-protect violation 1 Unknown unit 2 Drive not ready 3 Unknown command 4 CRC error 5 Bad drive request structure length 6 Seek error 7 Unknown media 8 Sector not found 9 Printer out of paper A Write fault B Read fault C General failure D Reserved E Reserved F Invalid disk change */ /* Multiplex Interrupt routines "borrowed" from Ralf Brown's MSDOS Interrupt List v4.1 short 21 - CD-ROM device driver - IOCTL INPUT AX = 4402h BX = file handle referencing character device for CD-ROM driver CX = number of bytes to read DS:DX -> control block (see #0563) Return: CF clear if successful AX = number of bytes actually read CF set on error AX = error code (01h,05h,06h,0Dh) (see #0770 at AH=59h) Note: the data returned depends on the first byte of the control block; the remainder of the control block is filled by the driver SeeAlso: AX=4403h"CD-ROM",short 2F/AX=0802h (Table 0562) Values for CD-ROM data being requested: 00h device driver header address 01h drive head location 02h reserved 03h error statistics 04h audio channel info 05h raw drive bytes (uninterpreted and device-specific) 06h device status 07h sector size 08h volume size 09h media change status 0Ah audio disk info 0Bh audio track info 0Ch audio Q-Channel info 0Dh audio sub-channel info 0Eh UPC code Format of CD-ROM control block: Offset Size Description (Table 0563) 00h BYTE data being requested (see #0562) ---function 00h--- 01h DWORD device driver header address (see also AH=52h) ---function 01h--- 01h BYTE addressing mode 00h HSG 01h Red Book 02h DWORD current location of drive's head logical sector number in HSG mode frame/second/minute/unused in Red Book mode (HSG sector = minute * 4500 + second * 75 + frame - 150) ---function 03h--- 01h N BYTEs undefined as of 5 Aug 88 specification ---function 04h--- 01h BYTE input channel (0-3) for output channel 0 02h BYTE volume for output channel 0 03h BYTE input channel (0-3) for output channel 1 04h BYTE volume for output channel 1 05h BYTE input channel (0-3) for output channel 2 06h BYTE volume for output channel 2 07h BYTE input channel (0-3) for output channel 3 08h BYTE volume for output channel 3 Notes: output channels 0 and 1 are left and right, 2 and 3 are left prime and right prime; a volume of 00h is off the default setting is for each input channel to be assigned to the same-numbered output channel at full (FFh) volume ---function 05h--- 01h BYTE number of bytes read 02h 128 BYTEs buffer for drive bytes ---function 06h--- 01h DWORD device parameters (see #0564) ---function 07h--- 01h BYTE read mode 00h cooked 01h raw 02h WORD sector size in bytes ---function 08h--- 01h DWORD volume size in sectors ---function 09h--- 01h BYTE media change status 00h don't know 01h media unchanged FFh media has been changed ---function 0Ah--- 01h BYTE lowest audio track number 02h BYTE highest audio track number 03h DWORD start address of lead-out track (Red Book format) --function 0Bh--- 01h BYTE track number (set by caller) 02h DWORD starting point of track (Red Book format) 06h BYTE track control info bits 15,14,12: track type (notice: bits not contiguous!) 000 two audio channels, no pre-emphasis 001 two audio channels with pre-emphasis 010 data track 100 four audio channels, no pre-emphasis 101 four audio channels with pre-emphasis other reserved bit 13: digital copy permitted ---function 0Ch--- 01h BYTE CONTROL and ADR byte (as received from drive) 02h BYTE track number 03h BYTE point or index 04h BYTE minute \ 05h BYTE second > running time within track 06h BYTE frame / 07h BYTE zero 08h BYTE "AMIN" or "PMIN" \ 09h BYTE "ASEC" or "PSEC" > running time on disk 0Ah BYTE "AFRAME" or "PFRAME" / ---function 0Dh--- 01h DWORD starting frame address (Red Book format) 05h DWORD transfer address 09h DWORD number of sectors to read Note: copies 96 bytes of sub-channel info per sector into buffer ---function 0Eh--- 01h BYTE CONTROL and ADR byte 02h 7 BYTEs UPC/EAN code (13 BCD digits,low-order nybble of last byte is 0) 09h BYTE zero 0Ah BYTE "AFRAME" Bitfields for CD-ROM device parameters: Bit(s) Description (Table 0564) 0 door open 1 door unlocked 2 supports raw reading in addition to cooked 3 writable 4 can play audio/video tracks 5 supports interleaving 6 reserved 7 supports prefetch requests 8 supports audio channel control 9 supports Red Book addressing in addition to HSG 10 audio is playing */ static struct rminfo { long EDI; long ESI; long EBP; long reserved_by_system; long EBX; long EDX; long ECX; long EAX; short flags; short ES,DS,FS,GS,IP,CS,SP,SS; } RMI; static union REGS inregs, outregs; static struct SREGS sregs; static short lowptr; static int lowptr2; static void *watptr, *watptr2; void allocbuffers (void) { memset (&sregs, 0, sizeof(sregs)); inregs.w.ax = 0x0100; inregs.w.bx = 0x20; int386x (0x31, &inregs, &outregs, &sregs); lowptr = outregs.w.ax; watptr = (void *)((outregs.x.eax & 0xffff) << 4); int386x (0x31, &inregs, &outregs, &sregs); lowptr2 = outregs.w.ax << 16; watptr2 = (void *)((outregs.x.eax & 0xffff) << 4); } void device_request () { memset (&sregs, 0, sizeof (sregs)); memset (&RMI, 0, sizeof(RMI)); RMI.EAX = 0x00001510; RMI.ECX = cdrom_data.first_drive; RMI.EBX = 0; RMI.ES = lowptr; inregs.w.ax = 0x0300; inregs.h.bl = 0x2f; inregs.h.bh = 0; inregs.w.cx = 0; sregs.es = FP_SEG (&RMI); inregs.x.edi = FP_OFF (&RMI); int386x (0x31, &inregs, &outregs, &sregs); if (outregs.x.cflag) printf ("DEVICE REQUEST FAILED!!!\n"); } void red_book (unsigned int value, unsigned char *min, unsigned char *sec, unsigned char *frame) { *frame = value & 0x000000ff; *sec = (value & 0x0000ff00) >> 8; *min = (value & 0x00ff0000) >> 16; } unsigned int hsg (unsigned int value) { unsigned char min, sec, frame; red_book (value, &min, &sec, &frame); value = (unsigned int)min * 4500; value += (short)sec * 75; value += frame; value -= 150; return value; } unsigned int cd_head_position (void) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char media; unsigned int address; unsigned short bytes; unsigned short sector; unsigned int volid; unsigned char unused[4]; } tray_request; struct { unsigned char mode; unsigned char adr_mode; unsigned int address; } head_data; tray_request.length = sizeof (tray_request); tray_request.subunit = 0; tray_request.comcode = 3; tray_request.media = tray_request.sector = tray_request.volid = 0; tray_request.address = lowptr2; tray_request.bytes = 6; head_data.mode = 0x01; head_data.adr_mode = 0x00; memcpy (watptr, &tray_request, tray_request.length); memcpy (watptr2, &head_data, 6); device_request (); memcpy (&tray_request, watptr, tray_request.length); memcpy (&head_data, watptr2, 6); cdrom_data.error = tray_request.status; return head_data.address; } void cd_get_volume (struct volumeinfo *vol) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char media; unsigned int address; unsigned short bytes; unsigned short sector; unsigned int volid; } tray_request; tray_request.length = sizeof (tray_request); tray_request.subunit = 0; tray_request.comcode = 3; tray_request.media = 0; tray_request.media = tray_request.sector = tray_request.volid = 0; tray_request.address = lowptr2; tray_request.bytes = 9; vol->mode = 0x04; memcpy (watptr, &tray_request, sizeof (tray_request)); memcpy (watptr2, vol, sizeof (struct volumeinfo)); device_request (); memcpy (&tray_request, watptr, tray_request.length); memcpy (vol, watptr2, sizeof (struct volumeinfo)); cdrom_data.error = tray_request.status; } void cd_set_volume (struct volumeinfo *vol) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char media; unsigned int address; unsigned short bytes; unsigned char unused[4]; } cd_request; vol->mode = 3; cd_request.length = sizeof (cd_request); cd_request.subunit = 0; cd_request.comcode = 12; cd_request.media = 0; cd_request.address = lowptr2; cd_request.bytes = 9; memcpy (watptr, &cd_request, sizeof (cd_request)); memcpy (watptr2, vol, sizeof (struct volumeinfo)); device_request (); memcpy (&cd_request, watptr, cd_request.length); cdrom_data.error = cd_request.status; } short cd_getupc (void) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char media; unsigned int address; unsigned short bytes; unsigned short sector; unsigned int volid; } tray_request; struct { unsigned char mode; unsigned char adr; unsigned char upc[7]; unsigned char zero; unsigned char aframe; } upc_data; tray_request.length = sizeof (tray_request); tray_request.subunit = 0; tray_request.comcode = 3; tray_request.media = 0; tray_request.media = tray_request.sector = tray_request.volid = 0; tray_request.address = lowptr2; tray_request.bytes = 11; upc_data.mode = 0x0e; upc_data.adr = 2; memcpy (watptr, &tray_request, sizeof (tray_request)); memcpy (watptr2, &upc_data, 11); device_request (); memcpy (&upc_data, watptr2, 11); memcpy (&tray_request, watptr, tray_request.length); cdrom_data.error = tray_request.status; if (upc_data.adr == 0) memset (&upc_data.upc, 0, 7); memcpy (&cdrom_data.upc[0], &upc_data.upc[0], 7); return 1; } void cd_get_audio_info (void) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char media; int address; short bytes; short sector; int volid; } ioctli; struct { unsigned char mode; unsigned char lowest; unsigned char highest; unsigned int address; } track_data; ioctli.length = sizeof (ioctli); ioctli.subunit = 0; ioctli.comcode = 3; ioctli.media = 0; ioctli.sector = 0; ioctli.volid = 0; ioctli.address = (unsigned int)lowptr2; ioctli.bytes = 7; memset (&track_data, 0, 7); track_data.mode = 0x0a; memcpy (watptr, &ioctli, sizeof (ioctli)); memcpy (watptr2, &track_data.mode, 7); memcpy (&track_data, watptr2, sizeof (track_data)); device_request (); memcpy (&track_data, watptr2, sizeof (track_data)); memcpy (&ioctli, watptr, sizeof (ioctli)); memcpy (&cdrom_data.diskid, &track_data.lowest, 6); cdrom_data.low_audio = track_data.lowest; cdrom_data.high_audio = track_data.highest; red_book (track_data.address, &cdrom_data.disk_length_min, &cdrom_data.disk_length_sec, &cdrom_data.disk_length_frames); cdrom_data.endofdisk = hsg (track_data.address); cdrom_data.error = ioctli.status; } void cd_set_track (short tracknum) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char media; unsigned int address; unsigned short bytes; unsigned short sector; unsigned int volid; } tray_request; struct { unsigned char mode; unsigned char track; unsigned int address; unsigned char control; } track_data; tray_request.length = sizeof (tray_request); tray_request.subunit = 0; tray_request.comcode = 3; tray_request.media = 0; tray_request.media = tray_request.sector = tray_request.volid = 0; tray_request.address = lowptr2; tray_request.bytes = 7; track_data.mode = 0x0b; track_data.track = tracknum; memcpy (watptr, &tray_request, sizeof (tray_request)); memcpy (watptr2, &track_data, sizeof (track_data)); device_request (); memcpy (&tray_request, watptr, sizeof (tray_request)); memcpy (&track_data, watptr2, sizeof (track_data)); cdrom_data.error = tray_request.status; cdrom_data.track_position = hsg (track_data.address); cdrom_data.current_track = tracknum; cdrom_data.track_type = track_data.control & TRACK_MASK; } unsigned int get_track_length (short tracknum) { unsigned int start, finish; unsigned short ct; ct = cdrom_data.current_track; cd_set_track (tracknum); start = cdrom_data.track_position; if (tracknum < cdrom_data.high_audio) { cd_set_track (tracknum+1); finish = cdrom_data.track_position; } else finish = cdrom_data.endofdisk; cd_set_track (ct); finish -= start; return finish; } void cd_track_length (short tracknum, unsigned char *min, unsigned char *sec, unsigned char *frame) { unsigned int value; value = get_track_length (tracknum); value -= 150; *frame = value % 75; value -= *frame; value /= 75; *sec = value % 60; value -= *sec; value /= 60; *min = value; } void cd_status (void) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char media; unsigned int address; unsigned short bytes; unsigned short sector; unsigned int volid; } tray_request; struct { unsigned char mode; unsigned int status; } cd_data; tray_request.length = sizeof (tray_request); tray_request.subunit = 0; tray_request.comcode = 3; tray_request.media = 0; tray_request.media = tray_request.sector = tray_request.volid = 0; tray_request.address = lowptr2; tray_request.bytes = 5; cd_data.mode = 0x06; memcpy (watptr, &tray_request, sizeof (tray_request)); memcpy (watptr2, &cd_data, sizeof (cd_data)); device_request (); memcpy (&tray_request, watptr, sizeof (tray_request)); memcpy (&cd_data, watptr2, sizeof (cd_data)); cdrom_data.status = cd_data.status; cdrom_data.error = tray_request.status; } void cd_seek (unsigned int location) { unsigned char min, sec, frame; struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char addressmode; unsigned int transfer; unsigned short sectors; unsigned int seekpos; } play_request; play_request.length = sizeof (play_request); play_request.subunit = 0; play_request.comcode = 131; play_request.addressmode = 0; play_request.transfer = 0; play_request.sectors = 0; play_request.seekpos = location; memcpy (watptr, &play_request, sizeof (play_request)); device_request (); memcpy (&play_request, watptr, sizeof (play_request)); cdrom_data.error = play_request.status; } void cd_play_audio (unsigned int begin, unsigned int end) { unsigned int leng; struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char addressmode; unsigned int start; unsigned int playlength; } play_request; play_request.length = sizeof (play_request); play_request.subunit = 0; play_request.comcode = 132; play_request.addressmode = 0; play_request.start = begin; play_request.playlength = end-begin; memcpy (watptr, &play_request, sizeof (play_request)); device_request (); memcpy (&play_request, watptr, sizeof (play_request)); cdrom_data.error = play_request.status; } void cd_stop_audio (void) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; } stop_request; stop_request.length = sizeof (stop_request); stop_request.subunit = 0; stop_request.comcode = 133; memcpy (watptr, &stop_request, sizeof (stop_request)); device_request (); memcpy (&stop_request, watptr, sizeof (stop_request)); cdrom_data.error = stop_request.status; } void cd_resume_audio (void) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; } stop_request; stop_request.length = sizeof (stop_request); stop_request.subunit = 0; stop_request.comcode = 136; memcpy (watptr, &stop_request, sizeof (stop_request)); device_request (); memcpy (&stop_request, watptr, sizeof (stop_request)); cdrom_data.error = stop_request.status; } void cd_cmd (unsigned char mode) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char media; unsigned int address; unsigned short bytes; unsigned char unused[4]; } tray_request; unsigned char cd_mode; cd_mode = mode; tray_request.length = sizeof (tray_request); tray_request.subunit = 0; tray_request.comcode = 12; tray_request.media = 0; tray_request.address = lowptr2; tray_request.bytes = 1; memcpy (watptr, &tray_request, sizeof (tray_request)); memcpy (watptr2, &cd_mode, 1); device_request (); memcpy (&tray_request, watptr, sizeof (tray_request)); cdrom_data.error = tray_request.status; } void cd_getpos (struct playinfo *info) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char media; unsigned int address; unsigned short bytes; unsigned short sector; unsigned int volid; } tray_request; tray_request.length = sizeof (tray_request); tray_request.subunit = 0; tray_request.comcode = 3; tray_request.media = 0; tray_request.media = tray_request.sector = tray_request.volid = 0; tray_request.address = lowptr2; tray_request.bytes = 6; info->control = 12; memcpy (watptr, &tray_request, sizeof (tray_request)); memcpy (watptr2, info, sizeof (struct playinfo)); device_request (); memcpy (&tray_request, watptr, sizeof (tray_request)); memcpy (info, watptr2, sizeof (struct playinfo)); cdrom_data.error = tray_request.status; } short cdrom_installed (void) { inregs.h.ah = 0x15; inregs.h.al = 0x00; inregs.w.bx = 0; int386 (0x2f, &inregs, &outregs); if (outregs.w.bx == 0) return (0); cdrom_data.drives = outregs.w.bx; cdrom_data.first_drive = outregs.w.cx; if (lowptr == 0) allocbuffers (); cd_get_audio_info (); return (1); } short cd_done_play (void) { cd_cmd (CLOSE_TRAY); return ((cdrom_data.error & BUSY) == 0); } short cd_mediach (void) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char media; unsigned int address; unsigned short bytes; unsigned short sector; unsigned int volid; } tray_request; struct { unsigned char mode; unsigned char media; } cd_data; tray_request.length = sizeof (tray_request); tray_request.subunit = 0; tray_request.comcode = 3; tray_request.media = 0; tray_request.media = tray_request.sector = tray_request.volid = 0; tray_request.address = lowptr2; tray_request.bytes = 2; cd_data.mode = 0x09; memcpy (watptr, &tray_request, sizeof (tray_request)); memcpy (watptr2, &cd_data, sizeof (cd_data)); device_request (); memcpy (&tray_request, watptr, sizeof (tray_request)); memcpy (&cd_data, watptr2, sizeof (cd_data)); cdrom_data.error = tray_request.status; return cd_data.media; } void cd_lock (unsigned char doormode) { struct { unsigned char length; unsigned char subunit; unsigned char comcode; unsigned short status; char ununsed[8]; unsigned char media; unsigned int address; unsigned short bytes; unsigned char unused[4]; } tray_request; struct { unsigned char mode; unsigned char media; } cd_data; tray_request.length = sizeof (tray_request); tray_request.subunit = 0; tray_request.comcode = 12; tray_request.media = 0; tray_request.address = lowptr2; tray_request.bytes = 2; cd_data.mode = 1; cd_data.media = doormode; memcpy (watptr, &tray_request, sizeof (tray_request)); memcpy (watptr2, &cd_data, sizeof (cd_data)); device_request (); memcpy (&tray_request, watptr, sizeof (tray_request)); cdrom_data.error = tray_request.status; }