ZuluSCSI_cdrom.cpp 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661
  1. /* Advanced CD-ROM drive emulation.
  2. * Adds a few capabilities on top of the SCSI2SD CD-ROM emulation:
  3. *
  4. * - bin/cue support for support of multiple tracks
  5. * - on the fly image switching
  6. *
  7. * SCSI2SD V6 - Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
  8. * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
  9. *
  10. * This file is licensed under the GPL version 3 or any later version. 
  11. * It is derived from cdrom.c in SCSI2SD V6
  12. *
  13. * https://www.gnu.org/licenses/gpl-3.0.html
  14. * ----
  15. * This program is free software: you can redistribute it and/or modify
  16. * it under the terms of the GNU General Public License as published by
  17. * the Free Software Foundation, either version 3 of the License, or
  18. * (at your option) any later version. 
  19. *
  20. * This program is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23. * GNU General Public License for more details. 
  24. *
  25. * You should have received a copy of the GNU General Public License
  26. * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  27. */
  28. #include <string.h>
  29. #include "ZuluSCSI_cdrom.h"
  30. #include "ZuluSCSI_log.h"
  31. #include "ZuluSCSI_config.h"
  32. #include <CUEParser.h>
  33. #include <assert.h>
  34. #ifdef ENABLE_AUDIO_OUTPUT
  35. #include "ZuluSCSI_audio.h"
  36. #endif
  37. extern "C" {
  38. #include <scsi.h>
  39. }
  40. /******************************************/
  41. /* Basic TOC generation without cue sheet */
  42. /******************************************/
  43. static const uint8_t SimpleTOC[] =
  44. {
  45. 0x00, // toc length, MSB
  46. 0x12, // toc length, LSB
  47. 0x01, // First track number
  48. 0x01, // Last track number,
  49. // TRACK 1 Descriptor
  50. 0x00, // reserved
  51. 0x14, // Q sub-channel encodes current position, Digital track
  52. 0x01, // Track 1,
  53. 0x00, // Reserved
  54. 0x00,0x00,0x00,0x00, // Track start sector (LBA)
  55. 0x00, // reserved
  56. 0x14, // Q sub-channel encodes current position, Digital track
  57. 0xAA, // Leadout Track
  58. 0x00, // Reserved
  59. 0x00,0x00,0x00,0x00, // Track start sector (LBA)
  60. };
  61. static const uint8_t LeadoutTOC[] =
  62. {
  63. 0x00, // toc length, MSB
  64. 0x0A, // toc length, LSB
  65. 0x01, // First track number
  66. 0x01, // Last track number,
  67. 0x00, // reserved
  68. 0x14, // Q sub-channel encodes current position, Digital track
  69. 0xAA, // Leadout Track
  70. 0x00, // Reserved
  71. 0x00,0x00,0x00,0x00, // Track start sector (LBA)
  72. };
  73. static const uint8_t SessionTOC[] =
  74. {
  75. 0x00, // toc length, MSB
  76. 0x0A, // toc length, LSB
  77. 0x01, // First session number
  78. 0x01, // Last session number,
  79. // TRACK 1 Descriptor
  80. 0x00, // reserved
  81. 0x14, // Q sub-channel encodes current position, Digital track
  82. 0x01, // First track number in last complete session
  83. 0x00, // Reserved
  84. 0x00,0x00,0x00,0x00 // LBA of first track in last session
  85. };
  86. static const uint8_t FullTOC[] =
  87. {
  88. 0x00, // 0: toc length, MSB
  89. 0x44, // 1: toc length, LSB
  90. 0x01, // 2: First session number
  91. 0x01, // 3: Last session number,
  92. // A0 Descriptor
  93. 0x01, // 4: session number
  94. 0x14, // 5: ADR/Control
  95. 0x00, // 6: TNO
  96. 0xA0, // 7: POINT
  97. 0x00, // 8: Min
  98. 0x00, // 9: Sec
  99. 0x00, // 10: Frame
  100. 0x00, // 11: Zero
  101. 0x01, // 12: First Track number.
  102. 0x00, // 13: Disc type 00 = Mode 1
  103. 0x00, // 14: PFRAME
  104. // A1
  105. 0x01, // 15: session number
  106. 0x14, // 16: ADR/Control
  107. 0x00, // 17: TNO
  108. 0xA1, // 18: POINT
  109. 0x00, // 19: Min
  110. 0x00, // 20: Sec
  111. 0x00, // 21: Frame
  112. 0x00, // 22: Zero
  113. 0x01, // 23: Last Track number
  114. 0x00, // 24: PSEC
  115. 0x00, // 25: PFRAME
  116. // A2
  117. 0x01, // 26: session number
  118. 0x14, // 27: ADR/Control
  119. 0x00, // 28: TNO
  120. 0xA2, // 29: POINT
  121. 0x00, // 30: Min
  122. 0x00, // 31: Sec
  123. 0x00, // 32: Frame
  124. 0x00, // 33: Zero
  125. 0x79, // 34: LEADOUT position BCD
  126. 0x59, // 35: leadout PSEC BCD
  127. 0x74, // 36: leadout PFRAME BCD
  128. // TRACK 1 Descriptor
  129. 0x01, // 37: session number
  130. 0x14, // 38: ADR/Control
  131. 0x00, // 39: TNO
  132. 0x01, // 40: Point
  133. 0x00, // 41: Min
  134. 0x00, // 42: Sec
  135. 0x00, // 43: Frame
  136. 0x00, // 44: Zero
  137. 0x00, // 45: PMIN
  138. 0x00, // 46: PSEC
  139. 0x00, // 47: PFRAME
  140. // b0
  141. 0x01, // 48: session number
  142. 0x54, // 49: ADR/Control
  143. 0x00, // 50: TNO
  144. 0xB1, // 51: POINT
  145. 0x79, // 52: Min BCD
  146. 0x59, // 53: Sec BCD
  147. 0x74, // 54: Frame BCD
  148. 0x00, // 55: Zero
  149. 0x79, // 56: PMIN BCD
  150. 0x59, // 57: PSEC BCD
  151. 0x74, // 58: PFRAME BCD
  152. // c0
  153. 0x01, // 59: session number
  154. 0x54, // 60: ADR/Control
  155. 0x00, // 61: TNO
  156. 0xC0, // 62: POINT
  157. 0x00, // 63: Min
  158. 0x00, // 64: Sec
  159. 0x00, // 65: Frame
  160. 0x00, // 66: Zero
  161. 0x00, // 67: PMIN
  162. 0x00, // 68: PSEC
  163. 0x00 // 69: PFRAME
  164. };
  165. static uint8_t SimpleHeader[] =
  166. {
  167. 0x01, // 2048byte user data, L-EC in 288 byte aux field.
  168. 0x00, // reserved
  169. 0x00, // reserved
  170. 0x00, // reserved
  171. 0x00,0x00,0x00,0x00 // Track start sector (LBA or MSF)
  172. };
  173. static const uint8_t DiscInformation[] =
  174. {
  175. 0x00, // 0: disc info length, MSB
  176. 0x20, // 1: disc info length, LSB
  177. 0x0E, // 2: disc status (finalized, single session non-rewritable)
  178. 0x01, // 3: first track number
  179. 0x01, // 4: number of sessions (LSB)
  180. 0x01, // 5: first track in last session (LSB)
  181. 0x01, // 6: last track in last session (LSB)
  182. 0x00, // 7: format status (0x00 = non-rewritable, no barcode, no disc id)
  183. 0x00, // 8: disc type (0x00 = CD-ROM)
  184. 0x00, // 9: number of sessions (MSB)
  185. 0x00, // 10: first track in last session (MSB)
  186. 0x00, // 11: last track in last session (MSB)
  187. 0x00, // 12: disc ID (MSB)
  188. 0x00, // 13: .
  189. 0x00, // 14: .
  190. 0x00, // 15: disc ID (LSB)
  191. 0x00, // 16: last session lead-in start (MSB)
  192. 0x00, // 17: .
  193. 0x00, // 18: .
  194. 0x00, // 19: last session lead-in start (LSB)
  195. 0x00, // 20: last possible lead-out start (MSB)
  196. 0x00, // 21: .
  197. 0x00, // 22: .
  198. 0x00, // 23: last possible lead-out start (LSB)
  199. 0x00, // 24: disc bar code (MSB)
  200. 0x00, // 25: .
  201. 0x00, // 26: .
  202. 0x00, // 27: .
  203. 0x00, // 28: .
  204. 0x00, // 29: .
  205. 0x00, // 30: .
  206. 0x00, // 31: disc bar code (LSB)
  207. 0x00, // 32: disc application code
  208. 0x00, // 33: number of opc tables
  209. };
  210. // Convert logical block address to CD-ROM time
  211. static void LBA2MSF(uint32_t LBA, uint8_t* MSF)
  212. {
  213. MSF[2] = LBA % 75; // Frames
  214. uint32_t rem = LBA / 75;
  215. MSF[1] = rem % 60; // Seconds
  216. MSF[0] = rem / 60; // Minutes
  217. }
  218. // Convert logical block address to CD-ROM time in binary coded decimal format
  219. static void LBA2MSFBCD(uint32_t LBA, uint8_t* MSF)
  220. {
  221. uint8_t fra = LBA % 75;
  222. uint32_t rem = LBA / 75;
  223. uint8_t sec = rem % 60;
  224. uint8_t min = rem / 60;
  225. MSF[0] = ((min / 10) << 4) | (min % 10);
  226. MSF[1] = ((sec / 10) << 4) | (sec % 10);
  227. MSF[2] = ((fra / 10) << 4) | (fra % 10);
  228. }
  229. // Convert CD-ROM time to logical block address
  230. static uint32_t MSF2LBA(uint8_t m, uint8_t s, uint8_t f)
  231. {
  232. uint32_t lba = (m * 60 + s) * 75 + f;
  233. return lba;
  234. }
  235. static void doReadTOCSimple(bool MSF, uint8_t track, uint16_t allocationLength)
  236. {
  237. if (track == 0xAA)
  238. {
  239. // 0xAA requests only lead-out track information (reports capacity)
  240. uint32_t len = sizeof(LeadoutTOC);
  241. memcpy(scsiDev.data, LeadoutTOC, len);
  242. uint32_t capacity = getScsiCapacity(
  243. scsiDev.target->cfg->sdSectorStart,
  244. scsiDev.target->liveCfg.bytesPerSector,
  245. scsiDev.target->cfg->scsiSectors);
  246. // Replace start of leadout track
  247. if (MSF)
  248. {
  249. scsiDev.data[8] = 0;
  250. LBA2MSF(capacity, scsiDev.data + 9);
  251. }
  252. else
  253. {
  254. scsiDev.data[8] = capacity >> 24;
  255. scsiDev.data[9] = capacity >> 16;
  256. scsiDev.data[10] = capacity >> 8;
  257. scsiDev.data[11] = capacity;
  258. }
  259. if (len > allocationLength)
  260. {
  261. len = allocationLength;
  262. }
  263. scsiDev.dataLen = len;
  264. scsiDev.phase = DATA_IN;
  265. }
  266. else if (track <= 1)
  267. {
  268. // We only support track 1.
  269. // track 0 means "return all tracks"
  270. uint32_t len = sizeof(SimpleTOC);
  271. memcpy(scsiDev.data, SimpleTOC, len);
  272. uint32_t capacity = getScsiCapacity(
  273. scsiDev.target->cfg->sdSectorStart,
  274. scsiDev.target->liveCfg.bytesPerSector,
  275. scsiDev.target->cfg->scsiSectors);
  276. // Replace start of leadout track
  277. if (MSF)
  278. {
  279. scsiDev.data[0x10] = 0;
  280. LBA2MSF(capacity, scsiDev.data + 0x11);
  281. }
  282. else
  283. {
  284. scsiDev.data[0x10] = capacity >> 24;
  285. scsiDev.data[0x11] = capacity >> 16;
  286. scsiDev.data[0x12] = capacity >> 8;
  287. scsiDev.data[0x13] = capacity;
  288. }
  289. if (len > allocationLength)
  290. {
  291. len = allocationLength;
  292. }
  293. scsiDev.dataLen = len;
  294. scsiDev.phase = DATA_IN;
  295. }
  296. else
  297. {
  298. scsiDev.status = CHECK_CONDITION;
  299. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  300. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  301. scsiDev.phase = STATUS;
  302. }
  303. }
  304. static void doReadSessionInfoSimple(uint8_t session, uint16_t allocationLength)
  305. {
  306. uint32_t len = sizeof(SessionTOC);
  307. memcpy(scsiDev.data, SessionTOC, len);
  308. if (len > allocationLength)
  309. {
  310. len = allocationLength;
  311. }
  312. scsiDev.dataLen = len;
  313. scsiDev.phase = DATA_IN;
  314. }
  315. static inline uint8_t
  316. fromBCD(uint8_t val)
  317. {
  318. return ((val >> 4) * 10) + (val & 0xF);
  319. }
  320. static void doReadFullTOCSimple(int convertBCD, uint8_t session, uint16_t allocationLength)
  321. {
  322. // We only support session 1.
  323. if (session > 1)
  324. {
  325. scsiDev.status = CHECK_CONDITION;
  326. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  327. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  328. scsiDev.phase = STATUS;
  329. }
  330. else
  331. {
  332. uint32_t len = sizeof(FullTOC);
  333. memcpy(scsiDev.data, FullTOC, len);
  334. if (convertBCD)
  335. {
  336. int descriptor = 4;
  337. while (descriptor < len)
  338. {
  339. int i;
  340. for (i = 0; i < 7; ++i)
  341. {
  342. scsiDev.data[descriptor + i] =
  343. fromBCD(scsiDev.data[descriptor + 4 + i]);
  344. }
  345. descriptor += 11;
  346. }
  347. }
  348. if (len > allocationLength)
  349. {
  350. len = allocationLength;
  351. }
  352. scsiDev.dataLen = len;
  353. scsiDev.phase = DATA_IN;
  354. }
  355. }
  356. void doReadHeaderSimple(bool MSF, uint32_t lba, uint16_t allocationLength)
  357. {
  358. uint32_t len = sizeof(SimpleHeader);
  359. memcpy(scsiDev.data, SimpleHeader, len);
  360. if (len > allocationLength)
  361. {
  362. len = allocationLength;
  363. }
  364. scsiDev.dataLen = len;
  365. scsiDev.phase = DATA_IN;
  366. }
  367. void doReadDiscInformationSimple(uint16_t allocationLength)
  368. {
  369. uint32_t len = sizeof(DiscInformation);
  370. memcpy(scsiDev.data, DiscInformation, len);
  371. if (len > allocationLength)
  372. {
  373. len = allocationLength;
  374. }
  375. scsiDev.dataLen = len;
  376. scsiDev.phase = DATA_IN;
  377. }
  378. /*********************************/
  379. /* TOC generation from cue sheet */
  380. /*********************************/
  381. // Fetch track info based on LBA
  382. static void getTrackFromLBA(CUEParser &parser, uint32_t lba, CUETrackInfo *result)
  383. {
  384. // Track info in case we have no .cue file
  385. result->file_mode = CUEFile_BINARY;
  386. result->track_mode = CUETrack_MODE1_2048;
  387. result->sector_length = 2048;
  388. result->track_number = 1;
  389. const CUETrackInfo *tmptrack;
  390. while ((tmptrack = parser.next_track()) != NULL)
  391. {
  392. if (tmptrack->track_start <= lba)
  393. {
  394. *result = *tmptrack;
  395. }
  396. else
  397. {
  398. break;
  399. }
  400. }
  401. }
  402. // Format track info read from cue sheet into the format used by ReadTOC command.
  403. // Refer to T10/1545-D MMC-4 Revision 5a, "Response Format 0000b: Formatted TOC"
  404. static void formatTrackInfo(const CUETrackInfo *track, uint8_t *dest, bool use_MSF_time)
  405. {
  406. uint8_t control_adr = 0x14; // Digital track
  407. if (track->track_mode == CUETrack_AUDIO)
  408. {
  409. control_adr = 0x10; // Audio track
  410. }
  411. dest[0] = 0; // Reserved
  412. dest[1] = control_adr;
  413. dest[2] = track->track_number;
  414. dest[3] = 0; // Reserved
  415. if (use_MSF_time)
  416. {
  417. // Time in minute-second-frame format
  418. dest[4] = 0;
  419. LBA2MSF(track->data_start, &dest[5]);
  420. }
  421. else
  422. {
  423. // Time as logical block address
  424. dest[4] = (track->data_start >> 24) & 0xFF;
  425. dest[5] = (track->data_start >> 16) & 0xFF;
  426. dest[6] = (track->data_start >> 8) & 0xFF;
  427. dest[7] = (track->data_start >> 0) & 0xFF;
  428. }
  429. }
  430. // Load data from CUE sheet for the given device,
  431. // using the second half of scsiDev.data buffer for temporary storage.
  432. // Returns false if no cue sheet or it could not be opened.
  433. static bool loadCueSheet(image_config_t &img, CUEParser &parser)
  434. {
  435. if (!img.cuesheetfile.isOpen())
  436. {
  437. return false;
  438. }
  439. // Use second half of scsiDev.data as the buffer for cue sheet text
  440. size_t halfbufsize = sizeof(scsiDev.data) / 2;
  441. char *cuebuf = (char*)&scsiDev.data[halfbufsize];
  442. img.cuesheetfile.seek(0);
  443. int len = img.cuesheetfile.read(cuebuf, halfbufsize);
  444. if (len <= 0)
  445. {
  446. return false;
  447. }
  448. cuebuf[len] = '\0';
  449. parser = CUEParser(cuebuf);
  450. return true;
  451. }
  452. static void doReadTOC(bool MSF, uint8_t track, uint16_t allocationLength)
  453. {
  454. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  455. CUEParser parser;
  456. if (!loadCueSheet(img, parser))
  457. {
  458. // No CUE sheet, use hardcoded data
  459. return doReadTOCSimple(MSF, track, allocationLength);
  460. }
  461. // Format track info
  462. uint8_t *trackdata = &scsiDev.data[4];
  463. int trackcount = 0;
  464. int firsttrack = -1;
  465. int lasttrack = -1;
  466. const CUETrackInfo *trackinfo;
  467. while ((trackinfo = parser.next_track()) != NULL)
  468. {
  469. if (firsttrack < 0) firsttrack = trackinfo->track_number;
  470. lasttrack = trackinfo->track_number;
  471. if (track <= trackinfo->track_number)
  472. {
  473. formatTrackInfo(trackinfo, &trackdata[8 * trackcount], MSF);
  474. trackcount += 1;
  475. }
  476. }
  477. // Format lead-out track info
  478. CUETrackInfo leadout = {};
  479. leadout.track_number = 0xAA;
  480. leadout.track_mode = CUETrack_MODE1_2048;
  481. leadout.data_start = img.scsiSectors;
  482. formatTrackInfo(&leadout, &trackdata[8 * trackcount], MSF);
  483. trackcount += 1;
  484. // Format response header
  485. uint16_t toc_length = 2 + trackcount * 8;
  486. scsiDev.data[0] = toc_length >> 8;
  487. scsiDev.data[1] = toc_length & 0xFF;
  488. scsiDev.data[2] = firsttrack;
  489. scsiDev.data[3] = lasttrack;
  490. if (track != 0xAA && trackcount < 2)
  491. {
  492. // Unknown track requested
  493. scsiDev.status = CHECK_CONDITION;
  494. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  495. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  496. scsiDev.phase = STATUS;
  497. }
  498. else
  499. {
  500. uint32_t len = 2 + toc_length;
  501. if (len > allocationLength)
  502. {
  503. len = allocationLength;
  504. }
  505. scsiDev.dataLen = len;
  506. scsiDev.phase = DATA_IN;
  507. }
  508. }
  509. static void doReadSessionInfo(uint8_t session, uint16_t allocationLength)
  510. {
  511. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  512. CUEParser parser;
  513. if (!loadCueSheet(img, parser))
  514. {
  515. // No CUE sheet, use hardcoded data
  516. return doReadSessionInfoSimple(session, allocationLength);
  517. }
  518. uint32_t len = sizeof(SessionTOC);
  519. memcpy(scsiDev.data, SessionTOC, len);
  520. // Replace first track info in the session table
  521. // based on data from CUE sheet.
  522. const CUETrackInfo *trackinfo = parser.next_track();
  523. if (trackinfo)
  524. {
  525. formatTrackInfo(trackinfo, &scsiDev.data[4], false);
  526. }
  527. if (len > allocationLength)
  528. {
  529. len = allocationLength;
  530. }
  531. scsiDev.dataLen = len;
  532. scsiDev.phase = DATA_IN;
  533. }
  534. // Format track info read from cue sheet into the format used by ReadFullTOC command.
  535. // Refer to T10/1545-D MMC-4 Revision 5a, "Response Format 0010b: Raw TOC"
  536. static void formatRawTrackInfo(const CUETrackInfo *track, uint8_t *dest)
  537. {
  538. uint8_t control_adr = 0x14; // Digital track
  539. if (track->track_mode == CUETrack_AUDIO)
  540. {
  541. control_adr = 0x10; // Audio track
  542. }
  543. dest[0] = 0x01; // Session always 1
  544. dest[1] = control_adr;
  545. dest[2] = 0x00; // "TNO", always 0?
  546. dest[3] = track->track_number; // "POINT", contains track number
  547. // Next three are ATIME. The spec doesn't directly address how these
  548. // should be reported in the TOC, just giving a description of Q-channel
  549. // data from Red Book/ECMA-130. On all disks tested so far these are
  550. // given as 00/00/00.
  551. dest[4] = 0x00;
  552. dest[5] = 0x00;
  553. dest[6] = 0x00;
  554. dest[7] = 0; // HOUR
  555. LBA2MSFBCD(track->data_start, &dest[8]);
  556. }
  557. static void doReadFullTOC(int convertBCD, uint8_t session, uint16_t allocationLength)
  558. {
  559. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  560. CUEParser parser;
  561. if (!loadCueSheet(img, parser))
  562. {
  563. // No CUE sheet, use hardcoded data
  564. return doReadFullTOCSimple(convertBCD, session, allocationLength);
  565. }
  566. // We only support session 1.
  567. if (session > 1)
  568. {
  569. scsiDev.status = CHECK_CONDITION;
  570. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  571. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  572. scsiDev.phase = STATUS;
  573. return;
  574. }
  575. // Take the beginning of the hardcoded TOC as base
  576. uint32_t len = 4 + 11 * 3; // Header, A0, A1, A2
  577. memcpy(scsiDev.data, FullTOC, len);
  578. // Add track descriptors
  579. int trackcount = 0;
  580. int firsttrack = -1;
  581. int lasttrack = -1;
  582. const CUETrackInfo *trackinfo;
  583. while ((trackinfo = parser.next_track()) != NULL)
  584. {
  585. if (firsttrack < 0) firsttrack = trackinfo->track_number;
  586. lasttrack = trackinfo->track_number;
  587. formatRawTrackInfo(trackinfo, &scsiDev.data[len]);
  588. trackcount += 1;
  589. len += 11;
  590. }
  591. // First and last track numbers
  592. scsiDev.data[12] = firsttrack;
  593. scsiDev.data[23] = lasttrack;
  594. // Leadout track position
  595. LBA2MSFBCD(img.scsiSectors, &scsiDev.data[34]);
  596. // Append recordable disc records b0 and c0 indicating non-recordable disc
  597. memcpy(scsiDev.data + len, &FullTOC[48], 22);
  598. len += 22;
  599. // Correct the record length in header
  600. uint16_t toclen = len - 2;
  601. scsiDev.data[0] = toclen >> 8;
  602. scsiDev.data[1] = toclen & 0xFF;
  603. if (len > allocationLength)
  604. {
  605. len = allocationLength;
  606. }
  607. scsiDev.dataLen = len;
  608. scsiDev.phase = DATA_IN;
  609. }
  610. // SCSI-3 MMC Read Header command, seems to be deprecated in later standards.
  611. // Refer to ANSI X3.304-1997
  612. void doReadHeader(bool MSF, uint32_t lba, uint16_t allocationLength)
  613. {
  614. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  615. #if ENABLE_AUDIO_OUTPUT
  616. // terminate audio playback if active on this target (Annex C)
  617. audio_stop(img.scsiId & 7);
  618. #endif
  619. CUEParser parser;
  620. if (!loadCueSheet(img, parser))
  621. {
  622. // No CUE sheet, use hardcoded data
  623. return doReadHeaderSimple(MSF, lba, allocationLength);
  624. }
  625. // Take the hardcoded header as base
  626. uint32_t len = sizeof(SimpleHeader);
  627. memcpy(scsiDev.data, SimpleHeader, len);
  628. // Search the track with the requested LBA
  629. CUETrackInfo trackinfo = {};
  630. getTrackFromLBA(parser, lba, &trackinfo);
  631. // Track mode (audio / data)
  632. if (trackinfo.track_mode == CUETrack_AUDIO)
  633. {
  634. scsiDev.data[0] = 0;
  635. }
  636. // Track start
  637. if (MSF)
  638. {
  639. scsiDev.data[4] = 0;
  640. LBA2MSF(trackinfo.data_start, &scsiDev.data[5]);
  641. }
  642. else
  643. {
  644. scsiDev.data[4] = (trackinfo.data_start >> 24) & 0xFF;
  645. scsiDev.data[5] = (trackinfo.data_start >> 16) & 0xFF;
  646. scsiDev.data[6] = (trackinfo.data_start >> 8) & 0xFF;
  647. scsiDev.data[7] = (trackinfo.data_start >> 0) & 0xFF;
  648. }
  649. if (len > allocationLength)
  650. {
  651. len = allocationLength;
  652. }
  653. scsiDev.dataLen = len;
  654. scsiDev.phase = DATA_IN;
  655. }
  656. void doReadDiscInformation(uint16_t allocationLength)
  657. {
  658. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  659. CUEParser parser;
  660. if (!loadCueSheet(img, parser))
  661. {
  662. // No CUE sheet, use hardcoded data
  663. return doReadDiscInformationSimple(allocationLength);
  664. }
  665. // Take the hardcoded header as base
  666. uint32_t len = sizeof(DiscInformation);
  667. memcpy(scsiDev.data, DiscInformation, len);
  668. // Find first and last track number
  669. int firsttrack = -1;
  670. int lasttrack = -1;
  671. const CUETrackInfo *trackinfo;
  672. while ((trackinfo = parser.next_track()) != NULL)
  673. {
  674. if (firsttrack < 0) firsttrack = trackinfo->track_number;
  675. lasttrack = trackinfo->track_number;
  676. }
  677. scsiDev.data[3] = firsttrack;
  678. scsiDev.data[5] = firsttrack;
  679. scsiDev.data[6] = lasttrack;
  680. if (len > allocationLength)
  681. {
  682. len = allocationLength;
  683. }
  684. scsiDev.dataLen = len;
  685. scsiDev.phase = DATA_IN;
  686. }
  687. /****************************************/
  688. /* CUE sheet check at image load time */
  689. /****************************************/
  690. bool cdromValidateCueSheet(image_config_t &img)
  691. {
  692. CUEParser parser;
  693. if (!loadCueSheet(img, parser))
  694. {
  695. return false;
  696. }
  697. const CUETrackInfo *trackinfo;
  698. int trackcount = 0;
  699. while ((trackinfo = parser.next_track()) != NULL)
  700. {
  701. trackcount++;
  702. if (trackinfo->track_mode != CUETrack_AUDIO &&
  703. trackinfo->track_mode != CUETrack_MODE1_2048 &&
  704. trackinfo->track_mode != CUETrack_MODE1_2352)
  705. {
  706. logmsg("---- Warning: track ", trackinfo->track_number, " has unsupported mode ", (int)trackinfo->track_mode);
  707. }
  708. if (trackinfo->file_mode != CUEFile_BINARY)
  709. {
  710. logmsg("---- Unsupported CUE data file mode ", (int)trackinfo->file_mode);
  711. }
  712. }
  713. if (trackcount == 0)
  714. {
  715. logmsg("---- Opened cue sheet but no valid tracks found");
  716. return false;
  717. }
  718. logmsg("---- Cue sheet loaded with ", (int)trackcount, " tracks");
  719. return true;
  720. }
  721. /**************************************/
  722. /* Ejection and image switching logic */
  723. /**************************************/
  724. void cdromPerformEject(image_config_t &img)
  725. {
  726. uint8_t target = img.scsiId & 7;
  727. #if ENABLE_AUDIO_OUTPUT
  728. // terminate audio playback if active on this target (MMC-1 Annex C)
  729. audio_stop(target);
  730. #endif
  731. dbgmsg("------ CDROM open tray on ID ", (int)target);
  732. img.ejected = true;
  733. img.cdrom_events = 3; // Media removal
  734. }
  735. // Reinsert any ejected CDROMs on reboot
  736. void cdromReinsertFirstImage(image_config_t &img)
  737. {
  738. if (img.image_index > 0)
  739. {
  740. // Multiple images for this drive, force restart from first one
  741. dbgmsg("---- Restarting from first CD-ROM image");
  742. img.image_index = 9;
  743. cdromSwitchNextImage(img);
  744. }
  745. else if (img.ejected)
  746. {
  747. // Reinsert the single image
  748. dbgmsg("---- Closing CD-ROM tray");
  749. img.ejected = false;
  750. img.cdrom_events = 2; // New media
  751. }
  752. }
  753. // Check if we have multiple CD-ROM images to cycle when drive is ejected.
  754. bool cdromSwitchNextImage(image_config_t &img)
  755. {
  756. // Check if we have a next image to load, so that drive is closed next time the host asks.
  757. img.image_index++;
  758. char filename[MAX_FILE_PATH];
  759. int target_idx = img.scsiId & 7;
  760. if (!scsiDiskGetImageNameFromConfig(img, filename, sizeof(filename)))
  761. {
  762. img.image_index = 0;
  763. scsiDiskGetImageNameFromConfig(img, filename, sizeof(filename));
  764. }
  765. #ifdef ENABLE_AUDIO_OUTPUT
  766. // if in progress for this device, terminate audio playback immediately (Annex C)
  767. audio_stop(target_idx);
  768. // Reset position tracking for the new image
  769. audio_get_status_code(target_idx); // trash audio status code
  770. #endif
  771. if (filename[0] != '\0')
  772. {
  773. logmsg("Switching to next CD-ROM image for ", target_idx, ": ", filename);
  774. img.file.close();
  775. bool status = scsiDiskOpenHDDImage(target_idx, filename, target_idx, 0, 2048);
  776. if (status)
  777. {
  778. img.ejected = false;
  779. img.cdrom_events = 2; // New media
  780. return true;
  781. }
  782. }
  783. return false;
  784. }
  785. static void doGetEventStatusNotification(bool immed)
  786. {
  787. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  788. if (!immed)
  789. {
  790. // Asynchronous notification not supported
  791. scsiDev.status = CHECK_CONDITION;
  792. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  793. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  794. scsiDev.phase = STATUS;
  795. }
  796. else if (img.cdrom_events)
  797. {
  798. scsiDev.data[0] = 0;
  799. scsiDev.data[1] = 6; // EventDataLength
  800. scsiDev.data[2] = 0x04; // Media status events
  801. scsiDev.data[3] = 0x04; // Supported events
  802. scsiDev.data[4] = img.cdrom_events;
  803. scsiDev.data[5] = 0x01; // Power status
  804. scsiDev.data[6] = 0; // Start slot
  805. scsiDev.data[7] = 0; // End slot
  806. scsiDev.dataLen = 8;
  807. scsiDev.phase = DATA_IN;
  808. img.cdrom_events = 0;
  809. if (img.ejected)
  810. {
  811. // We are now reporting to host that the drive is open.
  812. // Simulate a "close" for next time the host polls.
  813. cdromSwitchNextImage(img);
  814. }
  815. }
  816. else
  817. {
  818. scsiDev.data[0] = 0;
  819. scsiDev.data[1] = 2; // EventDataLength
  820. scsiDev.data[2] = 0x00; // Media status events
  821. scsiDev.data[3] = 0x04; // Supported events
  822. scsiDev.dataLen = 4;
  823. scsiDev.phase = DATA_IN;
  824. }
  825. }
  826. /**************************************/
  827. /* CD-ROM audio playback */
  828. /**************************************/
  829. void cdromGetAudioPlaybackStatus(uint8_t *status, uint32_t *current_lba, bool current_only)
  830. {
  831. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  832. #ifdef ENABLE_AUDIO_OUTPUT
  833. if (status) {
  834. uint8_t target = img.scsiId & 7;
  835. if (current_only) {
  836. *status = audio_is_playing(target) ? 1 : 0;
  837. } else {
  838. *status = (uint8_t) audio_get_status_code(target);
  839. }
  840. }
  841. #else
  842. if (status) *status = 0; // audio status code for 'unsupported/invalid' and not-playing indicator
  843. #endif
  844. if (current_lba)
  845. {
  846. if (img.file.isOpen()) {
  847. *current_lba = img.file.position() / 2352;
  848. } else {
  849. *current_lba = 0;
  850. }
  851. }
  852. }
  853. static void doPlayAudio(uint32_t lba, uint32_t length)
  854. {
  855. #ifdef ENABLE_AUDIO_OUTPUT
  856. dbgmsg("------ CD-ROM Play Audio request at ", lba, " for ", length, " sectors");
  857. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  858. uint8_t target_id = img.scsiId & 7;
  859. // Per Annex C terminate playback immediately if already in progress on
  860. // the current target. Non-current targets may also get their audio
  861. // interrupted later due to hardware limitations
  862. audio_stop(img.scsiId & 7);
  863. // if transfer length is zero no audio playback happens.
  864. // don't treat as an error per SCSI-2; handle via short-circuit
  865. if (length == 0)
  866. {
  867. scsiDev.status = 0;
  868. scsiDev.phase = STATUS;
  869. return;
  870. }
  871. // if actual playback is requested perform steps to verify prior to playback
  872. CUEParser parser;
  873. if (loadCueSheet(img, parser))
  874. {
  875. CUETrackInfo trackinfo = {};
  876. getTrackFromLBA(parser, lba, &trackinfo);
  877. if (lba == 0xFFFFFFFF)
  878. {
  879. // request to start playback from 'current position'
  880. lba = img.file.position() / 2352;
  881. }
  882. // --- TODO --- determine proper track offset, software I tested with had a tendency
  883. // to ask for offsets that seem to hint at 2048 here, not the 2352 you'd assume.
  884. // Might be due to a mode page reporting something unexpected? Needs investigation.
  885. uint64_t offset = trackinfo.file_offset + 2048 * (lba - trackinfo.data_start);
  886. dbgmsg("------ Play audio CD: ", (int)length, " sectors starting at ", (int)lba,
  887. ", track number ", trackinfo.track_number, ", data offset in file ", (int)offset);
  888. if (trackinfo.track_mode != CUETrack_AUDIO)
  889. {
  890. dbgmsg("---- Host tried audio playback on track type ", (int)trackinfo.track_mode);
  891. scsiDev.status = CHECK_CONDITION;
  892. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  893. scsiDev.target->sense.asc = 0x6400; // ILLEGAL MODE FOR THIS TRACK
  894. scsiDev.phase = STATUS;
  895. return;
  896. }
  897. // playback request appears to be sane, so perform it
  898. // see earlier note for context on the block length below
  899. if (!audio_play(target_id, &(img.file), offset, offset + length * 2048, false))
  900. {
  901. // Underlying data/media error? Fake a disk scratch, which should
  902. // be a condition most CD-DA players are expecting
  903. scsiDev.status = CHECK_CONDITION;
  904. scsiDev.target->sense.code = MEDIUM_ERROR;
  905. scsiDev.target->sense.asc = 0x1106; // CIRC UNRECOVERED ERROR
  906. scsiDev.phase = STATUS;
  907. return;
  908. }
  909. scsiDev.status = 0;
  910. scsiDev.phase = STATUS;
  911. }
  912. else
  913. {
  914. // virtual drive supports audio, just not with this disk image
  915. dbgmsg("---- Request to play audio on non-audio image");
  916. scsiDev.status = CHECK_CONDITION;
  917. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  918. scsiDev.target->sense.asc = 0x6400; // ILLEGAL MODE FOR THIS TRACK
  919. scsiDev.phase = STATUS;
  920. }
  921. #else
  922. dbgmsg("---- Target does not support audio playback");
  923. // per SCSI-2, targets not supporting audio respond to zero-length
  924. // PLAY AUDIO commands with ILLEGAL REQUEST; this seems to be a check
  925. // performed by at least some audio playback software
  926. scsiDev.status = CHECK_CONDITION;
  927. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  928. scsiDev.target->sense.asc = 0x0000; // NO ADDITIONAL SENSE INFORMATION
  929. scsiDev.phase = STATUS;
  930. #endif
  931. }
  932. static void doPauseResumeAudio(bool resume)
  933. {
  934. #ifdef ENABLE_AUDIO_OUTPUT
  935. logmsg("------ CD-ROM ", resume ? "resume" : "pause", " audio playback");
  936. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  937. uint8_t target_id = img.scsiId & 7;
  938. if (audio_is_playing(target_id))
  939. {
  940. audio_set_paused(target_id, !resume);
  941. scsiDev.status = 0;
  942. scsiDev.phase = STATUS;
  943. }
  944. else
  945. {
  946. scsiDev.status = CHECK_CONDITION;
  947. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  948. scsiDev.target->sense.asc = 0x2C00; // COMMAND SEQUENCE ERROR
  949. scsiDev.phase = STATUS;
  950. }
  951. #else
  952. dbgmsg("---- Target does not support audio pausing");
  953. scsiDev.status = CHECK_CONDITION;
  954. scsiDev.target->sense.code = ILLEGAL_REQUEST; // assumed from PLAY AUDIO(10)
  955. scsiDev.target->sense.asc = 0x0000; // NO ADDITIONAL SENSE INFORMATION
  956. scsiDev.phase = STATUS;
  957. #endif
  958. }
  959. static void doStopAudio()
  960. {
  961. dbgmsg("------ CD-ROM Stop Audio request");
  962. #ifdef ENABLE_AUDIO_OUTPUT
  963. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  964. uint8_t target_id = img.scsiId & 7;
  965. audio_stop(target_id);
  966. #endif
  967. }
  968. static void doMechanismStatus(uint16_t allocation_length)
  969. {
  970. uint8_t *buf = scsiDev.data;
  971. uint8_t status;
  972. uint32_t lba;
  973. cdromGetAudioPlaybackStatus(&status, &lba, true);
  974. *buf++ = 0x00; // No fault state
  975. *buf++ = (status) ? 0x20 : 0x00; // Currently playing?
  976. *buf++ = (lba >> 16) & 0xFF;
  977. *buf++ = (lba >> 8) & 0xFF;
  978. *buf++ = (lba >> 0) & 0xFF;
  979. *buf++ = 0; // No CD changer
  980. *buf++ = 0;
  981. *buf++ = 0;
  982. int len = 8;
  983. if (len > allocation_length) len = allocation_length;
  984. scsiDev.dataLen = len;
  985. scsiDev.phase = DATA_IN;
  986. }
  987. /*******************************************/
  988. /* CD-ROM data reading in low level format */
  989. /*******************************************/
  990. static void doReadCD(uint32_t lba, uint32_t length, uint8_t sector_type,
  991. uint8_t main_channel, uint8_t sub_channel)
  992. {
  993. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  994. #if ENABLE_AUDIO_OUTPUT
  995. // terminate audio playback if active on this target (Annex C)
  996. audio_stop(img.scsiId & 7);
  997. #endif
  998. CUEParser parser;
  999. if (!loadCueSheet(img, parser)
  1000. && (sector_type == 0 || sector_type == 2)
  1001. && main_channel == 0x10 && sub_channel == 0)
  1002. {
  1003. // Simple case, return sector data directly
  1004. scsiDiskStartRead(lba, length);
  1005. return;
  1006. }
  1007. // Search the track with the requested LBA
  1008. // Supplies dummy data if no cue sheet is active.
  1009. CUETrackInfo trackinfo = {};
  1010. getTrackFromLBA(parser, lba, &trackinfo);
  1011. // Figure out the data offset in the file
  1012. uint64_t offset = trackinfo.file_offset + trackinfo.sector_length * (lba - trackinfo.data_start);
  1013. dbgmsg("------ Read CD: ", (int)length, " sectors starting at ", (int)lba,
  1014. ", track number ", trackinfo.track_number, ", sector size ", (int)trackinfo.sector_length,
  1015. ", main channel ", main_channel, ", sub channel ", sub_channel,
  1016. ", data offset in file ", (int)offset);
  1017. // Verify sector type
  1018. if (sector_type != 0)
  1019. {
  1020. bool sector_type_ok = false;
  1021. if (sector_type == 1 && trackinfo.track_mode == CUETrack_AUDIO)
  1022. {
  1023. sector_type_ok = true;
  1024. }
  1025. else if (sector_type == 2 && trackinfo.track_mode == CUETrack_MODE1_2048)
  1026. {
  1027. sector_type_ok = true;
  1028. }
  1029. if (!sector_type_ok)
  1030. {
  1031. dbgmsg("---- Failed sector type check, host requested ", (int)sector_type, " CUE file has ", (int)trackinfo.track_mode);
  1032. scsiDev.status = CHECK_CONDITION;
  1033. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  1034. scsiDev.target->sense.asc = 0x6400; // ILLEGAL MODE FOR THIS TRACK
  1035. scsiDev.phase = STATUS;
  1036. return;
  1037. }
  1038. }
  1039. // Select fields to transfer
  1040. // Refer to table 351 in T10/1545-D MMC-4 Revision 5a
  1041. // Only the mandatory cases are supported.
  1042. int sector_length = 0;
  1043. int skip_begin = 0;
  1044. bool add_fake_headers = false;
  1045. if (main_channel == 0)
  1046. {
  1047. // No actual data requested, just sector type check or subchannel
  1048. sector_length = 0;
  1049. }
  1050. else if (trackinfo.track_mode == CUETrack_AUDIO)
  1051. {
  1052. // Transfer whole 2352 byte audio sectors from file to host
  1053. sector_length = 2352;
  1054. }
  1055. else if (trackinfo.track_mode == CUETrack_MODE1_2048 && main_channel == 0x10)
  1056. {
  1057. // Transfer whole 2048 byte data sectors from file to host
  1058. sector_length = 2048;
  1059. }
  1060. else if (trackinfo.track_mode == CUETrack_MODE1_2048 && (main_channel & 0xB8) == 0xB8)
  1061. {
  1062. // Transfer 2048 bytes of data from file and fake the headers
  1063. sector_length = 2048;
  1064. add_fake_headers = true;
  1065. dbgmsg("------ Host requested ECC data but image file lacks it, replacing with zeros");
  1066. }
  1067. else if (trackinfo.track_mode == CUETrack_MODE1_2352 && main_channel == 0x10)
  1068. {
  1069. // Transfer the 2048 byte payload of data sector to host.
  1070. sector_length = 2048;
  1071. skip_begin = 16;
  1072. }
  1073. else if (trackinfo.track_mode == CUETrack_MODE1_2352 && (main_channel & 0xB8) == 0xB8)
  1074. {
  1075. // Transfer whole 2352 byte data sector with ECC to host
  1076. sector_length = 2352;
  1077. }
  1078. else
  1079. {
  1080. dbgmsg("---- Unsupported channel request for track type ", (int)trackinfo.track_mode);
  1081. scsiDev.status = CHECK_CONDITION;
  1082. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  1083. scsiDev.target->sense.asc = 0x6400; // ILLEGAL MODE FOR THIS TRACK
  1084. scsiDev.phase = STATUS;
  1085. return;
  1086. }
  1087. bool field_q_subchannel = false;
  1088. if (sub_channel == 2)
  1089. {
  1090. // Include position information in Q subchannel
  1091. field_q_subchannel = true;
  1092. }
  1093. else if (sub_channel != 0)
  1094. {
  1095. dbgmsg("---- Unsupported subchannel request");
  1096. scsiDev.status = CHECK_CONDITION;
  1097. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  1098. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  1099. scsiDev.phase = STATUS;
  1100. return;
  1101. }
  1102. scsiDev.phase = DATA_IN;
  1103. scsiDev.dataLen = 0;
  1104. scsiDev.dataPtr = 0;
  1105. scsiEnterPhase(DATA_IN);
  1106. // Use two buffers alternately for formatting sector data
  1107. uint32_t result_length = sector_length + (field_q_subchannel ? 16 : 0) + (add_fake_headers ? 304 : 0);
  1108. uint8_t *buf0 = scsiDev.data;
  1109. uint8_t *buf1 = scsiDev.data + result_length;
  1110. // Format the sectors for transfer
  1111. for (uint32_t idx = 0; idx < length; idx++)
  1112. {
  1113. platform_poll();
  1114. diskEjectButtonUpdate(false);
  1115. img.file.seek(offset + idx * trackinfo.sector_length + skip_begin);
  1116. // Verify that previous write using this buffer has finished
  1117. uint8_t *buf = ((idx & 1) ? buf1 : buf0);
  1118. uint8_t *bufstart = buf;
  1119. uint32_t start = millis();
  1120. while (!scsiIsWriteFinished(buf + result_length - 1) && !scsiDev.resetFlag)
  1121. {
  1122. if ((uint32_t)(millis() - start) > 5000)
  1123. {
  1124. logmsg("doReadCD() timeout waiting for previous to finish");
  1125. scsiDev.resetFlag = 1;
  1126. }
  1127. platform_poll();
  1128. diskEjectButtonUpdate(false);
  1129. }
  1130. if (scsiDev.resetFlag) break;
  1131. if (add_fake_headers)
  1132. {
  1133. // 12-byte data sector sync pattern
  1134. *buf++ = 0x00;
  1135. for (int i = 0; i < 10; i++)
  1136. {
  1137. *buf++ = 0xFF;
  1138. }
  1139. *buf++ = 0x00;
  1140. // 4-byte data sector header
  1141. LBA2MSFBCD(lba + idx, buf);
  1142. buf += 3;
  1143. *buf++ = 0x01; // Mode 1
  1144. }
  1145. if (sector_length > 0)
  1146. {
  1147. // User data
  1148. img.file.read(buf, sector_length);
  1149. buf += sector_length;
  1150. }
  1151. if (add_fake_headers)
  1152. {
  1153. // 288 bytes of ECC
  1154. memset(buf, 0, 288);
  1155. buf += 288;
  1156. }
  1157. if (field_q_subchannel)
  1158. {
  1159. // Formatted Q subchannel data
  1160. // Refer to table 354 in T10/1545-D MMC-4 Revision 5a
  1161. *buf++ = (trackinfo.track_mode == CUETrack_AUDIO ? 0x10 : 0x14); // Control & ADR
  1162. *buf++ = trackinfo.track_number;
  1163. *buf++ = (lba + idx >= trackinfo.data_start) ? 1 : 0; // Index number (0 = pregap)
  1164. LBA2MSF(lba + idx, buf); buf += 3;
  1165. *buf++ = 0;
  1166. LBA2MSF(lba + idx, buf); buf += 3;
  1167. *buf++ = 0; *buf++ = 0; // CRC (optional)
  1168. *buf++ = 0; *buf++ = 0; *buf++ = 0; // (pad)
  1169. *buf++ = 0; // No P subchannel
  1170. }
  1171. assert(buf == bufstart + result_length);
  1172. scsiStartWrite(bufstart, result_length);
  1173. }
  1174. scsiFinishWrite();
  1175. scsiDev.status = 0;
  1176. scsiDev.phase = STATUS;
  1177. }
  1178. static void doReadSubchannel(bool time, bool subq, uint8_t parameter, uint8_t track_number, uint16_t allocation_length)
  1179. {
  1180. uint8_t *buf = scsiDev.data;
  1181. if (parameter == 0x01)
  1182. {
  1183. uint8_t audiostatus;
  1184. uint32_t lba;
  1185. cdromGetAudioPlaybackStatus(&audiostatus, &lba, false);
  1186. dbgmsg("------ Get audio playback position: status ", (int)audiostatus, " lba ", (int)lba);
  1187. // Fetch current track info
  1188. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  1189. CUEParser parser;
  1190. CUETrackInfo trackinfo = {};
  1191. loadCueSheet(img, parser);
  1192. getTrackFromLBA(parser, lba, &trackinfo);
  1193. // Request sub channel data at current playback position
  1194. *buf++ = 0; // Reserved
  1195. *buf++ = audiostatus;
  1196. int len;
  1197. if (subq)
  1198. {
  1199. len = 12;
  1200. *buf++ = 0; // Subchannel data length (MSB)
  1201. *buf++ = len; // Subchannel data length (LSB)
  1202. *buf++ = 0x01; // Subchannel data format
  1203. *buf++ = (trackinfo.track_mode == CUETrack_AUDIO ? 0x10 : 0x14);
  1204. *buf++ = trackinfo.track_number;
  1205. *buf++ = (lba >= trackinfo.data_start) ? 1 : 0; // Index number (0 = pregap)
  1206. if (time)
  1207. {
  1208. *buf++ = 0;
  1209. LBA2MSF(lba, buf);
  1210. dbgmsg("------ ABS M ", *buf, " S ", *(buf+1), " F ", *(buf+2));
  1211. buf += 3;
  1212. }
  1213. else
  1214. {
  1215. *buf++ = (lba >> 24) & 0xFF; // Absolute block address
  1216. *buf++ = (lba >> 16) & 0xFF;
  1217. *buf++ = (lba >> 8) & 0xFF;
  1218. *buf++ = (lba >> 0) & 0xFF;
  1219. }
  1220. uint32_t relpos = (uint32_t)((int32_t)lba - (int32_t)trackinfo.data_start);
  1221. if (time)
  1222. {
  1223. *buf++ = 0;
  1224. LBA2MSF(relpos, buf);
  1225. dbgmsg("------ REL M ", *buf, " S ", *(buf+1), " F ", *(buf+2));
  1226. buf += 3;
  1227. }
  1228. else
  1229. {
  1230. *buf++ = (relpos >> 24) & 0xFF; // Track relative position (may be negative)
  1231. *buf++ = (relpos >> 16) & 0xFF;
  1232. *buf++ = (relpos >> 8) & 0xFF;
  1233. *buf++ = (relpos >> 0) & 0xFF;
  1234. }
  1235. }
  1236. else
  1237. {
  1238. len = 0;
  1239. *buf++ = 0;
  1240. *buf++ = 0;
  1241. }
  1242. len += 4;
  1243. if (len > allocation_length) len = allocation_length;
  1244. scsiDev.dataLen = len;
  1245. scsiDev.phase = DATA_IN;
  1246. }
  1247. else
  1248. {
  1249. dbgmsg("---- Unsupported subchannel request");
  1250. scsiDev.status = CHECK_CONDITION;
  1251. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  1252. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  1253. scsiDev.phase = STATUS;
  1254. return;
  1255. }
  1256. }
  1257. /**************************************/
  1258. /* CD-ROM command dispatching */
  1259. /**************************************/
  1260. // Handle direct-access scsi device commands
  1261. extern "C" int scsiCDRomCommand()
  1262. {
  1263. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  1264. int commandHandled = 1;
  1265. uint8_t command = scsiDev.cdb[0];
  1266. if (command == 0x1B)
  1267. {
  1268. if ((scsiDev.cdb[4] & 2))
  1269. {
  1270. // CD-ROM load & eject
  1271. int start = scsiDev.cdb[4] & 1;
  1272. if (start)
  1273. {
  1274. dbgmsg("------ CDROM close tray on ID ", (int)(img.scsiId & 7));
  1275. img.ejected = false;
  1276. img.cdrom_events = 2; // New media
  1277. }
  1278. else
  1279. {
  1280. cdromPerformEject(img);
  1281. }
  1282. }
  1283. else
  1284. {
  1285. // flow through to disk handler
  1286. commandHandled = 0;
  1287. }
  1288. }
  1289. else if (command == 0x43)
  1290. {
  1291. // CD-ROM Read TOC
  1292. bool MSF = (scsiDev.cdb[1] & 0x02);
  1293. uint8_t track = scsiDev.cdb[6];
  1294. uint16_t allocationLength =
  1295. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1296. scsiDev.cdb[8];
  1297. // Reject MMC commands for now, otherwise the TOC data format
  1298. // won't be understood.
  1299. // The "format" field is reserved for SCSI-2
  1300. uint8_t format = scsiDev.cdb[2] & 0x0F;
  1301. switch (format)
  1302. {
  1303. case 0: doReadTOC(MSF, track, allocationLength); break; // SCSI-2
  1304. case 1: doReadSessionInfo(MSF, allocationLength); break; // MMC2
  1305. case 2: doReadFullTOC(0, track, allocationLength); break; // MMC2
  1306. case 3: doReadFullTOC(1, track, allocationLength); break; // MMC2
  1307. default:
  1308. {
  1309. scsiDev.status = CHECK_CONDITION;
  1310. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  1311. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  1312. scsiDev.phase = STATUS;
  1313. }
  1314. }
  1315. }
  1316. else if (command == 0x44)
  1317. {
  1318. // CD-ROM Read Header
  1319. bool MSF = (scsiDev.cdb[1] & 0x02);
  1320. uint32_t lba = 0; // IGNORED for now
  1321. uint16_t allocationLength =
  1322. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1323. scsiDev.cdb[8];
  1324. doReadHeader(MSF, lba, allocationLength);
  1325. }
  1326. else if (command == 0x51)
  1327. {
  1328. uint16_t allocationLength =
  1329. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1330. scsiDev.cdb[8];
  1331. doReadDiscInformation(allocationLength);
  1332. }
  1333. else if (command == 0x4A)
  1334. {
  1335. // Get event status notifications (media change notifications)
  1336. bool immed = scsiDev.cdb[1] & 1;
  1337. doGetEventStatusNotification(immed);
  1338. }
  1339. else if (command == 0x45)
  1340. {
  1341. // PLAY AUDIO (10)
  1342. uint32_t lba =
  1343. (((uint32_t) scsiDev.cdb[2]) << 24) +
  1344. (((uint32_t) scsiDev.cdb[3]) << 16) +
  1345. (((uint32_t) scsiDev.cdb[4]) << 8) +
  1346. scsiDev.cdb[5];
  1347. uint32_t blocks =
  1348. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1349. scsiDev.cdb[8];
  1350. doPlayAudio(lba, blocks);
  1351. }
  1352. else if (command == 0xA5)
  1353. {
  1354. // PLAY AUDIO (12)
  1355. uint32_t lba =
  1356. (((uint32_t) scsiDev.cdb[2]) << 24) +
  1357. (((uint32_t) scsiDev.cdb[3]) << 16) +
  1358. (((uint32_t) scsiDev.cdb[4]) << 8) +
  1359. scsiDev.cdb[5];
  1360. uint32_t blocks =
  1361. (((uint32_t) scsiDev.cdb[6]) << 24) +
  1362. (((uint32_t) scsiDev.cdb[7]) << 16) +
  1363. (((uint32_t) scsiDev.cdb[8]) << 8) +
  1364. scsiDev.cdb[9];
  1365. doPlayAudio(lba, blocks);
  1366. }
  1367. else if (command == 0x47)
  1368. {
  1369. // PLAY AUDIO (MSF)
  1370. uint32_t start = MSF2LBA(scsiDev.cdb[3], scsiDev.cdb[4], scsiDev.cdb[5]);
  1371. uint32_t end = MSF2LBA(scsiDev.cdb[6], scsiDev.cdb[7], scsiDev.cdb[8]);
  1372. uint32_t lba = start;
  1373. if (scsiDev.cdb[3] == 0xFF
  1374. && scsiDev.cdb[4] == 0xFF
  1375. && scsiDev.cdb[5] == 0xFF)
  1376. {
  1377. // request to start playback from 'current position'
  1378. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  1379. lba = img.file.position() / 2352;
  1380. }
  1381. uint32_t length = end - lba;
  1382. doPlayAudio(lba, length);
  1383. }
  1384. else if (command == 0x4B)
  1385. {
  1386. // PAUSE/RESUME AUDIO
  1387. doPauseResumeAudio(scsiDev.cdb[8] & 1);
  1388. }
  1389. else if (command == 0xBD)
  1390. {
  1391. // Mechanism status
  1392. uint16_t allocationLength = (((uint32_t) scsiDev.cdb[8]) << 8) + scsiDev.cdb[9];
  1393. doMechanismStatus(allocationLength);
  1394. }
  1395. else if (command == 0xBB)
  1396. {
  1397. // Set CD speed (just ignored)
  1398. scsiDev.status = 0;
  1399. scsiDev.phase = STATUS;
  1400. }
  1401. else if (command == 0xBE)
  1402. {
  1403. // ReadCD (in low level format)
  1404. uint8_t sector_type = (scsiDev.cdb[1] >> 2) & 7;
  1405. uint32_t lba =
  1406. (((uint32_t) scsiDev.cdb[2]) << 24) +
  1407. (((uint32_t) scsiDev.cdb[3]) << 16) +
  1408. (((uint32_t) scsiDev.cdb[4]) << 8) +
  1409. scsiDev.cdb[5];
  1410. uint32_t blocks =
  1411. (((uint32_t) scsiDev.cdb[6]) << 16) +
  1412. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1413. (((uint32_t) scsiDev.cdb[8]));
  1414. uint8_t main_channel = scsiDev.cdb[9];
  1415. uint8_t sub_channel = scsiDev.cdb[10];
  1416. doReadCD(lba, blocks, sector_type, main_channel, sub_channel);
  1417. }
  1418. else if (command == 0xB9)
  1419. {
  1420. // ReadCD MSF
  1421. uint8_t sector_type = (scsiDev.cdb[1] >> 2) & 7;
  1422. uint32_t start = MSF2LBA(scsiDev.cdb[3], scsiDev.cdb[4], scsiDev.cdb[5]);
  1423. uint32_t end = MSF2LBA(scsiDev.cdb[6], scsiDev.cdb[7], scsiDev.cdb[8]);
  1424. uint8_t main_channel = scsiDev.cdb[9];
  1425. uint8_t sub_channel = scsiDev.cdb[10];
  1426. doReadCD(start, end - start, sector_type, main_channel, sub_channel);
  1427. }
  1428. else if (command == 0x42)
  1429. {
  1430. // Read subchannel data
  1431. bool time = (scsiDev.cdb[1] & 0x02);
  1432. bool subq = (scsiDev.cdb[2] & 0x40);
  1433. uint8_t parameter = scsiDev.cdb[3];
  1434. uint8_t track_number = scsiDev.cdb[6];
  1435. uint16_t allocationLength = (((uint32_t) scsiDev.cdb[7]) << 8) + scsiDev.cdb[8];
  1436. doReadSubchannel(time, subq, parameter, track_number, allocationLength);
  1437. }
  1438. else if (command == 0x28)
  1439. {
  1440. // READ(10) for CDs (may need sector translation for cue file handling)
  1441. uint32_t lba =
  1442. (((uint32_t) scsiDev.cdb[2]) << 24) +
  1443. (((uint32_t) scsiDev.cdb[3]) << 16) +
  1444. (((uint32_t) scsiDev.cdb[4]) << 8) +
  1445. scsiDev.cdb[5];
  1446. uint32_t blocks =
  1447. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1448. scsiDev.cdb[8];
  1449. doReadCD(lba, blocks, 0, 0x10, 0);
  1450. }
  1451. else if (command == 0xA8)
  1452. {
  1453. // READ(12) for CDs (may need sector translation for cue file handling)
  1454. uint32_t lba =
  1455. (((uint32_t) scsiDev.cdb[2]) << 24) +
  1456. (((uint32_t) scsiDev.cdb[3]) << 16) +
  1457. (((uint32_t) scsiDev.cdb[4]) << 8) +
  1458. scsiDev.cdb[5];
  1459. uint32_t blocks =
  1460. (((uint32_t) scsiDev.cdb[6]) << 24) +
  1461. (((uint32_t) scsiDev.cdb[7]) << 16) +
  1462. (((uint32_t) scsiDev.cdb[8]) << 8) +
  1463. scsiDev.cdb[9];
  1464. doReadCD(lba, blocks, 0, 0x10, 0);
  1465. }
  1466. else if (command == 0x4E)
  1467. {
  1468. // STOP PLAY/SCAN
  1469. doStopAudio();
  1470. scsiDev.status = 0;
  1471. scsiDev.phase = STATUS;
  1472. }
  1473. else if (command == 0x01)
  1474. {
  1475. // REZERO UNIT
  1476. // AppleCD Audio Player uses this as a nonstandard
  1477. // "stop audio playback" command
  1478. doStopAudio();
  1479. scsiDev.status = 0;
  1480. scsiDev.phase = STATUS;
  1481. }
  1482. else if (command == 0x0B || command == 0x2B)
  1483. {
  1484. // SEEK
  1485. // implement Annex C termination requirement and pass to disk handler
  1486. doStopAudio();
  1487. // this may need more specific handling, the Win9x player appears to
  1488. // expect a pickup move to the given LBA
  1489. commandHandled = 0;
  1490. }
  1491. else
  1492. {
  1493. commandHandled = 0;
  1494. }
  1495. return commandHandled;
  1496. }